轉載自:https://www.jianshu.com/p/84d96a6385b0
我們的源代碼通過預處理,編譯,匯編,鏈接后形成可執行文件,)那么當我們在cmd窗口敲出指令$test argv1 argv2\n 后,操作系統是怎么將我們的可執行文件加載並運行的呢?
首先知道,計算機的操作系統的啟動程序是寫死在硬件上的,每次計算機上電時,都將自動加載啟動程序,之后的每一個程序,每一個應用,都是不斷的 fork 出來的新進程。那么我們的可執行文件,以linux 系統為例,也是由shell 進程 fork 出一個新進程,在新進程中調用exec函數裝載我們的可執行文件並執行。
1. execve()
當shell中敲入執行程序的指令之后,shell進程獲取到敲入的指令,並執行execve()函數,該函數的參數是敲入的可執行文件名和形參,還有就是環境變量信息。execve()函數對進程棧進行初始化,即壓棧環境變量值,並壓棧傳入的參數值,最后壓棧可執行文件名。初始化完成后調用 sys_execve()
2. sys_execve()
該函數進行一些參數的檢查與復制,而后調用 do_execve()
3. do_execve()
該函數在當前路徑與環境變量的路徑中尋找給定的可執行文件名,找到文件后讀取該文件的前128字節。讀取這128個字節的目的是為了判斷文件的格式,每個文件的開頭幾個字節都是魔數,可以用來判斷文件類型。讀取了前128字節的文件頭部后,將調用 search_binary_handle()
4. search_binary_handle()
該函數將去搜索和匹配合適的可執行文件裝載處理程序。Linux 中所有被支持的可執行文件格式都有相應的裝在處理程序。以Linux 中的ELF 文件為例,接下來將會調用elf 文件的處理程序:load_elf_binary()
5. load_elf_binary()
該函數執行以下三個步驟:
a)創建虛擬地址空間:實際上指的是建立從虛擬地址空間到物理內存的映射函數所需要的相應的數據結構。(即創建一個空的頁表)
b)讀取可執行文件的文件頭,建立可執行文件到虛擬地址空間之間的映射關系
c)將CPU指令寄存器設置為可執行文件入口(虛擬空間中的一個地址)
load_elf_binary()函數執行完畢,事實上裝載函數執行完畢后,可執行文件真正的指令和數據都沒有被裝入內存中,只是建立了可執行文件與虛擬內存之間的映射關系,以及分配了一個空的頁表,用來存儲虛擬內存與物理內存之間的映射關系。
6. 程序返回到execve()中
此時從內核態返回到用戶態,且寄存器的地址被設置為了ELF 的入口地址,於是新的程序開始啟動,發現程序入口對應的頁面並沒有加載(因為初始時是空頁面),則此時引發一個缺頁錯誤,操作系統根據可執行文件和虛擬內存之間的映射關系,在磁盤上找到缺的頁,並申請物理內存,將其加載到物理內存中,並在頁表中填入該虛擬內存頁與物理內存頁之間的映射關系。之后程序正常運行,直至結束后回到shell 父進程中,結束回到 shell。
