Unix內核開發不是洪水猛獸。一旦你了解到其中的規則,你就會發現,跟開發應用程序一樣;兩者區別在於要遵守的規則集合不一樣。Linux是Unix家族的一員,而且其Unix內核源代碼唾手可得,因此這裡用其來作說明。
規則上,與應用程序(運行於用戶空間)的開發不同,主要表現在:
沒有C庫
用GNU C編程(對於Linux內核而言)
沒有內存保護
在Unix內核中很難使用浮點數
內核棧大小固定且很小
由於異步中斷、搶占以及支持SMP,需要額外小心同步和並發
移植性問題
Unix內核沒有鏈接任何C庫。這裡面涉及到很多問題,比如雞生蛋還是蛋生雞的問題:因為C庫都會包裹一些系統調用,可是沒有Unix內核時就沒有系統調用,那麼……呵呵,明白了吧?另一個問題就是大小問題。
任何一個C庫,甚至其一個子集,對於內核來說都太大了。不過,不要著急,很多常用的庫函數在內核中都有實現。
這裡面涉及到一個著名的函數:printf(),Unix內核提供了一個替代品:printk()。如果你要做內核開發,就會頻繁使用該函數。記住:Linus本人不允許在Unix內核中嵌入調試器(這是另外的話題,有興趣的可以自己去google一下),因此很多情況下要依靠printk()。
毫無疑問,Linux的內核是用C語言寫的。但所用的C並不是ANSI C,而是經過GNU擴展之後的C,這就是為什麼Linux內核對於gcc編譯器的依賴程度如此之高。GNU對C的擴展中就包括:內聯函數(inline functions),分支預測和內聯匯編(inline assembly)。分支預測用於判斷哪些情況是幾乎永遠不可能發生的,或者哪些情況幾乎永遠都會發生——unlikely()和likely()。
當用戶空間的代碼訪問非法地址時,Unix內核能夠捕獲該錯誤,然後向進程發送SIGSEGV並終止進程。在Unix世界,人們總是說kill/殺掉進程,其實,kill僅僅是用來向進程發送信號的,並不是殺掉它——太殘忍了。
這是題外話了,呵呵。那麼,當內核代碼訪問非法地址時,誰來照顧Unix內核呢?只能自己照顧自己了。非法的地址訪問將導致oops,這是重大的問題,沒人會告訴你訪問了非法地址,但是你可以通過日志來查詢/調試。
另外,內核內存是不分頁,因此你沒申請一個字節,物理內存就少掉一個字節。小心了!這裡,我們對Unix內核的講解先告一段落,我們以後會有更多的講解。