獨辟蹊徑品內核

出版時間:2009-08-01  出版社:電子工業(yè)出版社  作者:李云華  頁數:482  
Tag標簽:無  

前言

  幾乎每一個操作系統(tǒng)內核的學習者在初學階段都會感覺到難以入門。這是由于內核涉及到知識面非常廣泛,需要學習者從根本上掌握大量的知識,這包括:程序編譯,鏈接,裝載的細節(jié),操作系統(tǒng)理論,計算機系統(tǒng)體系結構,數據結構與算法,深厚的C/匯編語言編程功底。如此相對較高的門檻常常令很大一部分初學者望而卻步。那么是不是一定要先學好以上的各門知識后才能學習內核呢?事實上大部分學習者在學習以上各門知識都會遇到同樣的問題,因為知識是一個網狀結構。所以重要的不是先去學會什么知識,而是學會如何學習,學會在自己掌握的知識體系上提出問題,學會思考,進而堅持不懈的解決心中的疑問。筆者從學完C/C++ 語言開始,由于C/C++ 的示例程序都是在命令行下的,于是常常想如何才能編寫出視窗程序,學習了MFC,但是同樣想不通諸如WM_CHAR,WM_LBUTTONDOWN 的消息從何而來,帶著MFC 中諸多疑問,筆者開始學習Windows SDK 程序開發(fā),在這個學習過程中感覺對MFC 的認識更加深入了,但同時又有新的問題想不通,于是進而學習Windows DDK,之后開始學習操作系統(tǒng)內核。在這個過程中,筆者也遇到過數不盡的疑問,但是都是需要的時候再補充相關知識。因此初學者要明白,學習并不需要等到“萬事具備”了才可以開始。需要的是保持好奇心,養(yǎng)成思考的習慣,樹立解決問題的決心。很多讀者渴望尋找好的入門教材,也常常有人問看什么書才能進步的快,但是當他們看了別人推薦的書卻沒有取得同樣的收獲,這是為什么呢?筆者認為,讀書有以下幾種境界:  1. 面對書上講到的某個知識點,不能接合自己掌握的知識提出疑問,僅僅知識死記書本上的東西。這種狀態(tài)就算學到最高境界,也僅僅只是能把書本上的知識點完好的記下來在腦海中形成孤立的知識節(jié)點?! ?. 面對書本上講到的某個知識點,能接合自己掌握的知識提出疑問,但是大多數時候沒有探索精神,僅僅局限于到其他書籍或者請教別人來排除心中的疑問。腦海中的知識形成了簡單網狀結構,但由于探索能力長期得不到鍛煉,綜合自己的知識去分析和解決問題的能力十分有限。  3. 面對書上講到的某個知識點,能接合自己掌握的知識提出疑問,并且能根據問題補充相關必要的知識,不斷綜合分析各知識點的關系,提出各種假設和驗證排除的方法并親自驗證,解決不了問題決不罷休。如能經過長期鍛煉,其腦海中的知識點形成復雜的網狀結構,綜合分析能力必將加強?! ?. 根據自己掌握的知識,提出全新的問題,并始終堅持找到答案為止。這種境界需要淵博的知識作為基礎。  因此,不要還沒學內核就被嚇倒,說了這么多看似和內核無關的東西,就是要從先排除讀者的心理擔憂,樹立正確的態(tài)度,重要的不是學會什么,而是學會學習。確定自己處于哪一種學習境界,然后通過學習某項具體的知識把自己提升到更高的境界。在現(xiàn)實生活中我們不難發(fā)現(xiàn),能力強的學什么都又快又好。其根本原因在于他們處于更高的學習境界,并形成了良性循環(huán)!  有很多的人都渴望學習操作系統(tǒng)內核,但是內核涉及到的知識非常廣泛,因此很多人半途而廢,許多人往往抱怨沒有好的書籍,教材。實際上,對于同一本書籍,不同的讀者收獲也是不同的,這取決于他們的態(tài)度和學習方法。筆者建議,在讀書的時候,一定要以自己心中的疑問作為主線,而不要沒有任何疑問就死記書本上的知識?! ∪绾问褂帽緯 」P者認為對于任何知識的學習,首先是以自我為中心,任何書籍資料都是用來解答讀者心中的疑問的,因此在你閱讀一本書時,首先要明確自己的疑問是什么?這可以是一個非常梗概的問題,例如:“Linux 內核是什么?”;也可以是一個非常細節(jié)的問題,例如:“按下鍵盤上的A,到屏幕上顯示出字符A 的內部原理”。當你有了來自內心深處經過獨立思考的疑問后,閱讀對你來說是一種享受,一種樂趣。來自內心的疑問,經過不斷的綜合分析,縝密的推理,堅持不懈的查閱和求索,之后撥開迷霧見天日喜悅只有經過才能體會。雖然本書是一本很厚的書,但是這不是畏懼的理由,也不要因為它厚,就給自己下一個決心,制定一個閱讀計劃,幾個月要讀完本書。學習是主動探求的過程,而不是被動接受,在這個過程中,有太多的東西,不是誰可以計劃出來的。例如:在筆者學習內核之初,看到大量的傳言,讀完《Understanding the linux Kernel》,讀完《Linux 內核情景分析》... 就可以成為“高手”了。于是筆者常常捧著厚厚的書,尋思著自己什么時候可以讀完,然而有時好幾天也前進不了幾頁,免不了感慨自己今生將與“高手”無緣,但是又心有不甘,于是囫圇吞棗的“快速”前進,但是越前進,就越感覺到艱難。“欲速則不達”這個道理人人都懂,但是在切身體會之前,人人都會犯這個錯誤。在經歷了很長一段曲折和郁悶之后,筆者擺脫了“書”的束縛,完全以自己的疑問為中心,例如在讀到中斷處理時,由于知識不夠全面,于是丟開內核的書籍,閱讀了大量的計算機體系結構方面的資料,同樣計算機體系結構的書籍也很厚,但是我也沒有想過要把它們讀完,這時只撿中斷相關的讀,之后再來讀內核的書籍,發(fā)現(xiàn)自己原理懂了,但是具體到理解代碼時,就迷糊了,于是有補充GCC 內嵌匯編,C 代碼編譯到匯編代碼的相關知識,反復試驗等等。這個過程很慢,但是積累到最后,筆者發(fā)現(xiàn)自己讀的非常快,甚至可以不讀了,因為很多地方,只要讀到前面的,就領悟了作者后面想要說什么了?! ≈两?,我仍然沒有完成當初為了成為“高手”而制定下的“宏偉”目標,因為我沒有完整的讀完《Understanding the Linux Kernel》、《Linux 內核情景分析》或《Linux 內核完全剖析》等等這類傳說中“驚世駭俗”之作中的任何一本。但是筆者卻從這些著作中受益菲淺?! ‖F(xiàn)在,你應該知道要如何使用本書了吧?那就是不要拘泥如任何教條。雖然本書經筆者從初學到現(xiàn)在的心得體會以及相關筆記和資料整理而成,初學者的大量疑問都能在本書本書中找到答案。但是每個人都是獨一無二的,筆者希望任何一個讀者能綜合利用本書和其它相關資料尋找你自己的答案。多問一點為什么,多一點假設,多一點思考,多一點推理,多一點試驗,多一點堅持。最后,你會感慨原來傳說中的任何“秘籍”都是“浪得虛名”,因為讀完它,你不一定能成為“高手”,而“高手”卻不需要讀完它。能否成為“高手”的決定性因素取決于你的學習方法和學習態(tài)度,而好的“秘籍”僅僅只是催化劑。

內容概要

  《獨辟蹊徑品內核:Linux內核源代碼導讀》根據最新的2.6.24內核為基礎。在講述方式上,《獨辟蹊徑品內核:Linux內核源代碼導讀》注重實例分析,盡量在討論“如何做”的基礎上,深入討論為什么要這么做,從而實現(xiàn)《獨辟蹊徑品內核:Linux內核源代碼導讀》的寫作宗旨:“授人以漁”。在內容安排上,《獨辟蹊徑品內核:Linux內核源代碼導讀》包含以下章節(jié)x86硬件基礎;基礎知識;Linux內核Makefile分析;Linux內核啟動;內存管理;中斷和異常處理;系統(tǒng)調用;信號機制在類UNIX系統(tǒng)中;時鐘機制;進程管理;調度器;文件系統(tǒng);常用內核分析方法?!  丢毐脔鑿狡穬群耍篖inux內核源代碼導讀》適合初、中級Linux用戶、從事內核相關開發(fā)的從業(yè)人員,也可以作為各類院校相關專業(yè)的教材及Linux培訓班的教材,也可作為Linux內核學習的專業(yè)參考書。

作者簡介

  李云華,是一名內核技術的狂熱愛好者,長期從事操作系統(tǒng)內核、計算機網絡、設備驅動程序、以及嵌入系統(tǒng)方面的開發(fā)和研究。擁有豐富的設備驅動開發(fā)、網絡優(yōu)化、內核及驅動移植、嵌入式系統(tǒng)構建等方面的開發(fā)經驗。對Windows內核驅動及Linux內核驅動均有豐富的開發(fā)經驗及心得體會。

書籍目錄

第1章 x86硬件基礎11.1 保護模式11.1.1 分頁機制11.1.2 分段機制71.2 系統(tǒng)門131.3 x86的寄存器141.4 典型的PC系統(tǒng)結構簡介16第2章 基礎知識182.1 AT&T與Intel匯編語法比較182.2 gcc內嵌匯編202.3 同步與互斥252.3.1 原子操作252.3.2 信號量272.3.3 自旋鎖292.3.4 RCU機制352.3.5 percpu變量392.4 內存屏障412.4.1 編譯器引起的內存屏障412.4.2 緩存引起的內存屏障442.4.3 亂序執(zhí)行引起的內存屏障472.5 高級語言的函數調用規(guī)范49第3章 Linux內核Makefile分析523.1 Linux內核編譯概述523.2 內核編譯過程分析543.3 內核鏈接腳本分析62第4章 Linux內核啟動654.1 BIOS啟動階段654.2 實模式setup階段674.3 保護模式startup_32774.4 內核啟動start_kernel()844.5 內核啟動時的參數傳遞904.5.1 內核參數處理914.5.2 模塊參數處理95第5章 內存管理995.1 內存地址空間995.1.1 物理內存地址空間995.1.2 虛擬地址空間1015.2 內存管理的基本數據結構1045.2.1 物理內存頁面描述符1045.2.2 內存管理區(qū)1065.2.3 非一致性內存管理1085.3 內存管理初始化1095.3.1 bootmemalloctor的初始化1095.3.2 頁表初始化1155.3.3 內存管理結構的初始化1185.4 內存的分配與回收1275.4.1 伙伴算法1275.4.2 SLUB分配器138第6章 中斷與異常處理1526.1 中斷的分類1526.2 中斷的初始化1566.2.1 異常初始化1566.2.2 中斷的初始化1606.2.3 中斷請求服務隊列的初始化1676.3 中斷與異常處理1716.3.1 特權轉換與堆棧變化1716.3.2 中斷處理1726.3.3 異常處理1776.4 軟件中斷與延遲函數1806.4.1 softirq1806.4.2 tasklet1856.5 中斷與異常返回1876.6 中斷優(yōu)先級回顧1916.7 關于高級可編程中斷控制器1926.7.1 APIC初始化193第7章 信號機制1997.1 信號機制的管理結構2007.2 信號發(fā)送2047.3 信號處理210第8章 系統(tǒng)調用2208.1 Libc和系統(tǒng)調用220第9章 時鐘機制2269.1 clocksource對象2279.1.1 clocksource概述2279.1.2 clocksource初始化2289.2 tickless機制2329.2.1 tickless由來2329.2.2 clockeventdevice對象概述2349.2.3 clockeventdevice對象的初始化2369.3 High-ResolutionTimers2479.3.1 High-ResolutionTimers管理結構2479.3.2 High-ResolutionTimers初始化2529.3.3 High-ResolutionTimers操作2589.4 時鐘中斷處理2689.4.1 時鐘維護2769.4.2 進程時間信息統(tǒng)計2819.5 軟件定時器2839.5.1 基本管理結構2839.5.2 初始化2849.5.3 注冊與過期處理287第10章 進程管理29510.1 進程描述符29610.1.1 進程狀態(tài)29710.1.2 進程標識29910.1.3 進程的親緣關系30010.1.4 進程的內核態(tài)堆棧30110.1.5 進程的虛擬內存布局30210.1.6 進程的文件信息30510.2 進程的建立30610.2.1 建立子進程的task_struct對象30810.2.2 子進程的內存區(qū)域31510.2.3 子進程的內核態(tài)堆棧32310.2.4 0號進程的建立32510.3 進程切換32710.4 進程的退出33110.4.1 do_exit函數33110.4.2 task_struct結構的刪除33410.4.3 通知父進程33510.5 do_wait()函數33810.6 程序的加載344第11章 調度器35111.1 早期的調度器35111.2 CFS調度器的虛擬時鐘35311.3 CFS調度器的基本管理結構35711.4 CFS調度器對象35911.5 CFS調度操作36011.5.1 update_curr()函數36011.5.2 scheduler_tick()函數36211.5.3 put_prev_task_fair()函數36411.5.4 pick_next_task()函數36611.5.5 等待和喚醒操作36811.5.6 nice系統(tǒng)調用373第12章 文件系統(tǒng)37612.1 Ext2的磁盤結構37612.2 Ext2的內存結構38512.3 虛擬文件系統(tǒng)的管理結構38712.3.1 文件系統(tǒng)對象38812.3.2 VFS的超級塊38912.3.3 VFS的inode結構40012.3.4 VFS的文件對象40612.3.5 VFS的目錄對象40912.3.6 VFS在進程中的文件結構41212.4 文件系統(tǒng)的掛載41312.5 路徑定位42512.6 文件打開與關閉44112.7 文件讀寫44912.7.1 緩沖區(qū)管理44912.7.2 文件讀寫操作分析456第13章 常用內核分析方法47113.1 準確定位同名宏及結構體47113.2 準確定位同名函數47313.3 利用linkmap文件定位全局變量47413.4 準確定位函數調用線索47613.5 SystemTap在代碼分析中的使用479

章節(jié)摘錄

  第1章 x86硬件基礎  如果你是一個Linux內核初學者,你一定常常遇到:保護模式,分段機制,分頁機制,段地址,線性地址,中斷門,調用門,局部描述符,全局描述符,等等這樣的名詞。這些概念常常把初學者弄得“云里來,霧里去”。你會常常感慨,Intel為什么要設計這么復雜的概念呢?僅僅是一個地址機制,就常常讓初學者打開書本,發(fā)現(xiàn)自己會算了,可是一旦關上書本,換個例子,又迷惑了?! ∈聦嵣线@些機制的背后都有它的緣由,任何一個復雜的設計都是由一個簡單的設計發(fā)展起來的,當簡單的設計滿足不了實際需要時,就會一步一步地革新,直到問題被圓滿地解決。因此理解一個“復雜”的東西的最好方式不是去記住它,而是要從最簡單的地方入手,一步一步地推敲,簡單的設計在現(xiàn)實中會遇到什么問題?又該如何解決這個問題?再聯(lián)系到你現(xiàn)在要理解的復雜的例子,慢慢地建立起一條完整的線索,這樣知識不會出現(xiàn)斷層,你也不需要一次跨越一個鴻溝,一切都水到渠成。例如:在學習保護模式中的地址機制時,你一定會感覺為什么要這么復雜呢?實模式不是很簡單嗎?既然實模式簡單,那么不妨想想,如果使用實模式會遇到什么問題呢?把這些問題都一一列出來,再結合現(xiàn)有機制,你就會很自然的理解為什么需要這么做了。本章將試圖讓讀者看到這些概念背后的“為什么”?! ?.1 保護模式  1.1.1 分頁機制  內存按字節(jié)編址,每個地址對應一個字節(jié)的存儲單元,早期的程序直接使用物理地址。在單任務操作系統(tǒng)時代,物理內存被劃分為兩部分,一部分地址空間由操作系統(tǒng)使用,另外一部分由應用程序使用。到了多任務時代,由于程序中的全局變量,起始加載地址是在鏈接期決定的。如果直接使用物理地址,則很可能有多個起始地址一致的應用程序需要同時被加載運行,這就需要把沖突的程序加載到另外的地址上去,然后重新修正程序中的所有相關的全局符號的地址。然而在早期的計算機系統(tǒng)上內存容量十分有限,即便是通過重定位解決了加載地址沖突的問題,由于內存大小的限制,能夠同時加載運行的程序仍然十分有限。而在多任務系統(tǒng)上,某些進程在部分時間內處于等待狀態(tài),于是人們很自然地想到,當內存不夠的時候把處于等待狀態(tài)的進程換入磁盤,騰出一些內存空間來加載新程序。這又帶來了新的問題:每次騰出來的空間地址可不是固定不變的,這就意味著把磁盤上的內容加載進來的時候,又要重新修正程序中的相關地址,在每次換入換出的過程中要不斷地修正相關程序的地址。而且還有一個更為嚴重的問題:假設進程A出現(xiàn)一個錯誤,對某個物理地址進行了寫入操作,恰好這個地址又屬于進程B,當進程B被調度運行的時候,必然會出現(xiàn)錯誤。很難想象一個軟件產品的BuG卻導致用戶抱怨另外一個軟件產品。于是虛擬內存技術發(fā)展起來。在虛擬內存中,程序代碼中訪問的不再是物理地址,而是虛擬地址?! ∫?2位系統(tǒng)為例,每一個進程有4GB的虛擬地址空間,每個進程中有一個表,它記錄著每個虛擬地址對應的物理地址是多少,這樣當程序加載的時候,可以先分配好物理內存,然后把物理內存的地址填入這個表里面,這樣進程之間互不影響。假設程序A和B都是要求在地址BASE處加載(程序中使用的都是虛擬地址。),由于每個進程都有4GB的私有虛擬地址空間,因此兩個進程沒有加載沖突。操作系統(tǒng)分配的物理地址分別是A1和B1,然后A1和B1起始的物理內存地址分別被填入兩個進程虛擬地址映射表中,從而建立虛擬地址和物理地址的一一映射關系。當進程A訪問虛擬地址BAsE+X的時候,由于MMu的硬件支持,硬件自動查找進程A的地址映射表,從而訪問到物理地址為A1+x的內存單元。同理,當進程B訪問虛擬地址為BAsE+x的時候,MMU自動查找進程B的地址映射。表,從而訪問到B1+X的內存單元。當然,實際上的虛擬地址機制比這個復雜得多,但是在對它又了總體認識之后,再來學習一個實際的例子就要簡單得多了。接下來就以32位的x86系統(tǒng)為例,進一步介紹虛擬內存機制?! ∶總€進程擁有4GB的虛擬地址空間,每個字節(jié)的虛擬地址可以通過地址映射表映射到一個字節(jié)的物理地址上面去。因此這個映射表本身必然要占據很大的內存空間,如何設計映射表成為問題的關鍵。如果在虛擬地址映射表中為每一個字節(jié)建立映射關系,那么映射4GB的虛擬地址需要230×4B(32位系統(tǒng)地址為4Byte)的內存??梢姾唵蔚囊灰惶畋碛成涫遣荒軡M足現(xiàn)實要求的。為了要減少虛擬地址映射表項占用的內存空問,所有操作系統(tǒng)都采用了頁式管理。把物理內存劃分為4KB,8KB或者16KB大小的頁,這樣每個頁面在虛擬地址映射表中僅僅占用4Byte的內存。以4KB的頁大小為例,4GB的虛擬地址空間有220個頁面,那么映射4GB空間的映射表僅僅需要220×4B(32系統(tǒng)地址為4Bvte)的內存?! ∑溆成湓砣鐖D1.1所示:程序要訪問的地址是0x12345A10,cPu中的MMu首先找到這個進程的虛擬地址映射表,其起始物理地址為0x10000000。在4KB頁大小的情況下,4GB虛擬地址空間含有220個頁面,只需要20位就可以表示220的大小了,所以虛擬地址的高20位0x12345作為虛地址映射表中的索引,在32位系統(tǒng)上虛地址映射表中的每一項是4個字節(jié),所以MMu根據地址0X10000000+0X123454取得虛擬地址0x12345A10對應的物理頁面起始地址為0x54321000,該地址的低12位總是為0,這是由于每一個4KB.大小的物理頁面總是在4KB的邊界上對齊的。而虛地址Oxl2345A10中的低12位被用伽頁內偏移量,最終虛地址0x12345A10對應的物理地址為0x54321000+A10,而CPU訪問到的內容是0x12345678?! ∮捎陧摯笮?KB,虛地址表中的表項低12位總是為0,因此可以把低12位用來做標識位。例如把第0位用做存在位,當第0位為1時表示該頁面在物理內存中,反之表示該頁面不在物理內存中。假設一個進程要占用10MB的內存空間,在進程初始化的時候,虛地址映射表初始化為0,在內存不足的情況下,系統(tǒng)只分配了5MB的內存,這5MB內存的物理地址被填入到映射表中,同時表項中最低位被設置成1,當進程訪問到另外5MB的虛地址的時候,MMU在查表時發(fā)現(xiàn)最低位為0,于是觸發(fā)一個缺頁中斷,這個時候,系統(tǒng)缺頁中斷處理例程再分配內存頁面,同時更新相應的映射表項,之后程序就可以正常運行了?! ⊥恚€可以把一位劃分出來作為讀寫位,如果對一個地址進行寫入操作,MMu在查表的時候會根據其讀寫位判斷是否允許寫入。幾乎每一個程序員都知道訪問NuLL指針時,一定會出錯,那么操作系統(tǒng)是如何捕捉到這個錯誤的呢?地址0(NULL)實實在在地對應了內存中的一個物理內存地址,如何根據一個地址來判斷指針是不是合法的呢?各個操作系統(tǒng)都保證在一個進程中虛擬地址從0開始的某一段區(qū)域是不映射的,其頁表項為0。例如windows中把0~64K的地址區(qū)域劃分到NULL指針區(qū)而不被映射。因此訪問這部分地址的時候必然會觸發(fā)缺頁中斷,這個時候操作系統(tǒng)就可以判斷出這個地址是否落在NuLL指針區(qū)內。否則無論像malloc這一類的函數返回的指針是0還是其他的值,都無法判斷分配成功或是失敗。

編輯推薦

  較新的內核版本:本書使用的內核版本為2.6.24?! —毺氐膶懽魇址ǎ罕緯谟懻摗癏ow”的基礎上,力求進一步探究“Why”?!  笆谌艘詽O”的寫作宗旨:Linux內核處于飛速發(fā)展中,任何資料都無法覆蓋內核的方方面面。配收以筆者學習過程的疑問和經驗為基礎,融會貫通于各個章節(jié),毫無保留地就如何學習內核,如何分析內核進行了大量且大膽的探討,從而力求體現(xiàn)本書的寫作宗旨——“授人以漁”。

圖書封面

圖書標簽Tags

評論、評分、閱讀與下載


    獨辟蹊徑品內核 PDF格式下載


用戶評論 (總計1條)

 
 

  •     不錯的一本關于內核的書,是作者長期研究的經驗總結。
      不過有些地方貼代碼多了點,占了比較大的篇幅。。。呵呵。
 

250萬本中文圖書簡介、評論、評分,PDF格式免費下載。 第一圖書網 手機版

京ICP備13047387號-7