1。我個人認為作者MAX對Linux的瞭解不像他對Solaris那樣深入,我不知道也沒法知道他的下列關於Linux的內容來自自己的代碼閱讀分析還是只是來自第三方的文檔資料而未經自己實地驗證;
2。我已經盡量符合原意地翻譯了,當然中間實在忍不住的地方也插兩句自己的話;
3。無論是只閱讀這一篇文章,還是看其他東西,我都覺得,保持自己頭腦清醒很重要;
4。謝謝
Max Bruning 是一名教師/資訊專家,他的教授內容包括Solaris內部組織,裝置驅動,核心和應用的crash分析及調試,網路組織和其他一些特定科目(他的 blog在blogspot,不費點勁可能訪問不了,所以也可以看看www.bruningsystems.com)。
在解釋這些子系統在Solaris中是如何?的時候,他的學生們總會問“Linux裡它是怎麼工作的?”或者“FreeBSD裡是這樣, Solaris裡呢?”這種經曆最終讓Max在OpenSolaris網站寫了這篇A Comparison of Solaris, Linux, and FreeBSD Kernels。
文章裡討論了調度,記憶體管理和檔案系統架構--這3個子系統在任何作業系統中都有普遍應用,而且他們是最well-understood 的組件。
目前很多分析或對比文章所引用的材料及代碼都比較老,與現實脫節,Max推薦如下幾個多少比較up to date的網站:
Solaris Vs. Linux
Comparing MySQL Performance
Fast Track to Solaris 10 Adoption
Solaris 10 Heads for Linux Territory
其實拋開3個系統之間的差別,他們也有很多相似之處。除了那些不同的命名習慣,這些OS在實現不同概念的時候採用了非常相似的方法。他們都支援線程的分時調度,支援最近未使用頁面替換演算法實現請求調頁,支援虛擬檔案系統層允許不同檔案系統架構。這個系統裡的一個好概念在另一個系統裡也會採用。比如Linux也接受並實現了 Solaris slab 記憶體配置演算法的概念。FreeBSD 代碼裡的很多術語在Solaris裡也出現了(快去看看代碼。。。)。考慮到這3個系統的原始碼都能得到了, fxr.watson.org提供了系統源碼的交叉閱讀瀏覽,可能會發現很多有趣的地方。
好了,溫情默默的套近乎結束,進入正題。
調度和調度器
Solaris的調度單位是kthread_t,FreeBSd是thread, Linux是task_struct。抬高一級,Solaris的進程是proc_t,當然每個進程裡的線程就是kthread_t;Linux的進程和線程都由task_struct 表示,單線程的進程在Linux裡是一個task_struct。單線程的進程在Solaris裡有一個proc_t,一個kthread_t,還有一個 klwp_t表示。klwp_t提供了使用者和核心模式線程切換的儲存區。FreeBSD裡的單線程進程有一個proc ,一個thread 和一個ksegrp 。ksegrp 是“核心調度的實體組kernel scheduling entity group”。三個系統的線程表示結構不同,不過都支援調度線程。
和大家熟悉的基本一樣,調度是基於優先順序的。小小的數學問題是,在Linux和FreeBSD裡,數字越小,優先順序越高;而SUN的寶貝卻喜歡數字越大,優先順序越高。參考下表
三個系統都更推崇interactive 線程/進程(下面會提到interactive怎麼回事)。Interactive 線程比compute-bound 線程優先順序要高,不過得到的時間片要少一些。Solaris,FreeBSD和Linux都使用每CPU的“運行隊列 runqueue”。FreeBSD和Linux有一個active隊列和一個expired隊列。名字說得很清楚了--系統從active上按照優先順序選擇線程進行調度。用完自己時間片的線程就從active搬到expired上(或者為了避免“餓死”的其他情況),active空以後,核心交換 active和expired。FreeBSD還多一個idle 隊列--其他兩個queue都空的時候才輪到這個。Solaris的概念是每CPU“調度隊列 dispatch queue”。線程用完時間片後,核心給其一個新優先順序然後放回調度隊列。所有3個系統的runqueue,對不同優先順序的可運行線程都分別有鏈表。
FreeBSD四個優先順序共用一個鏈表,Solaris和Linux則每個優先順序一個鏈表Linux和FreeBSD結合已耗用時間和睡眠時間計算線程的interactive-ness,Solaris查表。他們都不支援“gang scheduling”(有興趣查Google即知,並行計算上的調度演算法,大白話說就是一組任務一把disptach到各個CPU上。勞倫斯.利弗莫爾那幫造原子彈的傢伙最喜歡了,他們有世界上最昂貴的玩具,可以理解)每個OS都調度下一個線程而不是N個線程開始運行。這3個OS都有利用CACHE (warm affinity)和負載平衡的機制。對超執行緒CPU,FreeBSD能盡量將多個線程保持在一個CPU節點上(當然可能是不同的CPU超執行緒上)。 Solaris也有類似機制,不過是在使用者和應用的控制下,而且並不限於CPU的超執行緒,他們的術語是processor sets,FreeBSD的叫法是processor groups和其他2個OS最大的不同是,Solaris同時支援多個“scheduling classes”。3個OS都支援POSIX的SCHED_FIFO,SCHED_RR和SCHED_OTHER (或者SCHED_NORMAL)。SCHED_FIFO 和SCHED_RR通常支援即時線程(我不同意。。。但是照翻。。。)。
Solaris和Linux為支援即時線程都支援了核心搶佔。Solaris支援fixed priority類,system class的是系統線程(比如換頁線程),interactive的是在X控制下運行視窗環境的線程,還有一個Fair Share Scheduler 用於資源管理。具體可以參考Solaris資料。FreeBSD的調度器是在編譯時間決定的,Linux的調度?--要看版本了。
支援在系統中加入新的調度類是要付出代價的。核心中每個可能決定調度的地方都得有一個間接得函數調用去call調度類相關的代碼。比如,當一個線程將要 sleep時,核心調用調度類相關代碼,完成該類中線程sleep需要完成工作。在Linux和FreeBSD上,調度已經完成了所有工作。不需要再來一個間接調用。額外的層次,就意味著Solaris的調度要佔用稍微多一點的系統開銷--不過提供了更多的功能。
記憶體管理和分頁
Solaris的進程地址空間由邏輯段segment組成。進程地址中的這些段可以通過pmap訪問。 Solaris將其記憶體管理代碼和資料結構分為平台無關和平台相關部分(這不跟沒說一樣嘛。。。)。平台相關部分位於HAT(hardware address translation)層。FreeBSD用vmspace描述進程地址空間,將其劃分為邏輯塊region。硬體相關部分在pmap (physical map)模組,而vmap 常式處理硬體無關部分和資料結構。Linux使用記憶體描述符劃分進程地址空間,邏輯單位是memory areas。Linux也由pmap來examine 進程地址空間。
Linux將機器相關層從更高層次的機器無關層中劃分出來。Solaris 和FreeBSD中大多數類似代碼比如page fault處理是機器無關的,而Linux處理page fault的代碼則非常機器相關--從fault處理開始就是這樣了。由此下來的結果是,Linux能很快地完成大多數分頁相關代碼--因為資料抽象更少。不過,代價是,下層硬體的改變需要大量修改代碼--Solaris和FreeBSD則分別把這樣的工作堵截在HAT和pmap層搞定。
Segment,region和meory area的分割是:地區的虛擬位址segmetn/region/memory area映射的object/檔案的位置許可權map的大小
例如,程式的text(text段,即代碼)在一個segmetn/region/memory area中,OS管理地址空間的機制是類似的,不過資料結構名字完全不同。
分頁3個系統都使用了最近最少使用least recently used演算法的變種完成頁替換。他們都有一個守護daemon進程/線程完成頁替換。FreeBSD的是vm_pageout daemon,它周期性地,或者當free的記憶體不多時,被喚醒。當可用記憶體低於某個限制時,vm_pageout 運行常式vm_pageout_scan掃描記憶體並釋放一些頁面。vm_pageout_scan常式可能需要非同步地將更改過的頁面寫回到磁碟,在釋放他們之前。不論由多少顆CPU,只有一個這樣的daemon。Solaris的是pageout daemon,它也周期性地運行,處理空閑記憶體不多的情況。Solaris中的分頁限制值在系統啟動時自動校準,這樣可以避免該守護進程過渡佔用CPU或者向磁碟發出洪水般的換頁請求(嗯,flood這麼翻正好 ;P )。
FreeBSD的daemon在大多數情況下使用的值是固定的--不過也可以調整。Linux的LRU演算法可以在運行時動態調整,而且可以有多個kswapd daemon,每CPU最多一個。這3個系統都使用global working set策略,而不是per process working set。FreeBSD有多個頁面鏈表來追蹤最近使用頁。包括active,inactive,cached和feee頁。根據使用方式,頁面在這些鏈表間走來走去。經常訪問的頁面會在active上。退出的進程的資料頁面將被馬上放到free上。
如果因為負載原因vm_pageout_scan 來不及掃描全部記憶體的話,FreeBSD核心可能將整個進程全部換出。如果記憶體短缺十分嚴重,vm_pageout_scan 可能會kill系統中最大的進程。Linux也使用不同的頁面鏈表。實體記憶體被分為(多個)3重zone:一個DMA頁面,一個普通頁面,一個動態分配記憶體頁面。zone的實現很像由於x86架構限制而很產生的。頁面在hot,cold和free鏈表間移動--機制和FreeBSD的類似。經常用的頁面在 hot上。可用頁面則在cold或者free上。
SUN的大佬使用free鏈,雜湊鏈,vnode頁面鏈支援自己的LRU實現。後兩者大致相當於FreeBSD和Linux的 active/hot鏈--也是FreeBSD和Linux要掃描的鏈。Solaris要掃描的不是這兩個對象,它用two-handed clock演算法掃描全部頁面(見Solaris Internals 或其他什麼地方隨你便)。大致方法是,兩隻手相隔固定舉例,前面的手將page的引用位清空以作為標識,如果自此開始沒有進程引用這個頁,後面的手就釋放這個頁面(當然如果需要就寫回磁碟)。
3個系統在分頁時都考慮了NUMA本地性。他們都把IO buffer cache和虛擬記憶體頁面的cache合并到一個系統頁cache中。系統頁cache用於讀寫檔案已經被mmap了檔案,還有應用的text段和data段。
檔案系統
3個系統都使用資料抽象層嚮應用隱藏檔案系統實現細節。就是用大家熟悉的open,close,read, write,stat,等等系統調用訪問檔案,無論下層的檔案資料的實現和組織如何。Solaris和FreeBSD把這種機制稱為VFS (virtual file system),基本資料結構是vnode(virtual node)。Solaris和FreeBSD裡每個被訪問的檔案都有一個賦給他們的vnode。除了generic 的檔案資訊外,vnode還包含到file-system-specific 資訊的指標。Linux採用了詳細的機制,也叫VFS(virtual file switch),檔案系統無關的資料結構是inode。這個機構和vnode類似(小心:Solaris和FreeBSD也另有自己的inode--是 UFS檔案系統裡file-system-dependent 的資料)。Linux還有兩個不同的結構,一個用於檔案操作,另一個用於inode操作。Solaris和FreeBSD將他們合并為vnode操作。
VFS允許在系統裡實現多種檔案系統。這意味著他們相互訪問對方的檔案系統沒問題。只要相關的檔案系統常式和資料結構已經被移植到VFS上。所有這3個系統都允許檔案系統堆疊stacking。下表列出了每個OS實現的檔案系統類型,不是全部哈。
結論
Solaris,FreeBSD和Linux顯然都在從對方身上獲益。隨著Solaris 的開源,這種相互促進有望更快。Max個人已經感覺到Linux的變化是最快的。新技術被快速地整合進系統,只是文檔和健壯性可能有點落後。Linux有很多--或者有時是看上去有很多--開發人員。FreeBSD則大概是(從某種意義上)3個系統中曆史最長的。Solaris來自BSD Unix和AT&T Bell實驗室Unix的結合,使用了更多資料抽象層,因而一般說來能更簡便地支援更多功能。不過,核心中大多數這樣的分層都沒有文檔描述。可能隨著代碼的開放這一點會有所改善。
至於他們的差別,最大的地方之一是page fault處理了。在Solaris中,發生page fault時,代碼是從平台相關的trap handler開始執行的(以大家的智商,這好像不用說了吧。。。),然後會調用generic的as_fault常式,這個常式判斷髮生page fault的segment,然後調用segment driver處理page fault。segment driver調用檔案系統代碼,後者再調用進驅動程式,換入頁面。換入完成後,segment driver 調用HAT層來更新頁表項。在Linux上,發生page fault後,核心調用的代碼在會馬上進入平台相關部分,這些處理可能更快,不過可能不太容易擴充和移植(後半段說得太省,不知道作者有沒有真的研究過 Linux下對應的處理過程)。
核心觀察和調試工具對正確理解系統行為有關鍵意義。在這方面,Solaris有kmdb,mdb和DTrace 。在開源之前,Max就對Solaris做過多年“反向工程”--他發現解決問題的時候使用工具總比閱讀代碼來得快--我也知道,不過得看什麼場合,大家可不要被他誤導。Linux嘛,我看作者Max不太熟,所以認為沒有太多工具。對FreeBSD,他也認為只是可以用GDB調試核心的dump-- Liux也可以。