91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

鏈接器安排的【虛擬地址】是如何計算出來的?

li5236 ? 來源:道哥分享 ? 作者:道哥分享 ? 2022-03-29 14:36 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

問題描述

昨天下午,旁邊的同事在學習Linux系統(tǒng)中的虛擬地址映射(經(jīng)典書籍《程序員的自我修養(yǎng)-鏈接、裝載與庫》),在看到6.4章節(jié)的時候,對于一個可執(zhí)行的ELF文件中,虛擬地址的值百思不得其解!

例如下面這段C代碼:

poYBAGJCqPOABRdMAAG9iZYG0g4084.jpg

首先編譯出32位的可執(zhí)行程序(為了避開一些與主題無關(guān)的干擾因素,采用了靜態(tài)鏈接):

gcc -m32 -static test.c -o test

編譯得到ELF格式的可執(zhí)行文件:test。

pYYBAGJCqPOAYsyLAAIPXflPCH8725.jpg

這個時候,使用readelf工具來查看這個可執(zhí)行文件中的段信息(segment):

poYBAGJCqPSAcBW_AAWFTSSCtcA102.jpg

上圖中的紅色矩形框中,第二個段的地址為什么是 0x080e_9f5c?

這篇文章主要根據(jù)書中的解釋,來具體的分析這個值的來龍去脈。

ELF 文件格式

在Linux系統(tǒng)中,有4種類型的文件都是ELF格式,包括:目標文件,可執(zhí)行文件,動態(tài)鏈接庫文件、核心轉(zhuǎn)儲文件。

如果想系統(tǒng)掌握Linux系統(tǒng)中的底層知識,研究ELF的格式是避免不了的事情。

很久之前總結(jié)過這篇文章:《Linux系統(tǒng)中編譯、鏈接的基石-ELF文件:扒開它的層層外衣,從字節(jié)碼的粒度來探索》,里面詳細總結(jié)了ELF文件的內(nèi)部結(jié)構(gòu)。

這里就不再贅述了,只要記住2點:

1.從編譯器的角度看,ELF 文件是由很多的節(jié)(Section)組成的;

2.從程序加載器的角度看,ELF 文件是又很多的段(Segment)組成的;

其實它倆沒有本質(zhì)區(qū)別,只不過是鏈接器在鏈接階段,把不同目標文件中相同的section組織在一起,形成一個 segment。

對于剛才編譯出的test可執(zhí)行文件,其加載視圖如下:

pYYBAGJCqPSAanuEAAWm4WhSWq4394.jpg

可以看到該文件一共有5個段(segment),前2個需要LOAD到內(nèi)存的段,它們屬性分別是:讀、執(zhí)行(R E) 和 讀、寫(RW),它們分別是代碼段和數(shù)據(jù)段。

綠色的箭頭反映出:代碼段中包含了很多的 section;黃色的箭頭反映出數(shù)據(jù)段也包含了很多的 section。

地址轉(zhuǎn)換和內(nèi)存映射

從地址轉(zhuǎn)換的角度來看:

Linux 系統(tǒng)中CPU中使用的都是虛擬地址,該虛擬地址在尋址的時候,需要經(jīng)過MMU地址轉(zhuǎn)換,得到實際的物理地址,然后才能在物理內(nèi)存中讀取指令,或者讀取、寫入數(shù)據(jù)。

在現(xiàn)代操作系統(tǒng)中,MMU地址轉(zhuǎn)換單元基本上都是通過頁表來進行地址轉(zhuǎn)換的:

poYBAGJCqPWAUW85AAGOfont-XQ386.jpg

當然了,有些系統(tǒng)是兩級轉(zhuǎn)換(頁目錄、頁表),有些系統(tǒng)是三級或者四級頁表。

從內(nèi)存映射的角度來看:

操作系統(tǒng)在把一個可執(zhí)行程序加載到系統(tǒng)中時,把ELF文件中每個段的內(nèi)容讀取到物理內(nèi)存中,然后把這個物理內(nèi)存映射到該段對應(yīng)的虛擬地址上(VirtAddr)。

假設(shè)一個可執(zhí)行程序中的代碼段長度是1.2K字節(jié), 數(shù)據(jù)段長度是1.3K字節(jié)。

操作系統(tǒng)在把它倆讀取到內(nèi)存中時,需要 2 個物理內(nèi)存頁來分別存儲它們(每 1 個物理頁的長度是4K):

pYYBAGJCqPWADIVyAAFK6tP5sC0814.jpg

雖然每一個物理內(nèi)存頁的大小是 4K,但是代碼段和數(shù)據(jù)段實際上只使用了每個頁面剛開始的一段空間。

當CPU中需要讀取物理內(nèi)存上代碼段中的指令時,使用的虛擬地址是 0x0000_1000 ~ 0x0000_1000 + 1.2K這個區(qū)間的地址,MMU單元經(jīng)過頁表轉(zhuǎn)換之后,就會得到這個存放著代碼段的物理頁的物理地址。

數(shù)據(jù)段的尋址方式也是如此:當CPU中需要讀寫物理內(nèi)存上數(shù)據(jù)段中的數(shù)據(jù)時,使用的虛擬地址是 0x0000_2000 ~ 0x0000_2000 + 1.3K這個區(qū)間的地址。

MMU單元經(jīng)過頁表轉(zhuǎn)換之后,就會得到存放著數(shù)據(jù)段的物理頁的物理地址。

可以看出在這樣的安排下,每一個段的虛擬地址,都是按照4K(0x1000)對齊的。

如果操作系統(tǒng)都是這樣簡單映射的話,那么事情就簡單多了。

如果按照這樣的安排,來分析一下文章開頭的 test 可執(zhí)行程序中的虛擬地址安排:

1.代碼段安排的開始虛擬地址是 0x0804_8000,這是 4K 對齊的;

2.代碼段的結(jié)束虛擬地址就應(yīng)該是 0x0804_8000 + 0xa0725 = 0x080e_8725;

3.那么數(shù)據(jù)段的開始地址就可以安排在 0x080e_8725 之后的下一個 4K 對齊的邊界地址,即:0x080e_9000。

但是這樣的地址安排,嚴重浪費了物理內(nèi)存空間!

1.2K 字節(jié)的代碼段加上1.3K字節(jié)的數(shù)據(jù)段,本來只需要1個物理頁就夠了(4KB),但是這里卻消耗掉2個物理頁(8KB)。

為了減少物理內(nèi)存的浪費,Linux操作系統(tǒng)就采用了一些巧妙的辦法來減少物理內(nèi)存的浪費,那就是: 把文件中接壤部分的代碼段和數(shù)據(jù)段,讀取到同一個物理內(nèi)存頁中,然后在虛擬地址空間中映射兩次,詳述如下。

Linux 中的內(nèi)存重復映射

先來看一下test文件的結(jié)構(gòu):

poYBAGJCqPWAN0wmAAAsafA3Ujc854.jpg

代碼段在文件中的開始位置是:0x00000,長度是 0xa0725。

數(shù)據(jù)段的開始位置是:0xa0f5c,長度是0x1024。

可以看到它倆之間有一個空白區(qū)間,長度是: 0xa0f5c - 0xa0725 = 0x837(十進制:2103字節(jié))。

由于操作系統(tǒng)在把test文件讀取到物理內(nèi)存的時候,從文件開始代碼段的0x00000地址開始讀取,按照4KB為一個單位存放到一個物理頁中。

1.文件中代碼段的 0x00000 ~ 0x00FFF 讀取到一個物理頁中;

2.文件中代碼段的 0x01000 ~ 0x01FFF 讀取到物理頁中;

3.下面的內(nèi)容都是如此分割、復制;

也就是說:相當于把test文件從開始位置,按照4KB為一個單位進行"切割",然后復制到不同的物理內(nèi)存頁中,如下所示:

pYYBAGJCqPWASPGQAABtQ1QJflY467.jpg

注意:這些物理頁的地址很可能是不連續(xù)的。

這里有意思的是:代碼段與數(shù)據(jù)段接壤的這個4KB的空間,它的開始地址是0xA0000,結(jié)束地址是0xA0FFF,被復制到物理內(nèi)存中最上面的橙色物理頁中。

再來看一下代碼段的虛擬地址:在執(zhí)行g(shù)cc指令的的時候,鏈接器把代碼段的虛擬地址安排在0x0804_8000處:

poYBAGJCqPWAVw1yAAC1XFf5kFU636.jpg

也就是說:當CPU中(或者說程序代碼中),使用0x0804_8000 ~ 0x0804_7FFF 這個區(qū)間的地址時,經(jīng)過地址映射,就會找到物理內(nèi)存中淺綠色的物理頁,而這個物理頁也對應(yīng)著test可執(zhí)行文件開始的第一個4KB的空間。

而且,從虛擬地址的角度看,它的地址都是連續(xù)的,對應(yīng)著test文件中連續(xù)的內(nèi)容,這也是虛擬地址映射的本質(zhì)。

把代碼段的開始位置安排在 0x0804_8000 地址,這是 Linux 操作系統(tǒng)確定的。

那么考慮一下:代碼段的最后一部分指令相應(yīng)的4K頁面,其對應(yīng)的開始虛擬地址是多少呢?

上圖中已經(jīng)標記出來了,就是虛擬地址中橙色部分:0x080e_8000,計算如下:

通過代碼段的開始地址0x0804_8000,再加上代碼段在內(nèi)存中的長度0xa0725,結(jié)果就是 0x080e_8725。

按照4K (0x1000)對齊之后,最后一個虛擬頁就應(yīng)該是0x080e_8000。

也就是說:虛擬地址中0x080e_8000 ~ 0x080e_8724 這個區(qū)間就對應(yīng)著test文件中代碼段的最后一部分指令(0x725個字節(jié))。

此外,上圖中最右側(cè):test文件結(jié)構(gòu)中的2個紅色地址:0xA0000, 0xA1000,是如何計算得到的?

代碼段的長度是 0xA0725,按照4K為一個單位來進行分割,也就是把0xA0725對0x1000進行整除,就得到這個4KB的開始地址0xA0000。

同理,下一個4KB的開始地址就是0xA1000。

把文件中這部分4K的數(shù)據(jù)(包括:一部分代碼段內(nèi)容 + 0x837 字節(jié)空洞 + 一部分數(shù)據(jù)段內(nèi)容),復制到上圖中物理內(nèi)存中最上面的橙色物理頁中。

又因為虛擬地址空間中,0x080E_8000開始的這個4KB空間映射到這個物理頁中,所以:在這個虛擬地址空間中,也有一個0x837字節(jié)的空洞,如下所示:

pYYBAGJCqPaAUBlkAAB2jLkJ8go953.jpg

空洞的下方,是代碼段的指令;空洞的上方,是數(shù)據(jù)段的數(shù)據(jù)。

現(xiàn)在,這個物理頁中即存放了代碼,又存放了數(shù)據(jù)。

那么CPU中在查找部分的代碼和數(shù)據(jù)的時候,必須都能夠找得到才行!

對于代碼段比較好理解:從這個物理頁開始的前0x725個字節(jié)是有效的,從虛擬地址的角度看,就是從0x080e_8000開始的前0x725個字節(jié)是有效的。

因此,對于這部分代碼的尋址,使用的虛擬地址處于0x080e_8000 ~ 0x080e_8724這個區(qū)間中。

那么數(shù)據(jù)段呢?

重點來了:Linux系統(tǒng)把虛擬地址空間 0x080e_9000 ~ 0x080e_9FFF 也映射到圖中物理內(nèi)存中最上面的橙色物理頁上!

如下所示:

poYBAGJCqPaAVdcEAADEQ8znZnA069.jpg

因為物理頁中,是從0x837個字節(jié)空洞的上面開始,才是真正的數(shù)據(jù)段內(nèi)容,那么相應(yīng)的: 虛擬地址0x080e_9000 ~ 0x080e_9FFF空間中,0x837字節(jié)上面的內(nèi)容才是數(shù)據(jù)段內(nèi)容。

那么在虛擬地址空間中,這個數(shù)據(jù)段的開始地址應(yīng)該是多少呢?

只要計算出0x837字節(jié)空洞的上方,距離這個4K頁面開始地址的偏移量就可以了,然后再加上這個4K頁面的起始地址 0x080E_9000,就得到了數(shù)據(jù)段的開始地址(虛擬地址)。

因為虛擬地址、物理地址、test文件中,都是按照4K的單位進行劃分的,因此這個偏移量就等于:test文件中數(shù)據(jù)段的開始地址(0xA0F5C) 距離 這個頁面的開始地址(0xA0000) 的偏移量。

0xA0F5C - 0xA0000 = 0xF5C 。

pYYBAGJCqPaAFjcDAAA-1y0EUrk624.jpg

即:從這個4K頁面的開始地址,偏移量為0xF5C的地方,才是數(shù)據(jù)段內(nèi)容的開始。

因此對于虛擬地址來說,從0x080e_9000地址開始,偏移量為0xF5C之后的內(nèi)容才是數(shù)據(jù)段的內(nèi)容,這個地址值就是:0x080e_9000 + 0xF5C = 0x080e_9F5C,如下所示:

poYBAGJCqPaASJEZAACQYSwrSLA759.jpg

這個地址正是readelf工具讀所顯示的:數(shù)據(jù)段加載到虛擬地址空間中的開始地址,如下所示:

pYYBAGJCqPeAU7rvAAFV78G8sjU166.jpg

至此,就解釋了文章開頭提出的問題!

再來看一下整個數(shù)據(jù)段的內(nèi)容:在內(nèi)存中數(shù)據(jù)段占據(jù)的空間是 0x01e48(readelf 工具讀取到的 MemSiz),那么數(shù)據(jù)段的結(jié)束地址就是(虛擬地址):

0x080e_9F5C + 0x01e48 = 0x080e_bda4

如下所示:

poYBAGJCqPeASZohAACqNeG56yo245.jpg

小結(jié)

Linux系統(tǒng)中的這個操作:對屬于不同段的內(nèi)容進行重復映射,有點類似于共享內(nèi)存的味道了。

只不過這里重復映射之后,每個段的虛擬地址還是需要修正為該段的合法地址。

經(jīng)過這樣的操作之后,在虛擬地址中每一個段的界限是涇渭分明的,但是映射到的物理內(nèi)存頁,則有可能是同一個。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7336

    瀏覽量

    94812
  • Linux
    +關(guān)注

    關(guān)注

    88

    文章

    11767

    瀏覽量

    219103
  • 虛擬
    +關(guān)注

    關(guān)注

    0

    文章

    199

    瀏覽量

    24282
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    lab view NI6003搭建電路

    各位大佬,用labview程序仿真光敏電阻,得出電阻電壓電流各類數(shù)據(jù),光敏電阻的電壓可以用lab view程序測試出來,電阻用萬用表測試出來,電流用歐姆定律計算出來,但是需要搭建一個外置電路,請問這個電路怎么設(shè)計,是像下圖這樣的
    發(fā)表于 03-03 19:42

    ADC芯片溫度計算出來的溫度有時錯的離譜,為什么?

    以下是庫函數(shù)里關(guān)于溫度計算的要點,芯片手冊里也是這樣描述的: 讀取ADC參考電壓1.5V時的溫度傳感的測量溫度 void ADC_GetTsVref1V5(float *fAdcTsDegree
    發(fā)表于 01-22 06:39

    ESP32S3讀取NTC熱敏電阻阻值計算出當前環(huán)境溫度

    本文介紹了熱敏電阻模塊的工作原理及應(yīng)用,通過ESP32開發(fā)板ADC獲取熱敏電阻的電壓推算出熱敏電阻的阻值后進而推算出熱敏電阻周圍的溫度值。
    的頭像 發(fā)表于 01-05 17:16 ?657次閱讀
    ESP32S3讀取NTC熱敏電阻阻值<b class='flag-5'>計算出</b>當前環(huán)境溫度

    請問e203定義的地址空間是虛擬地址還是物理地址?

    蜂鳥e203實現(xiàn)的是物理地址,硬件端與軟件端的地址分配相同,從而確定軟件開發(fā)過程中能操作底層寄存。 硬件端:在總線分發(fā)模塊sirv_icb1to16_bus定義好各個端口寄存
    發(fā)表于 11-11 06:20

    關(guān)于系統(tǒng)鏈接腳本的介紹

    Flash里面,但上電后上載至ITCM中進行執(zhí)行(flash模式) 三、關(guān)于物理地址虛擬地址 物理地址是該程序要被存儲的存儲地址(調(diào)試
    發(fā)表于 10-30 08:26

    RVMCU課堂「19」: 手把手教你玩轉(zhuǎn)RVSTAR—CRC計算

    ,主要用來檢測或校驗數(shù)據(jù)傳輸或者保存后可能出現(xiàn)的錯誤。生成的數(shù)字在傳輸或者存儲之前計算出來并且附加到數(shù)據(jù)后面,然后接收方進行檢驗確定數(shù)據(jù)是否發(fā)生變化。一般來說,循環(huán)冗余校驗的值都是32位的整數(shù)。由于本
    發(fā)表于 10-30 07:49

    神經(jīng)網(wǎng)絡(luò)加速的雙線性插值上采樣

    雙線性插值法:目標象素值根據(jù)這個源圖中虛擬的點四周的四個真實的點來按照一定的規(guī)律計算出來。像最鄰近插值法那樣由目標圖的坐標反推得到的源圖的的坐標是一個浮點數(shù)的時候,采用了四舍五入的方法,直接采用
    發(fā)表于 10-29 06:36

    貼片電容的精度是怎么計算出來的?

    貼片電容的精度通過 實際電容值與標稱電容值的偏差范圍 計算得出,其核心計算邏輯和關(guān)鍵要點如下: 一、精度定義與計算公式 貼片電容的精度表示實際電容值與標稱值的允許偏差范圍,計算公式為:
    的頭像 發(fā)表于 10-11 15:01 ?1385次閱讀
    貼片電容的精度是怎么<b class='flag-5'>計算出來</b>的?

    MOS管的連續(xù)電流ID計算示例

    在電子電路的設(shè)計中,MOS管是一種極為重要的分立器件,它廣泛應(yīng)用于電源管理、電機驅(qū)動等眾多領(lǐng)域。而在MOS管的規(guī)格書中,連續(xù)電流ID這個參數(shù)備受關(guān)注。那么,MOS的規(guī)格書上的連續(xù)電流ID究竟是怎么計算出來的呢?今天我們就來解析其背后的計算邏輯。
    的頭像 發(fā)表于 09-22 11:04 ?1484次閱讀
    MOS管的連續(xù)電流ID<b class='flag-5'>計算</b>示例

    如何計算出管殼式換熱器和板式換熱器的長寬高,江蘇睿翌

    如何計算出管殼式換熱器和板式換熱器的長寬高,江蘇睿翌確定換熱器的長、寬、高(更準確地說,是確定其關(guān)鍵尺寸)是一個系統(tǒng)性的設(shè)計過程,而不是簡單的計算。它需要綜合考慮熱力學、流體力學、材料學和實際工況
    發(fā)表于 08-27 09:53

    請問STM32N6的攝像頭下采樣是怎么實現(xiàn)的?

    在N6的例程里面實現(xiàn)了把imx335的2592x1944下采樣到800x480,但是這里的參數(shù)配置是怎么計算出來的,還有如果我想要把這個配置為224*160的話,我得怎么修改? /[i
    發(fā)表于 07-11 07:29

    綠氫系統(tǒng) PEM 電解槽直流接入仿真驗證深度解析

    為 PEM 模塊實時計算出來的實際消耗電流。藍色部分為 AC/DC,綠色部分為 DC/DC。 AC/DC 通過整流將網(wǎng)側(cè)交流電壓轉(zhuǎn)換成直流電壓,接著通過 DC/DC 拓撲,將 PEM 等效負載的電壓
    發(fā)表于 07-03 18:25

    在matlab中如何計算含有第一類修正的貝塞爾函數(shù)的積分算不出的問題?

    ^3/3))*diff(y)/y,x,-inf,inf); 所定義的變量中,B、R、me、e、yipesiu0、c都取定值,E=30,xitap=0.19時,lamuda=0.5能計算出積分,但lamuda=0.9時卻計算出來
    發(fā)表于 05-19 16:53

    LT3482的OUT2引腳電壓與APD引腳電壓是什么關(guān)系?如何通過OUT2電壓 計算APD引腳電壓 ?

    你好, 如下圖,通過 OUT2電壓計算公式 OUT2電壓為 89.44V,而如下圖APD引腳 的 85V ,是怎么計算出來的。? LT3482 的 OUT2 引腳電壓計算公式:
    發(fā)表于 04-17 06:20

    AD2S1200解碼芯片的精度是11弧分,這個數(shù)值是怎么計算出來的?

    請問一下,AD2S1200解碼芯片的精度是11弧分,這個數(shù)值是怎么計算出來的?
    發(fā)表于 04-15 06:20