分類
科技相關 Mac

【翻譯】XNU:核心

Mac OSX 核心叫做XNU。有關他的說明簡述如下:

Mach

XNU內的程式碼主要是基於Mach 3.0版所開發出來的。Mach本身是卡內基、梅隆大學在1980年代中期一個傳奇性的研究計畫的產物。目前Mach也漸漸成為許多重要系統的核心了。 較早版本的Mach還是一個擁有許多BSD程式碼的單一核心,Mach3.0則是第一個微核心。
XNU的Mach並不是微核心。核心中包含像是BSD的子系統以及其他具代表性的子系統。XNU的Mach還是負擔了一些系統裡較低層次的部份,像是:

  • 核心執行緒支援先佔式多工。
  • 記憶體保護
  • 虛擬記憶體管理
  • 內部程序通訊 ( IPC )
  • 中斷管理
  • 即時支援
  • 核心Debug支援
  • console I/O

如前一章所述,在核心接續接續開機程序之前,bootloader會去呼叫核心的「啟動」程式碼,以進一步進入不同的開機模式。這個低層程式碼是任何一種系統開機程序裡面都會有的。 各種不同的重要變數都會初始化,像是:最大虛擬或物理位址、CPU降速溫度調節閥,同時BAT也會清除,Altivec和快取也都會初始化。之後程式會跳到系統架構的初始程式碼(PowerPC 上的 ppc_init())。
其後程序如下:

  • 一個模組執行緒將會插入,並從中產生一個初始化的執行緒來作為現時執行用的執行緒。
  • 部份CPU終至在執行的程序完成。
  • 「Platform Expert」初始化(PE_init_platform())。而同時設置旗標指出未被初始化的虛擬記憶體。
  • Mach 虛擬記憶體初始化
  • 呼叫machine_startup()程序。這步驟會用到許多系統內部的變數。並呼叫calls setup_main()
  • calls setup_main()會完成很多工作,像是:initializing the scheduler, IPC, kernel extension loading, clock, timers, tasks, threads…等等。最後還會產生一個核心執行緒,叫做tartup_thread,來完成更進一步的其他核心執行緒。
  • startup_thread會製造許多其他的執行緒(靜態的執行緒、負責時間與裝置的執行緒…..等等)。

此時,輪到Mach開始運作執行。
附帶一提,BSD系統會載入相當多APIs,並使用Mach messaging和IPC來讓使用者與系統核心之間轉換資訊。
XNU也提供相當多不同的方式來處理記憶體設置。因為對許多程式設計師來說處理這些問題是相當大的負擔,所以這樣的設計可以讓他們能用更聰明的方式去選擇要使用的API。
這在Mac OSX使用者部份的API處理方式也是一樣的。

BSD

XNU的BSD是以FreeBSD為主要程式碼參考基準(雖然說某些程式碼是參考其他BSD的)。Darwin 7.x(Mac OSX 10.3.x)之後是使用FreeBSD 5.x。如同前面說過的,BSD在這裡並非用在外部(或者說:使用者層級)的部份, 而是系統核心本身的一部份。在某些方面BSD還擔負以下的任務:

  • 程序模型
  • user ids, 權限, 基本安全
  • POSIX API, BSD style system calls
  • TCP/IP stack, BSD sockets, firewall
  • VFS 和 檔案系統
  • System V IPC
  • crypto framework
  • various synchronization mechanisms

XNU雖會統整暫存的快取,但是這東西都會跟Mach的虛擬記憶體綁在一起。
XNU利用一種叫做「漏斗」的同步摘要技術來整合核心中BSD的部份,而核心中會有個用「_flock」作為字尾的東西來將核心中的變數指向「漏斗」,像是kernel_flock或network_flock。當Mach透過呼叫bsd_init()來初始化BSD子系統 時,系統會先將這些漏斗定址(像是核心的漏斗就會定為TRUE)。之後:

  • 核心記憶體運算元會被重新初始。
  • “Platform Expert" 會去偵測有無任何關於BSD啟動的設定
  • VFS 暫存檔列表會重新定址並初始化
  • 程序相依性架構會重新定址並初始化
  • 這裡會創置一個Process 0,而核心程序的變數就是指向Process 0
  • 主機獨立時間和日期也會初始化
  • 統一的暫存快取初始化
  • 不同的 VFS 架構 初始化: the vnode table、檔案系統架構、 the vnode name cache, etc. 每一個現時的檔案系統時間也都初始化
  • mbufs (記憶體暫存、 網路上已使用的記憶體管理) 透過mbinit()初始化
  • 系統環境以及子系統像是 syslog, audit, kqueues, aio, and System V IPC 也都初始化
  • 核心的總 MIB (management information base) 初始化
  • 資料連結介面層初始化
  • Sockets 和各種協定初始化

XNU使用一種特製的核心延伸方式,NKEs(網路核心延伸套件)來讓4.4BSD網路架構可以適用於Mac OSX

  • 開啟核心剖析且 BSD 被當作是IOKit的資源
  • 乙太網路裝置初始化
  • A Mach Zone is initialized for the vnode pager.
  • BSD試著去掛載根目錄檔案系統 (這根目錄系統可以是透過網際網路來掛載,透過NFS掛載MacOSX映像檔–.dmg).
  • devfs 掛載在 /dev
  • kernproc (process 0)創造一個pid 1的新程序(cloned)以作為init (actually mach_init, which starts init)。 mach_init 透過 BSD asynchronous trap handler (bsd_ast())所呼叫出來的bsdinit_task()載入並執行

I/O Kit

I/O Kit,這個XNU核心裡面的物件導向裝置驅動程式韌體跟一般傳統系統是非常不同的。
I/O Kit使用有限制的C++語言子集合(基於Embedded C++)來撰寫,而這系統是由核心的函式庫提供函式。其中,C++在這子集合裡面的限制有:

  • 例外事件
  • 多重繼承
  • 多模組
  • RTTI (run-time type information), I/O Kit 有自己的執行時期系統

I/O Kit也提供裝置驅動程式模組許多相當有用的東西:

  • 各種裝置族群 (ATA/ATAPI, FireWire, Graphics, HID, Network, PCI, USB, HID, …)
  • 各個裝置的物件導向抽象元件可以相互分享使用
  • 隨裝即用以及熱插拔
  • 電源管理
  • 先佔式多工處理, 執行緒, 對稱式多重程序執行, 記憶體保護以及資料管理
  • 驅動程式動態比對以及載入 (multiple bus types)
  • 物件資料追蹤以及資訊建立的資料庫功能(the I/O Registry)
  • 全部系統上可用的I/O Kit classes資料庫 (the I/O Catalog)
  • 衍生API
  • 應用程式架構以及使用者介面與user-space drivers和I/O Kit之間的溝通
  • driver stacking

I/O Kit供給工具包含核心中3個C++函式庫,並可以供載入的驅動程式使用:IOKit.framework、 Kernel/libkern 與 Kernel/IOKit。藉著擷取各種不同的硬體、軟體之間的動態關係,I/O Kit包含模組化、層級化執行時期架構, 而這就是底層硬體的抽象化的現時情況。
各種不同的工具像是:ioreg, ioalloccount, ioclasscount, iostat, kextload, kextunload, kextstat, kextcache…等,讓我們可以操控I/O Kit。舉例來說,下列示範就能顯示出核心動態載入的物件:

% kextstat
Index Refs Address Size Wired Name (Version)
1 1 0x0 0x0 0x0 com.apple.kernel (7.2)
2 1 0x0 0x0 0x0 com.apple.kpi.bsd (7.2)
3 1 0x0 0x0 0x0 com.apple.kpi.iokit (7.2)
4 1 0x0 0x0 0x0 com.apple.kpi.libkern (7.2)
…

下列指令就會顯示出I/O Kit登錄檔的細節:

% ioreg -l -w 0
+-o Root
| {
| “IOKitBuildVersion" = “IOKit Component Version 7.2:
Thu Dec 11 16:15:20 PST 2003;
root(rcbuilder):RELEASE_PPC/iokit/RELEASE
"
| “IONDRVFramebufferGeneration" = <0000000200000002>
…
/* thousands of lines of output */

Platform Expert

Platform Exper是個能知道現在系統正在跑甚麼平台的物件(你能先把他看成是個驅動程式)。I/O Kit會登錄一個節點給Platform Exprt,而這節點會去載入正確的驅動程式給平台,甚至更進一步去發現系統上正在執行的匯流, 並為每一個匯流登錄一個節點。然後I/O Kit會為每個匯流節點載入正確的驅動程式,並將發現的裝置與節點連結起來。總結來說,Platform Expert負責:

  • 建造裝置樹狀結構
  • 分析開機程序內容
  • 辨別機器硬體 (包含硬體與bus clock 速度)
  • 如果發生kernel panic,則初始化其使用者介面

libkern and libsa

如前面所述,I/O Kit是使用C++的受限制的子集合,而這東西內容主要是libkern,提供以下的服務:

  • 動態元件整合 、建構、反建構(including data structures such as Arrays, Booleans, Dictionaries, …)
  • 自動化作用、 混合式方程式 (bcmp(), memcmp(), strlen(), …)
  • 追蹤即時class的預覽功能
  • 避免"Fragile Base Class Problem"

libsa則提供許多方程式來處理多樣的目的:binary搜尋、symbol、 remanglinggraphs, catalogs, kernel extension management, sorting, patching vtables…等等。

分類
科技相關 Mac

【翻譯】Mac OSX開機

現在我們來談談有關Mac的韌體(很多時候這東西可以說很類似PC上面的BIOS),要大家注意的是新舊版的ROM之間非常不同。舊版像是68k這種舊世界的PowerMacs,新版的就像是目前可見的Mac,用的是Open Firmware3.x版。而我們只討論較新的版本。
雖然說這些韌體不包含在MacOSX裡面,但是對機器上運行的系統來說,它還是扮演著非常重要的角色,此外這玩意兒在除錯上也是非常有用,所以我們才會在這邊加以論述。

Open Firmware

背景

Open Firmare(IEEE-1275 開機韌體標準:執行以及條件核心)是個非專利的平台。這個就像是PC上的BIOS,寫在ROM裡面,是電源開啟時電腦第一個載入的程式。
Open Firmware是用Forth程式語言:FCode所編寫而成的。而目前主要使用Open Firmware工具的電腦系統有Apple和SUN(SUN把這個叫做OpenBoot)。 大家可以參考Open Firmware的網頁。 (譯者註:有關Forth語言大家可以參考http://www.figtaiwan.org/f_intro.htm和http://www.figtaiwan.org/f_history.htm)
正如前面所述,firmware是用Forth語言寫在ROM裡面,而以bytecode的方式執行,所以電腦的設備驅動程式也是以類似的模式在系統啟動之時運行。 這些驅動程式通常是在擴充卡上的延伸ROM裡面,並且在作業系統啟動之前就被載入。

相互作用

你可以在按下Mac電源時,同時按著cmd+opt+O+F 這四個鍵,來進入Open Firmware。cmd鍵就是所謂的蘋果鍵,而opt就是Option鍵。之後你應該會看到一個歡迎訊息並進入一個提示像是下面這樣:

ok
0 >

你可以打入mac-boot來開機或是輸入shut-down來關機。
雖然說Forth “shell“提供一個還算不錯用的文字操作編輯模式(你可以藉著ctrl+a到該行的起始點;ctrl+e到結束點;ctrl+u殺掉該行;up-arrow鍵看輸入記錄), 但是你會發現透過網路從其他別的電腦來操作Mac的Open Firmware將更方便,特別是如果你想要在firmware上寫入些程式碼。下面是個指令範例(Open Firmware除了透過文字模式操作外,別無他法):

0 > dev /packages/telnet

注意:
當你輸入成功的時候,Open Firmware會在你按下的那一行列出ok字串。所以在這一篇文件中所舉的範例,如果你看到ok,記得這個是Open Firmware所列出的,不是要你輸入的指令喔!

前面所舉的例子,如果你的Mac的Open Firmware裡面有telnet server這套件,則應該會看到:

0 > dev /packages/telnet ok

如果你看到ok,則你就可以在上面使用TELNET server 服務:

" enet : telnet , 10 .0 . 0 . 1 " io

這樣就會在你的機器用IP10.0.0.1在上面跑TELNET server(當然你也可以選用你個人喜歡的IP),之後你就可以透過telnet連接到這台機器的Open Firmware,Windows也可以連接上喔!

注意:
G4之後的Apple Mac機種,會透過乙太網路port作自動偵測以及自我設定,所以你不需要另外用跳線就能直接連接到其他電腦。

範例:

1.這個指令可以列出你的裝置:

0 > dev / ls
ff880d90: /cpus
ff881068: /PowerPC,750@0
ff881488: /l2-cache
ff882148: /chosen
ff882388: /memory@0
ff882650: /openprom
ff882828: /client-services
…
More [,,q,a] ? _

2.這個指令可以告訴你有關RAM的資訊:

0 > dev /memory .properties ok
name memory
device_type memory
reg 00000000 10000000
10000000 10000000
slot-names 00000003
SODIMM0/J25LOWER
SODIMM1/J25UPPER
…
dimm-types DDR SDRAM
DDR SDRAM
dimm-speeds PC2700U-25330
PC2700U-25330
…

我們可以看出來這台機器(PowerBook G4 15″)有兩條個PC2700 DDR SDRAM。而兩對 reg欄的數字表示記憶體中的起始定址位置和晶片的大小, 也就是第一條ram 晶片從0x0000000這位址開始,大小是0x10000000(也就是256MB啦)。第二條的晶片從0x1000000開始,大小也是256MB,所以總記憶體就是512MB囉!
如果因為各種理由使得你必須把記憶體回復到原廠的樣子,而又不想拆機器拆下記憶體(或是想模擬記憶體較少時的狀態),你可以用delete-property指令來刪掉reg欄。 像是下面的例子就是把我們機器中的第二條記憶體暫停(記得,這個指令只有暫時的作用,當你重開機記憶體就會自己抓回來)

0 > " reg" delete-property ok
0 > 0 encode-int 10000000 encode-int encode+ " reg" property ok

注意:
特別需要注意的是更改reg這方法有其風險,而且也常因為機器的不同而不一定能適用(像是PowerMac G5就不能這樣搞),因此如果你想限制記憶體最保險的方法是透過maxmem:

# nvram boot-args="maxmem=128″

3.下面這組指令可以告訴你各種有關機器上CPU的資訊:

0 > dev / ok
0 > dev /cpus ok
0 > ls
ff886d58: /PowerPC,G4@0
ff8871f8: /l2-cache
ok
0 > dev PowerPC,G4@0 ok
0 > .properties
name cpu
reg 00000000
cpu-version 80020101
state running
clock-frequency 4a817c7b
bus-frequency 09ef21aa
…

電腦輸出的結果說明了各種不同的cache大小以及晶片繪圖能力…..等等,你可以當作這是類似Linux上面 /proc/cpuinfo 的功能。

4.下面這指令會列出root目錄下的檔案

0 > dir hd:
Size/ GMT File/Dir
bytes date time Name
6148 12/25/ 3 4:25:25 .DS_Store
156 9/12/ 3 20:41:59 .hidden
589824 12/25/ 3 6:45: 6 .hotfiles.btree
…

5.下面這指令會詳述hd,並以樹狀圖的方式給你完整的裝置路徑:

0 > devalias hd /pci@f4000000/ata-6@d/disk@0 ok

6.你可以用load指令來載入核心檔案,並用boot指令來用這檔案開機。當然就像是前面說過的,通常我們還是用mac-boot和shut-down來開關機, 而你可以透過printenv和setenv來取得或設定環境變數。這些變數都設定在Open Firmware裡並存在非揮發性記憶體上(NVRAM)。舉個例來說,如果你想用“OEM banner“來當你的e-mail位址,你應該這樣做:

0 > setenv oem-banner you@your.email.address
0 > setenv oem-banner? true

其實你不需要進入Open Firmware裡面來設定NVRAM的變數,你可以在OSX裡面透過nvram這指令做到一樣的事。
總結以上所述,Open Firmware 其實就是個控制、除錯、探索你的電腦的強力工具!

運作過程
當開機電源打開之時,Mac的硬體會進行一個自我診斷程序(透過一些POST的程式碼)並初始化,之後第一個掌控了CPU的就是firmware—韌體。 Open Firmware此時會開始建立裝置架構、偵測裝置插槽、檢測PCI裝置並為各個裝置定址,隨即再去尋找開機裝置。“snag“可以讓使用者在通電之後看到開機裝置:

C device referred to by the ‘cd’ alias, a CD-ROM drive
D device referred to by the ‘hd’ alias, a hard disk drive
N device referred to by the ‘enet’ alias, a network card
Z device referred to by the ‘zip’ alias, a ZIP drive

有人會特別把開機時按住T鍵然後進入的模式取名叫:Firewire目標磁碟模式,個人覺得沒啥意義。事實上這不過就是把你的Mac裝成一個延伸的Firewire磁碟裝置。
你可以透過網路開機的方式並使用TFTP來取得更完整的裝置路徑說明:
boot enet:,,;,;
如果此時Open Firmware找不到開機裝置,就會顯示一個閃爍的檔案夾。
Open Firmware然後會從系統磁區載入tbxi類型的檔案(ToolBox ROM Image),注意在Mac OS9的System Folder裡叫做“Mac OS ROM“,而Mac OSX則是從 /System/Library/CoreServices/BootX 載入。 BootX之後會執行並且控制權也換到它手上。
提醒一點,雖然Open Firmware可以直接載入 ELF,XCOFF和“bootinfo“(任何被支援的有XML檔頭的格式),但是卻不能載入Mac OSX真正可執行的原生格式—-Mach-O。所以需要BootX,因為BootX可以載入Mach-O。

Bootloader

BootX(/System/Library/CoreServices/BootX )是Mac OSX預設的開機管理員程式。
BootX也是一個開放源始碼的開機管理員程式的名字(跟Apple的BootX是不一樣的程式喔),這程式能讓你在「舊時代」的Mac機器上雙重開機:Mac OS和Linux。
BootX會根據不同的檔案系統載入不同的核心:HFS+ , HFS , UFS , ext2 , 和 TFTP(網路開機時,會當作是一種檔案系統)。除了Mach-O之外,BootX還能載入ELF核心,可是Apple並沒有在OSX上面運用這個功能。 換句話說,BootX可以在ext2磁區中載入ELF核心。
因為「舊時代」的Mac機在處理Open Firmware個有不同的問題,所以對Apple的工程師來說就造成了許多開機上的問題,同時對Linux PPC的port工作上也就有同樣的困擾。 Apple這邊透過許多NVRAM上面的修補程式或是直接更改Bootx程式碼的方式,來解決這樣的問題。而隨著BootX的成熟,Apple也增加了對ext2和ELF支援的目標,使得OSX有跟Linux PPC競爭的實力。
緊接著BootX執行的程序是:
1. BootX首先會初始化Open Firmware客端介面(這介面會開始跟韌體溝通)並取得韌體的版本。
2.接著它會在韌體裡製作一個假的裝置叫做 sl_words (‵sl′表示次級載體),並定義各個FORTH字串(這時會顯示一個旋轉的游標)。
3.BootX接著會在韌體中尋找可選擇的裝置,並將各個變數導入(你可以在Open Firmware裡用printenv和setenv指令觀測和設定)。

0 > dev /options .properties
name options
little-endian? false
real-mode? false
auto-boot? true
diag-switch? false
…
boot-command mac-boot
…

4. BootX找出被選好的裝置,像是input/output裝置,記憶體,MMU,PMU,CPU,PIC…..等等。例如底下這Open Firmware指令會顯示出被選擇的裝置:

0 > dev /chosen ok
0 > .properties
name chosen
stdin ffbc6e40
stdout ffbc6600
memory ffbdd600
mmu …
…

5.BootX透過chosen將記憶體和MMU初始化。
6.BootX會將螢幕和健盤也初始化。
7.BootX會確認「安全模式」是否有啟動。
8.BootX會確認是否有「verbose」(即開機時按著cmd+v)或「單人模式」(cmd+s)
9.BootX確認系統是否要在「安全模式」上啟動
10.BootX會要求記憶體針對不同行程作調整
11.BootX會找到所有的螢幕並將之做好設定,這步驟主要藉由搜尋裝置樹狀目錄裡面關於螢幕的節點來達成。而主營幕就是透過screen來映射。例如你可以試試下面這Open Firmware指令:

0 > dev screen ok
0 > .properties
name ATY,Bee_A
compatible ATY,Bee
width 00000400
height 00000300
linebytes 00000400
depth 00000008
display-type 4c434400
device_type display
character-set ISO859-1
…

12.當螢幕啟動之後,BootX就會讓螢幕顯示我們熟悉的灰白色。
13.BootX會去找啟動裝置並從中定義要從哪裡抓到核心(mach_kernel),同時核心檔案的路徑來源也會建構起來。如果是從磁碟裝置啟動(通常也是這樣),則那個指向kext cache的路徑跟著其他延伸目錄 (通常是/System/Library/Extensions)一起被運算。
Mac OSX 用了些不同「kext」(kernel extension)的快取來加速kexts的載入。核心的快取通常是保留在 /System/Library/Caches/com.apple.kernelcaches 這目錄裡。 這些快取檔通常叫kernelcache.XXXXXXX之類的名稱,並且會用跟Gzip一樣的32位元的壓縮運算法儲存。
14.此時,BootX會在螢幕上畫出一個Apple的標誌,並開始出現旋轉游標。如果是網路開機,則是旋轉的地球。
15.依據不同的情況,BootX會試著追溯以及載入核心快取檔。
16.下一步就是翻譯核心檔。如果核心檔頭指出這是個壓縮過的核心,則BootX就會試著解壓縮(用的是LZSS壓縮格式,專門適合那種壓縮一次但是要常常解開的資料)。因為核心的二進位檔是個胖核心 (多重架構的程式碼都在同一個二進位檔裡),BootX將會先確定它是不是個胖核心,如果是,就想辦法讓它減肥。
17.BootX會盡量翻譯這些檔案轉為Mach-O二進位檔,如果失敗了,BootX也會努力將之轉為ELF。
18.如果又失敗了,BootX只好放棄,在螢幕上畫出開機失敗的圖片,進入無窮迴圈再重頭來過。
19.如果BootX成功了,就會儲存檔案系統快取,設置各種啟動環境參數,這步驟又叫做搞定各個裝置的遞迴方程式。
20.最後,BootX會呼叫核心。而很快地,它也讓許多像是firmware,timers或DMA等不同步程序都隨著Open Firmware沉寂安靜下來。

系統啟動

Mac OSX使用者層級的啟動計不是純粹的BSD模式也不是SYSV模式,雖然/etc/rc是從BSD繼承過來的。事實上許多東西比較像是NEXTSTEP。
下一章XNU : The Kernel將會說明有關核心的東西。Mac OSX System Startup則會接著介紹user-level的啟動方式。

BootCache

Mac OSX在開機時會用一個叫做Bootcache的東西對系統做最佳化,也就是讀取磁碟裝置並將之做索引為「playlist」。
所有可以備讀取的磁碟裝置都會作出索引在 /var/db/Bootcache.playlist。當這裝置載入使用時,這些快取就會自動生效。
需注意的是這個優點必須有128MB以上的實體記憶體。滿足這條件系統就會自動啟動這功能。

本文由張元毓所翻譯,本人保有一切法律權利。
Copyright@ alan chang