Load process
要執行一隻程式須將它的 instruction 跟 data load 到 memory。現代 OS 結合 virtual memory 以及 memory management 的 paging 機制來 load process。
virtual memory 將 process 認知的 memory space 跟 physical memory 分離,藉此達到 process 不一定要在它認定的 physical memory address 上執行。如果實際有的記憶體比較少,virtual memory 也可以在記憶體不足的情況下讓 process 以為它有這麼多 memory 可用。
每個 process 有自己的 virtual memory space,virtual memory space 的大小由平台的定址 bit 數決定,例如 32 bit 的平台可定址的 address 就從 0 到 2^32 - 1,總大小為 4 GB。process 在 32 bit 系統中的 virtual memory space 大小是 4GB,但不是 4GB 都可以為 process 使用,process 只能用 OS 分配給它的空間,亂用的話會被強制結束。
paging 簡單來說是將 memory space 分成固定 size 的 page,現在 OS 大多使用 4K size 的 page。virtual memory space 跟 physical memory space 都以 page 切分,MMU(Memory Management Unit)負責 virtual page 與 physical page 的 mapping。virtual page 不存在 physical memory 而該 virtual page 又要被使用時會發生 page fault,此時 OS 負責將 virtual page 從 disk 中讀到 memory 並建立 virtual page 與 physical page 之間的 mapping。
結合 virtual memory 跟 paging 的 load process 步驟:
- 建立這個 process 的 virtual memory space,實際上是建立 virtual page 對應到 physical page 所需的 data structure。
- 由 executable file header 的資訊建立 virtual space 與 executable file 的關係,也就是 process 的 virtual memory space 哪一塊對應到 executable file 的哪一塊。
- 將 PC register 設到執行入口,process 開始執行。
- process 執行到的程式或需要的 data 沒有 load 進 physical memory,發生 page fault。OS 依照 virtual space 與 executable file 的對應關係知道要從 disk load 哪個 page 到 physical memory,load 進 physical memory 後建立 virtual apge 與該 physical page 的關係,這段即一般講 load 所指的動作。控制權還給 process 繼續跑。
其中 step 4 會不斷發生。至於 physical memory 被用到需要換掉裡面的 page 才能放新 page 又是另一個故事了……
View
對 load 來說,executabl file 裡 section 內容是什麼不重要,重要的是權限。ELF 檔以 load 的角度被分為多個 segment,而一個 segment 裡會有多個屬性類似的 section。以不同角度劃分 executable file 在 ELF 中稱為不同 view,以 section 角度劃分是 linking view,以 segment 角度劃分是 execution view。
之所以分了 section 又以 segment 再區分 ELF 檔,是因為以上述的 loading 方式,load 必須以 page 為單位。如果依據 section 區分 page 會使用太多 page 而且每個 page 又只用一點點,很浪費 memory。
Program header table
executable file 用 program header table 保存 segment 的資訊,可以用 readelf -l
查看。
program header table 中每個 segment 都有些欄位分別表示不同意義,其中 file size 跟 memory size 分別表示這個 segment 在 ELF file 中的 size 以及 load 到 memory 後佔用多少 virtual memory。正常來說 file size <= memory size,兩個相等沒什麼好說的,就是 file 內容 load 進 memory。出現 file size < memory size 其中一個可能是 bss section。
bss section 是放 C 語言裡沒有初始化或初始化為 0 的 global variable,在 ELF 檔中只會記錄「有這個 variable」以及它的 size,不會給予該 variable 所需要的空間,因而能縮小 ELF 檔。到執行時當然要給這些 variable 其所需要的空間好讓它存 value,所以以 segment 角度來看包含 bss section 的 segment 會看到 memory size > file size。另外,C 語言規定 bss section 的初始內容都是 0,不同系統會以不同方式實作。
Example
用 Static link 裡的例子看 segment。
1 | $ readelf -l foo |
type 是 LOAD 才是需要 load 的 segment(這句話怎麼聽起來像廢話)。有兩個 segment,分別是 .text
section 所在的可讀可執行 segment 以及 .data
section 所在的可讀可寫 segment,他們分別會 load 到 0x400000
跟 0x600180
,執行後可以用 cat /proc/<pid>/maps
驗證。這個例子的 file size 跟 memory size 相等,因為裡面沒有 .bss
section,加個未 initialize 的 global 變數就可以看到不一樣啦~
Ref
- Von Neumann architecture wiki
- 《程式設計師的自我修養》ch 6
- 《一步步寫嵌入式操作系統》ch 3