24.3. 這個 Linux 執行模式是如何運作的 ?

這個部份的說明大部分都是依據由 Terry Lambert 寫到 通信論壇的信件內容 (Message ID: <199906020108.SAA07001@usr09.primenet.com>)。

FreeBSD 中有一個概念叫做 ``程式執行類別載入器''(execution class loader)。這個概念在 execve(2) 系統呼叫中被實作。

也就是說,在 FreeBSD 中有許多的程式載入器, 而非單一個載入器再加上處理 #! 這一類可執行的 shell scripts 的程式的載入器。

從歷史上來說,UNIX 平台上唯一的程式載入器所需要處理是檢查程式檔的魔術數字 (magic number, 通常是檔案最前面的 4 或 8 個 byte 的資料) 來決定該檔案是否屬於該作業系統;如果是的話,便執行程式執行載入器。

如果該程式檔並非屬於該作業系統,execve(2)則會傳回表示失敗傳回值, 然後 shell 程式會試著將它當作一個 shell 命令來執行。

這個假設並不去理會到底 ``現在正在執行的 shell 是那一個''。

稍後,有人對於 sh(1) 做了些修改來檢察檔案的最前面兩個字元, 如果那兩個字元是 :\n的話,那麼就將該檔案視為 csh(1) 的程式 (我們相信 SCO 應該是最早作這個修改的) 。

而現在 FreeBSD 的處理方式是檢查一連串的程式載入器, 包括一個認得 #! 可以正確地找到該行敘述中其餘字元指定的直譯器來執行該 script 的程式載入器, 若未指定該 script 所使用的直譯器則使用 /bin/sh 作為其直譯器。

對於 Linux ABI 而言,FreeBSD 從魔術數字中判斷該檔案是一個 ELF 格式的可執行檔 (FreeBSD, Solaris, Linux 或是任何其他的作業系統在目前都有 ELF 格式的可執行檔)。

接下來 ELF 程式載入器便檢視在該 ELF 格式檔案中的一個叫做 brand 的註解部分的資料 (不過在 SVR4 或是 Solaris 的 ELF 可執行檔中並沒有該部份資料)。

如果要讓 Linux 可執行檔正常的工作,就必須將該以指令 brandelf(1) 將該檔案 branded 成為 Linux 格式:

    # brandelf -t Linux file

當你這樣作以後,ELF 程式載入器就可以讀到該檔案中 指出該檔案是 ELF Linux 格式的 brand 。

當 ELF 程式載入器讀到 Linux brand 後, 程式載入器就會將 proc 資料結構中的一些指標參數修改。 所有系統呼叫都是透過這個指標參數來呼叫使用的 (對於一個傳統的 UNIX 系統, 這個參數通常名為 sysent[] 結構陣列,包含了系統呼叫)。 進一步來說,該程式就被標記成以特別的方式處理系統中斷, 以及其他若干由 Linux 核心模組處理的 (次要的) 修正。

Linux 系統呼叫參數中的若干參數中包含有一個 sysent[] 結構指向核心模組的進入點。

當一個 Linux 程式呼叫一個系統呼叫,系統處理程式便由 proc 參數查到 Linux 的 (而非 FreeBSD 的) 系統呼叫進入點。

進一步來說, Linux 模式也動態地處理 根目錄重置 (reroot) 查詢; 就好像在檔案系統 mount 時一個對應的 union 參數 ( 並非 unionfs!) 所處理的。首先會先嘗試的在所指定的目錄前加上 /compat/linux/,也就是變成 /compat/linux/original-path只有 在該處讀取失敗時才會到 /original-path 目錄去找尋對應的檔案。 這使得需要其他的可執行檔的程式得以成功的執行 (例如一個會用到其他 Linux 可執行檔的 Linux 程式)。 這也表示 Linux 程式也可以呼叫執行 FreeBSD 的程式, 如果沒有對應的 Linux 可執行檔存在的話, 所以說你可以放一個 uname(1) 命令在 /compat/linux 目錄下來讓 Linux 程式分辨不出到底該程式是不是在 Linux 下執行。

這樣的處理方式,就好像在 FreeBSD 核心程式中有一個 Linux 核心程式, 不同的底層函數處理所有核心提供的服務對於 FreeBSD 以及 Linux 的系統呼叫進入點是相同的, 如:檔案系統,虛擬記憶體,signal 發送,System V 程式間通訊 (IPC), 等等的操作;唯一不同的是 FreeBSD 程式取得 FreeBSD 專用 (gule) 函式,而 Linux 程式取得 Linux 專用 函式 (許多舊的作業系統之包含他自己的 專用 函式:這些函式是存放在系統核心的 靜態全域性的 sysent[] 資料陣列中, 而非在程式執行系統呼叫時,才動態的從 proc 的參數中找到對應函式的位址)。

那一個是原生的 FreeBSD ABI ? 這並不重要。 基本上來說唯一的差異處在於 (就目前來說;當然未來的版本可能會改變) FreeBSD 專用 函式在核心程式中視是靜態的 連結,而 Linux 專用函式則可以靜態的連結或是經由 FreeBSD 核心模組的方式來取得。

是的,不過這就是真正的模擬嗎 ? 不,這是一個 ABI 的實作,而非模擬。事實上並沒有任何的模擬器在其中。

那為甚麼有時把它稱之為 ``Linux 模擬器呢''? 要使得販售 FreeBSD 更為困難嗎 ! 8-)。說真的, 這是因為這是因為在當初使用這個方式實作時, 還沒有更好的名稱來指稱這個過程;說 FreeBSD 執行 Linux 程式並非完全正確的說法,因為你必須編譯相對應得程式碼或是載入 FreeBSD 核心模組才行,所以必須有一個詞來敘述到底載入了甚麼-- 所以便稱之為 ``Linux 模擬器''。