通過do_execve源碼分析程式的執行(下)(基於linux0.11)
- 2019 年 10 月 4 日
- 筆記
上篇講了程式的載入。然後設置了eip,這一篇分析一下開始執行第一條指令的時候。會發生什麼。 我們先看一下這時候的記憶體布局。

在這裡插入圖片描述 當cpu通過cs:eip執行第一條指令的時候。首先通過cs的值,在ldt中找到程式碼段的基地址,然後加上eip,得到線性地址,然後通過線性地址到頁目錄和頁表項里找是否已經映射到物理地址了。這時候發現,還沒有對應的物理地址,然後就觸發缺頁中斷。 我們看一下缺頁中斷的處理。
// 缺頁處理,進程的內容還沒有載入到記憶體,訪問的時候導致缺頁異常 void do_no_page(unsigned long error_code,unsigned long address) { int nr[4]; unsigned long tmp; unsigned long page; int block,i; // 取得線性地址對應頁的頁首地址,與0xfffff000即減去頁偏移 address &= 0xfffff000; // 算出離程式碼段首地址的偏移 tmp = address - current->start_code; // tmp大於等於end_data說明是訪問堆或者棧的空間時發生的缺頁,直接申請一頁 if (!current->executable || tmp >= current->end_data) { get_empty_page(address); return; } // 是否有進程已經使用了 if (share_page(tmp)) return; // 獲取一頁,4kb if (!(page = get_free_page())) oom(); /* remember that 1 block is used for header */ /* 算出要讀的硬碟塊號,但是最多讀四塊。 tmp/BLOCK_SIZE算出線性地址對應頁的 頁首地址離程式碼塊距離了多少塊,然後讀取頁首 地址對應的塊號,所以需要加一。比如距離2塊的距離,則 需要讀取的塊是第三塊 */ block = 1 + tmp/BLOCK_SIZE; // 查找文件前4塊對應的硬碟號 for (i=0 ; i<4 ; block++,i++) // bmap算出邏輯塊號對應的物理塊號 nr[i] = bmap(current->executable,block); // 從硬碟讀四塊數據進來,並且複製到物理頁中 bread_page(page,current->executable->i_dev,nr); /* tmp是小於end_data的,因為從tmp開始載入了4kb的數據, 所以tmp+4kb(4096)後大於end_data,所以大於的部分需要清0, i即超出的位元組數 */ i = tmp + 4096 - current->end_data; // page是物理頁首地址,加上4kb,從後往前清0 tmp = page + 4096; while (i-- > 0) { tmp--; *(char *)tmp = 0; } // 建立線性地址和物理地址的映射 if (put_page(page,address)) return; // 失敗則是否剛才申請的物理頁 free_page(page); oom(); }
我們發現缺頁中斷有一句程式碼是
// 算出離程式碼段首地址的偏移 tmp = address - current->start_code
start_code的值就是cs對應的基地址。即進程號*64MB。address就是剛才我們通過cs:eip算出來的值。兩個相減得到線性地址離程式碼段基址的距離,即硬碟中,在程式碼section中的偏移。然後算出這個偏移在硬碟中相對程式碼section首地址塊偏移。因為程式碼塊不是可執行文件的第一塊。所以還需要做一個處理。
/* remember that 1 block is used for header */ /* 算出要讀的硬碟塊號,但是最多讀四塊。 tmp/BLOCK_SIZE算出線性地址對應頁的 頁首地址離程式碼塊距離了多少塊,然後讀取頁首 地址對應的塊號,因為邏輯塊號從0開始算,tmp/BLOCK_SIZE則是需要讀取的邏輯塊號, 因為執行文件頭還有一頁,所以加上 */ block = 1 + tmp/BLOCK_SIZE;
就是加上可執行文件頭佔據的那一塊,這樣就得出了cs:eip在硬碟中絕對偏移,最後把這塊數據讀進來。並且建立線性地址到物理地址的映射。然後重新執行cs:eip對應的指令,這時候就能找到對應的指令執行了。同理,當我們通過ds訪問數據段的時候也是這樣,在指令里,訪問數據的時候,用的地址是相對數據段的偏移,通過地址的轉換,會落到數據段對應的線性地址里。然後通過缺頁中斷載入進來。堆和棧缺頁中斷時候直接分配一頁物理記憶體就行。因為他的數據不在硬碟里。