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