#osxchat blog

2004/11/27

神奇(淫邪?)的Darwin

作者: lukhnos

不要被標題騙了,以下純粹是技術性的文章。

昨天和gugod聊到,「怎麼寫個OpenVanilla變成這樣...」,gugod笑說,「... 變成各種程式技巧的練習」。我是那種因為有了Mac、因為OS X才真的認真學起UNIX上各種工具的人,尤其是UNIX上的程式設計。而因為OpenVanilla的關係,使得我們最近對Darwin,也就是OS X基底的作業系統,稍有多一點的瞭解。

例如前陣子我們才知道,Darwin雖然支援BSD-style的dlopen() 函式庫,也就是可以在runtime 把程式庫「掛」進來,做到plug-in 的效果,但是Darwin 的dlopen() 事情只做了一半。Mach-O (就是Darwin 上的binary executable format)所做出來的共用程式庫,預設的檔案附屬名是 .dylib (Linux 上則是 .so),一旦dlopen() 開啟了,就不能dlclose() !意思是說應用程式如果載入了某個.dylib 的plug-in ,一旦載入了,就沒辦法「熱抽換」了。

為了這個問題,根據#opendarwin 頻道上的說法(Pogma, 也就是 Darwin dlopen() 函式的原作者),Apple 在2000-2001年時弄出了一個叫 "bundle" 的格式。Bundle 是可以卸載的shared library,基本上是Apple 對Mach-O 檔案格式做了很多dirty work搞出來的東西。

問題是...... 不管是 .dylib 還是 .bundle 也好,Darwin 的shared library (dynamically loadable binary executable 是比較精準的講法),竟然沒有一個其他UNIX上都有的重要屬性:reference count! 意思是說,在Darwin 上,所有 run-time 時期載入的程式庫,都是系統準備一份拷貝,掛進應用程式的記憶體空間裡頭。如果是這樣,OS X上許多有外掛能力的應用程式(例如PhotoShop),吃RAM吃得兇也就不意外了......

另一個令人昏倒的事情是,雖然 .bundle 是可以卸載的(可以用 dlclose() 再重新用 dlopen() 重新載入或是抽換等等),但是...... 如果這個 .bundle 是用 Objecitve-C/Cocoa 寫成的,嘿嘿!對不起,這個 .bundle 一樣不能 dlclose()──只要應用程式把 Objective-C 寫成的 .bundle 拿去關,奪命彩球就立刻拼命地轉──然後就segmentation fault了。究其原因是,Objective-C 的runtime system 做了太多奇怪的事,使得一旦啟動了就不能關掉。Apple 自己也在Cocoa 的文件說了,「用Obj-C 寫成的Bundle 是不能動態關閉的」。

結果Apple 原先為了解決 .dylib 不能動態卸載的問題,搞了個神奇的 .bundle 格式。但是 .bundle 也只做了半套,沒有 reference counting。然後說是能卸載,結果自家最引以為傲的 Cocoa framework / Objective-C 不買帳,這...... 簡直是 much ado about nothing (幹了這麼多白工!)的事嘛。

最近的新發現是,Darwin 的 gcc ,預設的linker 選項是,只要 /usr/lib 裡有可動態載入 (shared library) 的版本,就一定不會載入靜態連結版的程式庫。Apple 為了這件事,甚至沒有提供 crt0.o ,意思是,你想要編一份強迫靜態連結標準C 程式庫的二進位執行檔,還得自己去弄一份 crt0.o 來!Apple 不提供 crt0.o 的理由寫在他們的官方文件裡

另一個發現是,Darwin 的gcc 有一套非常神奇(用Autrijus 的講法叫「淫邪」)的選項,叫 ZeroLink ,據Apple 的說法是,這是為了加快開發時期的linking 速度所提供的功能。簡單地說,就是把所有原來在連結時期要查的表、要核對、繫結的函式及資料符號,全部通通延後到執行時間再用「懶人」(lazy) 的模式去連結──所有的 .o , .dylib, .a 都一樣!根據Apple 的文件,ZeroLink 編出來的程式只能在開發時期使用,因為執行檔內寫死了所有先前連結 .o 檔的絕對位置。

要知道 ZeroLink 到底有多大的差別(或者有多「懶」),簡單寫一個 C++ 版的 "hello, world" 就知道了。Darwin 很奇怪地竟然沒有 libstdc++ 的動態版本(libc 卻有)。以至於任何使用了 iostream (例如 cout << "foobar") 的程式,執行檔的基本消費都有 600K 以上。一旦用上 ZeroLink,執行檔瞬間縮小到 9K !

我在OS X 之前從來不瞭解UNIX 程式設計的細節。很意外(也很好笑)的是,當初本來買Mac 是為了再也不想管那些有的沒的系統細節的我,反而因為OS X才真的覺得UNIX可親,而寫程式還是有樂趣可言。結果反而花了更多時間在這些「有的沒的」的事情上。只是我到現在還是遜腳一枚。

最後一提,打 "man gcc",就會看到一大堆 Apple/Darwin 專用的gcc 選項(例如 gcc -fast 可以編出 PPC G5 專用的執行檔),好像也真的只能用「奇技淫巧」來形容Apple 對Darwin 所做的各種量身打造工夫了。

0 篇留言:

張貼留言

? 回前頁