www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > IOT物聯(lián)網(wǎng)小鎮(zhèn)
[導(dǎo)讀]什么是代碼段?什么是數(shù)據(jù)段?數(shù)據(jù)的類型和長度尋址范圍棧實模式和保護模式Linux中的分段策略飯是一口一口的吃,計算機也是一步一步的發(fā)展,例如下面這張英特爾公司的CPU型號歷史:為了利用性能越來越強悍的計算機,操作系統(tǒng)的也是在逐步變得膨脹和復(fù)雜。為了從最底層來學(xué)習(xí)操作系統(tǒng)的一些基本...


  • 什么是代碼段?


  • 什么是數(shù)據(jù)段?


  • 數(shù)據(jù)的類型和長度


  • 尋址范圍



  • 實模式和保護模式


  • Linux 中的分段策略



飯是一口一口的吃,計算機也是一步一步的發(fā)展,例如下面這張英特爾公司的CPU型號歷史:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈為了利用性能越來越強悍的計算機,操作系統(tǒng)的也是在逐步變得膨脹和復(fù)雜。


為了從最底層來學(xué)習(xí)操作系統(tǒng)的一些基本原理,我們只有拋開操作系統(tǒng)的外衣,從最原始的硬件和編程方式來入手,才能了解到一些根本的知識。


這篇文章我們就來繼續(xù)挖掘一下,8086 這個開天辟地的處理器中,是如何利用段機制來對內(nèi)存進行尋址的。


什么是代碼段?

在上一篇文章:Linux 從頭學(xué) 01:CPU 是如何執(zhí)行一條指令的? 中,已經(jīng)提到過,在處理器的內(nèi)部,執(zhí)行每一條指令碼時,CPU是非常機械、非常單純地從 CS:IP 這2個寄存器計算得到轉(zhuǎn)換后的物理地址,從這個物理地址所指向的內(nèi)存地址處,讀取一定長度的指令,然后交給邏輯運算單元(Arithmetic Logic Unit, ALU)去執(zhí)行。


物理地址的計算方式是:CS * 16 IP。


當(dāng)CPU讀取一條指令后,根據(jù)指令操作碼它能夠自動知道這條指令一共需要讀取多少個字節(jié)。


指令被讀取之后,IP寄存器中的內(nèi)容就會自增,指向內(nèi)存中下一條指令的地址。


例如,在內(nèi)存20000H開始的地方,存在2條指令:


mov ax, 1122H
mov bx, 3344H
當(dāng)執(zhí)行第一條指令時,CS = 2000H,IP = 0000H,經(jīng)過地址轉(zhuǎn)換之后的物理地址是:2000H * 16 0000 = 20000H(乘以 16 也就表示十六進制的數(shù)左移 1 位):


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈當(dāng)?shù)谝粭l指令碼B8 22 11這3個字節(jié)被讀取之后,IP 寄存器中的內(nèi)容自動增加3`,從而指向下一條指令:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈當(dāng)?shù)诙l指令碼BB 44 33這3個字節(jié)被讀取之后,IP寄存器中的內(nèi)容又增加3,變?yōu)?006H。


正如上篇文章所寫,CPU只是反復(fù)的從CS:IP指向的內(nèi)存地址中讀取指令碼、執(zhí)行指令,再讀取指令碼、再執(zhí)行指令。


可以看出,要完成一個有意義的工作,所有的指令碼必須集中在一起,統(tǒng)一放在內(nèi)存中某個確定的地址空間中,才能被CPU依次的讀取、執(zhí)行。


內(nèi)存中的這塊地址空間就叫做一個,又因為這個段中存儲的是代碼編譯得到的指令,因此又稱作代碼段。


因此,用來對代碼段進行尋址的這兩個寄存器 CS 和 IP,它們的含義就非常清楚了:


CS: 段寄存器,其中的值左移 1 位之后,得到的值就表示代碼段在內(nèi)存中的首地址,或者稱作基地址;


IP: 指令指針寄存器,表示一條指令的地址,距離基地址的偏移量,也就是說,IP 寄存器是用來幫助 CPU 記?。耗男┲噶钜呀?jīng)被處理過了,下一個要被處理的指令是哪一個;


什么是數(shù)據(jù)段?

作為一個有意義的程序,僅僅只有指令是不夠的,還必須操作數(shù)據(jù)。


這些數(shù)據(jù)也應(yīng)該集中放在一起,位于內(nèi)存中的某個地址空間中,這塊地址空間,也是一個段,稱作數(shù)據(jù)段


也就是說:代碼段和數(shù)據(jù)段,就是內(nèi)存中的兩個地址空間,其中分別存儲了指令和數(shù)據(jù)


可以想象一下:假如指令和數(shù)據(jù)不是分開存放的,而是夾雜放在一起,那么CPU在讀取一條指令時,肯定就會把數(shù)據(jù)當(dāng)做指令來讀取、執(zhí)行,就像下面這樣,不發(fā)生錯誤才怪呢!


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈CPU對內(nèi)存中數(shù)據(jù)段的訪問方式,與訪問代碼段是類似的,也是通過一個基地址,再加上一個偏移量來得到數(shù)據(jù)段中的某個物理地址。


在8086處理其中,數(shù)據(jù)段的段寄存器是DS,也就是說,當(dāng)CPU執(zhí)行一條指令,這條指令需要訪問數(shù)據(jù)段時,就會把DS這個數(shù)據(jù)段寄存器中的值左移 1 位之后得到的地址,當(dāng)做數(shù)據(jù)段的基地址。


遺憾的是,CPU中并沒有提供一個類似IP寄存器的其他寄存器,來表示數(shù)據(jù)段的偏移地址寄存器。


這其實并不是壞事,因為一個程序在處理數(shù)據(jù)時,需要對數(shù)據(jù)進行什么樣操作,程序的開發(fā)者是最清楚的,因此我們就可以用更靈活的方式來告訴CPU應(yīng)該如何計算數(shù)據(jù)的偏移地址。


就像猴子掰苞米一樣,不需要按照順序來掰,想掰哪個就掰哪個。同樣的,程序在操作數(shù)據(jù)時,無論操作哪一個數(shù)據(jù),直接給出該數(shù)據(jù)的偏移地址的值就可以了。


數(shù)據(jù)的類型和長度

但是,在操作數(shù)據(jù)段中每一個數(shù)據(jù),有一個比較重要的概念需要時刻銘記:數(shù)據(jù)的類型是什么,這個數(shù)據(jù)在內(nèi)存中占據(jù)的字節(jié)數(shù)是多少。


我們在高級語言編程中(eg:C語言),在定義一個變量的時候,必須明確這個變量的類型是什么。一旦類型確定了,那么它在被加載到內(nèi)存中之后,所占據(jù)的空間大小也就確定了。


比如下面這張圖:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈假設(shè)30000H是數(shù)據(jù)段的基地址(也就意味著DS寄存器中的內(nèi)容是3000H),那么30000H地址處的數(shù)據(jù)大小是多少:11H?2211H?還是44332211H?


這幾個都有可能,因為沒有確定數(shù)據(jù)的類型


我們知道,在C語言中,假如有一個指針ptr最終指向了這里的30000H物理地址處(C代碼中的ptr是虛擬地址,經(jīng)過地址轉(zhuǎn)換之后執(zhí)行這里的30000H物理地址)。


如果ptr定義成:


char *ptr;
那么可以說ptr指針指向的數(shù)值是11H。


如果ptr定義成:


int *ptrt;
就可以說ptr指針指向的數(shù)值就是44332211H(假設(shè)是小端格式)。


也就是說,指針ptr指向的數(shù)據(jù),取決于定義指針變量時的類型。


這是高級語言中的情況,那么在匯編語言中呢?


PS: 之前我曾說過,文章的主要目的是學(xué)習(xí) Linux 操作系統(tǒng),但是為了學(xué)習(xí)一些相對底層的內(nèi)容,在開始階段必須拋開操作系統(tǒng)的外衣,進入到硬件最近的地方去看。


但是該怎么看呢?還是要借助一些原始的手段和工具,那么匯編代碼無疑就是最好的、也是唯一的手段;


不過,涉及到的匯編代碼都是最簡單的,僅僅是為了說明原理;


匯編語言中,CPU是通過指令碼中的相關(guān)寄存器來判斷操作數(shù)據(jù)的長度。


在上一篇文章中說過,相對于寄存器來說,CPU操作內(nèi)存的速度是很慢的。


因此,CPU在對數(shù)據(jù)段中的數(shù)據(jù)進行處理的時候,一般都是先把原始數(shù)據(jù)讀取到通用寄存器中(比如:ax, bx, cx dx),然后進行計算。


得到計算結(jié)果之后,再把結(jié)果寫回到內(nèi)存的數(shù)據(jù)段中(如果需要的話)。


那么CPU在讀寫數(shù)據(jù)時,就根據(jù)指令碼中使用的寄存器,來決定讀寫數(shù)據(jù)的長度。例如:


mov ax, [0]
其中的 [0] 表示內(nèi)存的數(shù)據(jù)段中偏移地址是0的位置。


CPU在執(zhí)行這條指令的時候,就會到30000H(假設(shè)此時數(shù)據(jù)段寄存器DS的值為3000H) 這個物理地址處,取出2個字節(jié)的數(shù)據(jù),放到通用寄存器ax中,此時ax寄存器中的值就是2211H。


為什么取出2個字節(jié)?因為ax寄存器的長度是16位,就是2個字節(jié)。


那如果只想取1個字節(jié),該怎么辦?


16位的通用寄存器ax可以拆成2個8位的寄存器里使用:ah和al。


mov al, [0]
因為指令碼中的al寄存器是8位,因此CPU就只讀取30000H處的一個字節(jié)11,放到al寄存器中。(此時ax寄存器的高8位,也就是ah中的值保持不變)


那如果想取3個字節(jié)或4個字節(jié)怎么辦?


作為相當(dāng)古老的處理器,8086CPU 中是16位的,只能對8位或16位的數(shù)據(jù)進行操作。


尋址范圍

從以上內(nèi)容可以總結(jié)得出:


  1. 代碼段和數(shù)據(jù)段都是通過 【基地址 偏移地址】的方式進行尋址;


  2. 基地址都放在各自的段寄存器中,CPU 會自動把段寄存器的值,左移 1 位之后,作為段的基地址;


  3. 偏移地址決定了段中的每一個具體的地址,最大偏移地址是 16 個 bit1,也即是 64KB 的空間;


注意:這里的段寄存器左移1位,是指十六進制的左移,相當(dāng)于是乘以 16,因此段的基地址都是16的倍數(shù)。


我們再來看一下這里的64 KB空間,與20根地址線有什么瓜葛。


上篇文章說到:8086處理器有20根地址線,一共可以表示1MB的內(nèi)存空間,即使給它更大的空間,它也沒有福氣去享受,因為尋址不到大于1 MB的地址空間?。?


這1MB的內(nèi)存空間,就可以分割為很多個段。


例如:第1個段的地址范圍是:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈我們來計算最后一個段的空間。


段寄存器和偏移地址都取最大值,就是 FFFF:FFFF,先偏移再相加:FFFF0 FFFF = 10FFEF =1M 64K - 16Bytes。


超過了1 MB的空間大小,但是畢竟只有20根地址線,肯定是無法尋址超過1 MB地址空間的,因此系統(tǒng)會采取回繞的方式來定位到一個地址空間,類似與數(shù)學(xué)中的取模操作。


此外還有一點,在表示一個內(nèi)存地址的時候,一般不會直接給出物理地址的值(比如:3000A),而是使用 段地址:偏移地址 這樣的形式來表示(比如:3000:000A)。


棧也是數(shù)據(jù)空間的一種,只不過它的操作方式有些特殊而已。


棧的操作方式就是4個字:后進先出。


在上面介紹數(shù)據(jù)段的時候,我們都是在指令碼中手動對數(shù)據(jù)的偏移地址進行設(shè)置,指哪打哪,因為這些數(shù)據(jù)放在什么位置、表示什么意思、怎么來使用,開發(fā)者自己心里最門清


但是有些不一樣,雖然它的功能也是用來存儲數(shù)據(jù)的,但是操作棧的方式,是由處理器提供的一些專門的指令來操作的:push和pop。


push(入棧): 往??臻g中放入一個數(shù)據(jù);
pop(出棧): 從??臻g中彈出一個數(shù)據(jù);


注意:這里的數(shù)據(jù)是固定 2 個字節(jié),也就是一個。


寫過C/C程序的小伙伴都知道:在函數(shù)調(diào)用的時候,存在入棧操作;在函數(shù)返回的時候,存在出棧操作。


既然棧也是指一塊內(nèi)存空間,那么也就是表現(xiàn)為內(nèi)存中的一個段。


既然是一個段,那肯定就存在一個段寄存器,用來代表它的基地址,這個棧的段寄存器就是SS。


此外,由于棧在入棧和出棧的時候,是按照連續(xù)的地址順序操作的,因此處理器為棧也提供了一個偏移地址寄存器:SP(稱作:棧頂指針),指向??臻g中最頂上的那個元素的位置


例如下面這張圖:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈??臻g的基地址是1000:0000,SS:SP執(zhí)行的地址空間是棧頂,此時棧頂中的元素是44。


當(dāng)執(zhí)行下面這2條指令時:


mov ax, 1234H
push as
棧頂指針寄存器SP中的值首先減 2,變成000A:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈然后,再把寄存器ax中的值1234H放入SS:SP指向的內(nèi)存單元處:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈出棧的操作順序是相反的:


pop bx
首先把SS:SP指向的內(nèi)存單元中的數(shù)據(jù)1234H放入寄存器bx中,然后把棧頂指針寄存器SP中的值加 2,變成000C:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈以上描述的是 8086 處理器中對操作的執(zhí)行過程。


如果你看過其他一些棧相關(guān)的描述書籍,可以看出這里使用的是 “滿遞減” 的棧操作方式,另外還還有:滿遞增,空遞減,空遞增 這幾種操作方式。


滿:是指棧頂指針指向的那個空間中,是一個有效的數(shù)據(jù)。當(dāng)一個新數(shù)據(jù)入棧時,棧頂指針先指向下一個空的位置,然后 把數(shù)據(jù)放入這個位置;


空:是指棧頂指針指向的那個空間中,是一個無效的數(shù)據(jù)。當(dāng)一個新數(shù)據(jù)入棧時,先把數(shù)據(jù)放入這個位置,然后棧頂指針指向下一個空的位置;


遞增:是指在數(shù)據(jù)入棧時,棧頂指針向高地址方向增長;


遞減:是指在數(shù)據(jù)入棧時,棧頂指針向低地址方向遞減;


實模式和保護模式

從以上對內(nèi)存的尋址方式中可以看出:只要在可尋址的范圍內(nèi),我們寫的程序是可以對內(nèi)存中任意一個位置的數(shù)據(jù)進行操作的。


這樣的尋址方式,稱之為實模式。實,就是實在、實際的意思,簡潔、直接,沒有什么彎彎繞。


既然編寫代碼的是人,就一定會犯一些低級的小錯誤。或者一些惡意的家伙,故意去操作那些不應(yīng)該、不可以被操作的內(nèi)存空間中的代碼或數(shù)據(jù)。


為了對內(nèi)存進行有效的保護,從80386開始,引入了 保護模式 來對內(nèi)存進行尋址。


有些書籍中會提到 IA-32A 這個概念,IA-32 是英特爾 Architecture 32-bit簡稱,即英特爾32位體系架構(gòu),也是在386中首先采用。


雖然引進了保護模式,但是也存在實模式,即向前兼容。電腦開機后處于實模式,BIOS 加載主引導(dǎo)記錄以及進行一些寄存器的設(shè)置之后就進入保護模式。


從386以后引入的保護模式下,地址線變成了32根,最大尋址空間可以達到4GB。


當(dāng)然,處理器中的寄存器也變成了32位。


我們還是用 段基址 偏移量 的方式來計算一個物理地址,假設(shè)段寄存器中內(nèi)容為0,偏移地址最大長度也是32位,那么一個段能表示的最大空間也就是4GB。


這也是為什么如今現(xiàn)代處理器中,每個進程的最大可尋址空間是4GB(一般指的是虛擬地址)。


一句話總結(jié):實模式和保護模式最根本的區(qū)別就是 內(nèi)存是否收到保護。


Linux 中的分段策略

上面描述的分段機制是 x86 處理器中所提供的一種內(nèi)存尋址機制,這僅僅是一種機制而已。


在x86處理器之上,運行著Windows、Linux獲取其它操作系統(tǒng)。


我們開發(fā)者是面對操作系統(tǒng)來編程的,寫出來的程序是被操作系統(tǒng)接管,并不是直接被x86處理器來接管。


相當(dāng)于操作系統(tǒng)把應(yīng)用程序和x86處理器之間進行了一層隔離:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈因此,如何利用x86提供的分段機制是操作系統(tǒng)需要操心的問題。


而操作系統(tǒng)提供什么樣的策略應(yīng)用程序來使用,這就是另外一個問題了。


那么,Linux操作系統(tǒng)是如何來包裝、使用x86提供的段尋址方式的呢?


是否還記得上一篇文章中的這張圖:


Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來龍去脈這是Linux2.6版本中四個主要的段描述符,這里先不用管段描述符是什么,它們最終都是用來描述內(nèi)存中的一塊空間而已。


在現(xiàn)代操作系統(tǒng)中,分段和分頁都是對內(nèi)存的劃分和管理方式,在功能上是有點重復(fù)的。


Linux以非常有限的方式使用分段,更喜歡使用分頁方式


上面的這張圖,一共定義了4個段,每一個段的基地址都是0x00000000,每一個段的Limit都是0xFFFFF。


從Limit的值可以得到:最大值是 2 的 20 次方,只有1 MB的空間。


但是其中的G字段表示了段的粒度,1表示粒度是4 K,因此 1 MB * 4K = 4 GB ,也就是說,段的最大空間是4 GB。


這4個段的基地址尋址范圍都是一樣的!主要的區(qū)別就是Type和DPL字段不同。


DPL表示優(yōu)先級,2個用戶段(代碼段和數(shù)據(jù)段) 的優(yōu)先級值是3,優(yōu)先級最低(值越大,優(yōu)先級越低);2個內(nèi)核段(代碼段和數(shù)據(jù)段)的優(yōu)先級值是0,優(yōu)先級最高


因此,可以得出Linux系統(tǒng)中的一個重要結(jié)論:邏輯地址與線性地址,在數(shù)值上是相等的,因為基地址是 0x00000000。


關(guān)于Linux中的內(nèi)存分段和分頁尋址方式更詳細的內(nèi)容,我們以后再慢慢聊。



------ End ------
本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉