Linux從頭學(xué)02:x86中內(nèi)存【段尋址】方式的來(lái)龍去脈
-
什么是代碼段?
-
什么是數(shù)據(jù)段?
-
數(shù)據(jù)的類型和長(zhǎng)度
-
尋址范圍
-
棧
-
實(shí)模式和保護(hù)模式
-
Linux 中的分段策略
為了利用性能越來(lái)越強(qiáng)悍的計(jì)算機(jī),操作系統(tǒng)的也是在逐步變得膨脹和復(fù)雜。
什么是代碼段?
在上一篇文章:Linux 從頭學(xué) 01:CPU 是如何執(zhí)行一條指令的? 中,已經(jīng)提到過(guò),在處理器的內(nèi)部,執(zhí)行每一條指令碼時(shí),CPU是非常機(jī)械、非常單純地從 CS:IP 這2個(gè)寄存器計(jì)算得到轉(zhuǎn)換后的物理地址,從這個(gè)物理地址所指向的內(nèi)存地址處,讀取一定長(zhǎng)度的指令,然后交給邏輯運(yùn)算單元(Arithmetic Logic Unit, ALU)去執(zhí)行。
物理地址的計(jì)算方式是:CS * 16 IP。當(dāng)CPU讀取一條指令后,根據(jù)指令操作碼它能夠自動(dòng)知道這條指令一共需要讀取多少個(gè)字節(jié)。
mov bx, 3344H
當(dāng)執(zhí)行第一條指令時(shí),CS = 2000H,IP = 0000H,經(jīng)過(guò)地址轉(zhuǎn)換之后的物理地址是:2000H * 16 0000 = 20000H(乘以 16 也就表示十六進(jìn)制的數(shù)左移 1 位):
當(dāng)?shù)谝粭l指令碼B8 22 11這3個(gè)字節(jié)被讀取之后,IP 寄存器中的內(nèi)容自動(dòng)增加3`,從而指向下一條指令:
當(dāng)?shù)诙l指令碼BB 44 33這3個(gè)字節(jié)被讀取之后,IP寄存器中的內(nèi)容又增加3,變?yōu)?006H。
CS: 段寄存器,其中的值左移 1 位之后,得到的值就表示代碼段在內(nèi)存中的首地址,或者稱作基地址;IP: 指令指針寄存器,表示一條指令的地址,距離基地址的偏移量,也就是說(shuō),IP 寄存器是用來(lái)幫助 CPU 記?。耗男┲噶钜呀?jīng)被處理過(guò)了,下一個(gè)要被處理的指令是哪一個(gè);
什么是數(shù)據(jù)段?
作為一個(gè)有意義的程序,僅僅只有指令是不夠的,還必須操作數(shù)據(jù)。
CPU對(duì)內(nèi)存中數(shù)據(jù)段的訪問(wèn)方式,與訪問(wèn)代碼段是類似的,也是通過(guò)一個(gè)基地址,再加上一個(gè)偏移量來(lái)得到數(shù)據(jù)段中的某個(gè)物理地址。
數(shù)據(jù)的類型和長(zhǎng)度
但是,在操作數(shù)據(jù)段中每一個(gè)數(shù)據(jù),有一個(gè)比較重要的概念需要時(shí)刻銘記:數(shù)據(jù)的類型是什么,這個(gè)數(shù)據(jù)在內(nèi)存中占據(jù)的字節(jié)數(shù)是多少。
假設(shè)30000H是數(shù)據(jù)段的基地址(也就意味著DS寄存器中的內(nèi)容是3000H),那么30000H地址處的數(shù)據(jù)大小是多少:11H?2211H?還是44332211H?
那么可以說(shuō)ptr指針指向的數(shù)值是11H。
就可以說(shuō)ptr指針指向的數(shù)值就是44332211H(假設(shè)是小端格式)。
PS: 之前我曾說(shuō)過(guò),文章的主要目的是學(xué)習(xí) Linux 操作系統(tǒng),但是為了學(xué)習(xí)一些相對(duì)底層的內(nèi)容,在開始階段必須拋開操作系統(tǒng)的外衣,進(jìn)入到硬件最近的地方去看。在匯編語(yǔ)言中,CPU是通過(guò)指令碼中的相關(guān)寄存器來(lái)判斷操作數(shù)據(jù)的長(zhǎng)度。但是該怎么看呢?還是要借助一些原始的手段和工具,那么匯編代碼無(wú)疑就是最好的、也是唯一的手段;
不過(guò),涉及到的匯編代碼都是最簡(jiǎn)單的,僅僅是為了說(shuō)明原理;
其中的 [0] 表示內(nèi)存的數(shù)據(jù)段中偏移地址是0的位置。
因?yàn)橹噶畲a中的al寄存器是8位,因此CPU就只讀取30000H處的一個(gè)字節(jié)11,放到al寄存器中。(此時(shí)ax寄存器的高8位,也就是ah中的值保持不變)
尋址范圍
從以上內(nèi)容可以總結(jié)得出:
注意:這里的段寄存器左移1位,是指十六進(jìn)制的左移,相當(dāng)于是乘以 16,因此段的基地址都是16的倍數(shù)。
- 代碼段和數(shù)據(jù)段都是通過(guò) 【基地址 偏移地址】的方式進(jìn)行尋址;
- 基地址都放在各自的段寄存器中,CPU 會(huì)自動(dòng)把段寄存器的值,左移 1 位之后,作為段的基地址;
- 偏移地址決定了段中的每一個(gè)具體的地址,最大偏移地址是 16 個(gè) bit1,也即是 64KB 的空間;
我們來(lái)計(jì)算最后一個(gè)段的空間。
棧
棧也是數(shù)據(jù)空間的一種,只不過(guò)它的操作方式有些特殊而已。
push(入棧): 往??臻g中放入一個(gè)數(shù)據(jù);注意:這里的數(shù)據(jù)是固定 2 個(gè)字節(jié),也就是一個(gè)字。
pop(出棧): 從??臻g中彈出一個(gè)數(shù)據(jù);
??臻g的基地址是1000:0000,SS:SP執(zhí)行的地址空間是棧頂,此時(shí)棧頂中的元素是44。
push as
棧頂指針寄存器SP中的值首先減 2,變成000A:
然后,再把寄存器ax中的值1234H放入SS:SP指向的內(nèi)存單元處:
出棧的操作順序是相反的:
首先把SS:SP指向的內(nèi)存單元中的數(shù)據(jù)1234H放入寄存器bx中,然后把棧頂指針寄存器SP中的值加 2,變成000C:
以上描述的是 8086 處理器中對(duì)棧操作的執(zhí)行過(guò)程。
滿:是指棧頂指針指向的那個(gè)空間中,是一個(gè)有效的數(shù)據(jù)。當(dāng)一個(gè)新數(shù)據(jù)入棧時(shí),棧頂指針先指向下一個(gè)空的位置,然后 把數(shù)據(jù)放入這個(gè)位置;空:是指棧頂指針指向的那個(gè)空間中,是一個(gè)無(wú)效的數(shù)據(jù)。當(dāng)一個(gè)新數(shù)據(jù)入棧時(shí),先把數(shù)據(jù)放入這個(gè)位置,然后棧頂指針指向下一個(gè)空的位置;
遞增:是指在數(shù)據(jù)入棧時(shí),棧頂指針向高地址方向增長(zhǎng);
遞減:是指在數(shù)據(jù)入棧時(shí),棧頂指針向低地址方向遞減;
實(shí)模式和保護(hù)模式
從以上對(duì)內(nèi)存的尋址方式中可以看出:只要在可尋址的范圍內(nèi),我們寫的程序是可以對(duì)內(nèi)存中任意一個(gè)位置的數(shù)據(jù)進(jìn)行操作的。
有些書籍中會(huì)提到 IA-32A 這個(gè)概念,IA-32 是英特爾 Architecture 32-bit簡(jiǎn)稱,即英特爾32位體系架構(gòu),也是在386中首先采用。從386以后引入的保護(hù)模式下,地址線變成了32根,最大尋址空間可以達(dá)到4GB。雖然引進(jìn)了保護(hù)模式,但是也存在實(shí)模式,即向前兼容。電腦開機(jī)后處于實(shí)模式,BIOS 加載主引導(dǎo)記錄以及進(jìn)行一些寄存器的設(shè)置之后就進(jìn)入保護(hù)模式。
Linux 中的分段策略
上面描述的分段機(jī)制是 x86 處理器中所提供的一種內(nèi)存尋址機(jī)制,這僅僅是一種機(jī)制而已。
因此,如何利用x86提供的分段機(jī)制是操作系統(tǒng)需要操心的問(wèn)題。
這是Linux2.6版本中四個(gè)主要的段描述符,這里先不用管段描述符是什么,它們最終都是用來(lái)描述內(nèi)存中的一塊空間而已。
------ End ------





