[轉載]《Linux Kernel Development》讀書筆記 - 蔚藍海岸 - C++博客

《Linux內核情景分析》這本書讀過了一遍,不想繼續讀第二遍了.

    《Linux Kernel Development》這本書前後讀了3遍,寫得實在是好,正所謂"布衣暖,菜根香,好書滋味長".
去書城買來,飽讀之後置於書架之上,扮作有學問之人:) 本想買英文影印版,未能尋到,就買本中文的湊合着看,
看完之後發現有幾處明顯的翻譯錯誤(好在有英文CHM作爲對照).技術類書籍一直都是讀的英文CHM或PDF,這一本是例外.
    《Understanding the Linux Kernel》這本鉅作正在拜讀之中,這次不再去買中文版了,還是直接讀英文原版比較好.             
    下面是在閱讀《Linux Kernel Development》的過程中自認爲值得記錄的部分,五筆輸入的過程中選詞時可能會有輸入錯誤,敬請指正!            

        -------------------------------------------- 華麗的分割線 -----------------------------------------------------

chapter 1
1.當應用程序向內核請求調用一個系統調用時,我們說內核正在代其執行,如果進一步解釋,在這種情況下,應用程序被稱爲通過系統調用在內核空間運行;而內核則被稱爲運行在進程上下文中.
2.硬件與內核的交互:當硬件設備想和系統進行交互時,它首先要向CPU發送一個異步的中斷信號,然後由CPU去打斷內核當前正在執行的工作,中斷通常對應着一箇中斷號,內核通過這個中斷號來查找對應的中斷處理程序,並調用這個找到的中斷處理程序來處理中斷.爲了保證同步,內核可以停用中斷,也就是忽略某個中斷,既可以停止所有的中斷處理程序,也可以有選擇性地停止某些中斷處理程序。許多操作系統的中斷處理程序都不在進程上下文中,而是在一個單獨的與所有的進程都無關的中斷上下文中執行,這樣做是爲了保證中斷處理程序在第一時間響應和處理中斷信號,並快速退出.
3.任一時刻,CPU的活動範圍爲以下三者之一:
 a.運行於內核空間,處理進程上下文,代表某個特定的進程執行.
 b.運行於內核空間,處理中斷上下文,與任何進程無關,處理某個特定的中斷信號.
 c.運行於用戶空間,執行用戶程序.
4.單內核與多內核:操作系統的內核設計分爲兩大陣營:單內核和微內核(以及在科研中的外內核)
 單內核:就是把內核在整體上做爲一個單獨的大過程來實現,並同時運行在一個單獨的地址空間中。因此這樣的內核通常以單個靜態的二進制文件的形式存儲於磁盤,所有內核服務都在這樣一個大的內核空間中運行,內核之間的通信是微不足道的,因爲大家都運行在內核態,並處於同一地址空間,內核可以直接調用函數(其它的內核服務中的功能函數),這與用戶空間沒有什麼區別,這種模式的好處在於:簡單和高性能。
大部分的UNIX和Linux是單內核系統.Linux同時也吸收了微內核的優點:內核模塊化設計,搶佔式內核,支持內核線程,以及動態內核模塊加載和卸載.
 微內核:微內核並不將內核作爲一個單獨的大過程來實現,相反,微內核的功能被劃分爲獨立的過程,每個過程叫做一個服務。理想情況下,只有強烈請求特權服務的服務器運行在特權模式下,其它的服務都運行在用戶空間.不過所有的服務都保持獨立並運行在各自的地址空間,因此就不能像單內核那樣直接調用函數,而是通過消息傳遞處理微內核服務之間的通信:系統採用進程間通信(IPC)機制,各種服務器之間通過IPC機制互通消息,互換服務.服務器的各自獨立有效地避免了一個服務器的失效禍及另一個.Windows和Max OS X 都是微內核.Windows NT內核和Mac OS 的內核都將所有的內核服務程序運行於內核特權模式下,這一點違背了微內核的設計思想,但是減少了內核服務之間的通信之間的消息機制產生的開銷.
5.Linux內核並不區分進程和線程,對於內核來說,只有進程,而且所有的進程都一樣,只不過是有的進程共享一些資源而已.
6.Linux內核的版本號:x.y.z
 x:是主版本號;y:從版本號;z:修訂版本號
 z:如果爲偶數,那麼它是一個穩定的版本;如果爲奇數,那麼它是一個開發版.
 x.y 用於描述內核系列.


chapter 2
7.內核源碼文件結構:
 arch:    特定體系結構的源碼
 crypto:   Crypto API
 Documentation: 內核源碼文檔
 drivers:  設備驅動程序
 fs:    VFS和各種文件系統
 include:   內核頭文件
 init:   內核引導和初始化
 ipc:   進程間通信代碼
 kernel:   內核核心子系統
 lib:   通用內核函數
 mm:    內存管理子系統和VM
 net:   網絡子系統
 scripts:   編譯內核所用的腳本
 secrity:  Linux安全模塊
 sound:   語音子系統
 usr:   早期用戶空間代碼(所謂的initramfs)

8.內核中的內核空間都不分頁,所以,如果內核空間使用了一個字節的內存,那麼實際的可用的物理內存就少了一個字節。
9.在內核中沒有內存保護機制.
10.不要輕易在內核中使用浮點數.在用戶空間進行浮點數操作時,內核會完成從整數操作到浮點數操作的模式轉換,在執行浮點數操作時到底會做些什麼,因體系結構的不同,內核的選擇也會不同,但是內核通常捕獲陷阱並做相應的處理.和用戶空間進程不同,內核並不能完美支持浮點操作,因爲它本身不能陷入.在內核使用浮點數時,除了要人工保存和恢復浮點計數器,還有其它的一些瑣碎的事情要做.所以:不要在內核中使用浮點數.
11.內核開發中,不能使用內核源代碼之外的其它的外部庫文件.
12.內核中沒有printf函數,但是有printk函數可以用於打印調試信息.
13.內核的棧空間很小:內核棧的準確大小隨體系結構而變.在X86系統中,棧的大小可以在編譯時配置,可以是4KB,也可以是8KB.從歷史上說,內核棧的大小是兩頁,這也就意味着,在32位系統上內核棧是8K,在64位系統上,內核棧是16K,這是固定不變的,每個處理器都有自己的棧.
14.硬件中斷是異步到來的,由CPU發送給內核,完全不顧及內核當前的操作.
15.Linux內核中常用的用於解決併發產生的競爭的辦法是:自旋鎖和信號量.

chapter 3
15.進程:就是包含各種資源的處於執行期的程序.
16.線程:進程中的活動對象.每個線程都有一個獨立的程序計數器,進程棧和一組進程寄存器.
17.內核調度的是線程而不是進程.
18.Linux中,進程與線程並不特別進行區分,對於內核而言,線程只不過是一種特殊的進程而已.
19.進程的5種狀態:
 a. TASK_RUNNING(運行狀態)--進程是可以執行的,它或者正在執行,或者在運行隊列中等待執行,這是進程在用戶空間中執行的唯一狀態,也可以應用到內核空間中正在執行的進程.
 b. TASK_INTERRUPTIBLE(可中斷)--進程正在睡眠(也就是說它被阻塞),等待某些條件的達成.一旦這些條件達成,內核就會把進程設置爲運行,處於此狀態的進程也會因爲收到信號而提前被喚醒而投入運行.
 c. TASK_UNINTERRUPTIBLE(不可中斷)--除了不會因爲接收到信號而被喚醒而投入運行之外,這個狀態與TASK_INTERRUPTIBLE(可中斷)狀態相同.這個狀態通常在進程正在等待地不受干擾或等待事件很快就會發生時,由於此狀態不對信號進行響應,所以,較之TASK_INTERRUPTIBLE(可中斷)使用得比較少.
 d. TASK_ZOMBIE(僵死)--該進程已經結束,但是父進程還沒有調用wait4()系統調用,爲了父進程能夠獲知它的消息,子進程的進程描述符仍然被保留着。一旦父進程調用了wait4,該僵死的子進程的進程描述符就會被釋放.
 e. TASK_STOPPED(停止)--進程停止執行:進程沒有投入運行,也不能投入運行.通常這種狀態發生在接收到SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU等信號的時候,此外,在調試期間接收到的任何信號,都會使進程進入這種狀態.
20.UNIX創建進程的方式:
 許多其它的操作系統都提供了spawn進程的機制:先在新的地址空間裏創建進程,讀入可執行文件,最後開始執行.
 UNIX採用了與衆不同的機制:把上述步驟分解到兩個單獨的函數中去執行(fork和exec)。首先fork通過拷貝當前的進程創建一個子進程。子進程與父進程的區別僅僅在於PID,PPID和某些資源以及統計量;exec函數負責讀入可執行文件並加載到新創建的子進程的地址空間並開始運行.
21.Linux的fork系統調用採用寫時拷貝(copy-on-write)機制.寫時拷貝是一種推遲甚至是免除數據拷貝的技術.內核並不是複製整個進程地址空間,而是讓父進程和子進程以只讀的方式共用一個拷貝。只在在需要寫入的時候,數據纔會被複制,從而使各個進程擁有各自的拷貝.
22.fork系統調用是通過clone系統調用來實現的.
23.fork系統調用後,系統有意讓子進程先執行。因爲一般子進程會馬上調用exec函數,這樣可以避免寫時拷貝的額外開銷,如果父進程先執行,有可能會開始向地址空間寫入.
24.vfork:vfork系統調用和fork系統調用的功能類似,除了不拷貝父進程的頁表項,子進程作爲父進程的一個單獨的線程執行,父進程被阻塞,直到子進程退出運行,子進程不能向地址空間中寫入。
25.concurrent與parallelism的區別:
 concurrent是併發,如果在單核的系統上,並不是同時多個任何同時運行,只有在多核的系統上,纔可能有多個進程同時並行運行,這纔是真正意義上的併發,也就是並行.
 parallelism是並行,只針對於多核系統而言,指在任意一個時刻,系統上有多個的任務在不同的CPU上同時運行.
26.Linux把所有的線程都當作是進程來處理.線程僅被視作一個與其它的進程共享一些資源的進程
27.Windows和Sun Solaris則是從內核的角度來支持線程.
28.內核線程:獨立運行於內核空間的標準進程,用於執行一些後臺操作.內核線程與用戶線程的區別在於,它們沒有獨立的地址空間,它們只運行於內核空間,從來不會切換到用戶空間去運行.內核線程也可以被調度和搶佔.
29.孤兒進程的處理:在當前進程的進程組內找一個進程作爲父進程,如果不行,那麼就讓init做爲它們的父進程.

chapter 4
30.調度程序:可以看作是在可運行態的進程之間分配有限的處理器時間的內核子系統.
31.多任務系統分爲:非搶佔式和搶佔式多任務系統.
32.搶佔式多任務:由調度程序來決定什麼時候停止一個正在運行的進程而使得其它的進程有可執行的機會.
33.進程時間片:分配給每個可運行進程的處理器時間段.
34.非搶佔式多任務:除非進程自已退出運行,否則它會一直點用處理器.
35.進程分爲:處理器消息耗型和I/O消耗型.
36.優先級高的進程所獲得的時間片也更長,調度程序總是調度時間片未用盡而優先級又最高的進程運行.
37.調度程序會提高I/O消耗進程的優先級,降低處理器消耗進程的優先級.
38.Linux內核提供兩組獨立的優先級範圍:
 a. nice值,從-20到19,nice級越大,優先級越低.
 b. 實時優先級,其值是可以配置的,默認情況下是從0到99,任何實時進程的優先級都高於普通進程.
39.進程時間片:它是一個整數值,表示進程在被搶佔之前可以連續運行的最長的時間.進程的時間片不需要一次性用盡,可以分多次用完,這樣,一個進程可以被調度運行多次,這對於I/O消耗類型的進程非常有利
40.當一個進程的時間片耗盡時,就認爲進程到期了。沒有時間處的進程不會再次被調度運行,要等到其它的所有的進程都耗盡了它們的時間片,也就是說剩餘的時間片爲0,在那個時間,所有的進程的時間片會被重新計算.
50.每個處理器都有一個任務隊列,這個任務隊列裏面有一個“可運行進程優先隊列”和一個“已過期進程優先級隊列”,當一個進程的時間片耗盡時,它會被從“可運行隊列”移動到“已過期隊列”,在移動在過期隊列之前,它的新的時間片會被重新計算好。當“可運行隊列”爲空,也即是當前的CPU上的可執行的進程的時間片都已經耗盡時,這個時候會交換“可運行隊列”和“過期隊列”,這樣“過期隊列”中的所有的“已重新計算好時間片”的進程已可以重新投入運行,這就是Linux內核中O(1)調度程序的核心.
51.每一個CPU都有一個對應的schedule調度函數,用於決定當前的CPU上下一個可以執行的進程.
52.對於內核而言,如果一個進程的睡眠時間比運行時間長,那麼這個進程是I/O消耗型的;如果一個進程的運行時間比睡眠時間長,那麼這個進程是處理器消耗型的.
53.當一個新的子進程創建時,子進程會和父進程均分剩餘的時間片,這樣就可以避免用戶通過不停地創建子進程來不停地攫取時間片.
54.重新計算進程的時間片時,只依據進程的靜態優先級,這個優先級在進程狀態時由用戶指定,一旦指定,這個優先級就不會被改變.優先級越高,進程所獲得的進程運行時間片就越長.
55.內核對進程“休眠”和“喚醒”的處理:
 當進程“休眠”時,進程將自己標識爲“休眠”狀態,把自己從“可執行隊列”移動到“等待隊列”。
 當進程“喚醒”時,進程被設置爲“可執行狀態”,然後被從“等待隊列”移動到“可執行隊列”.
56.進程的用戶搶佔:是指進程在內核空間返回到用戶空間時或是從中斷處理程序中返回到用戶空間時,如果進程的need_resched標誌被重新標記了,那麼進程就需要被重新調度.
57.在Linux系統中,內核進程也可以被搶佔.只要沒有鎖,內核就可以進行搶佔.
 內核搶佔發生的時間:
 a. 當從中斷處理程序正在執行,且返回內核空間之前.
 b. 當內核代碼再一次具有可搶佔性的時候.
 c. 如果內核中的任務顯示地高度schedule。
 d. 如果內核中的任務阻塞(這同樣也會導致調用schedule).


chapter 5
58.系統調用的作用:
 a. 爲用戶空間提供了一種硬件的抽象接口.
 b. 系統調用保證了系統的穩定和安全.
 c. 系統調用是用戶空間訪問內核的唯一手段.
59.UNIX的設計原則是:提供機制而不是策略.換句話說,UNIX系統抽象出了用於完成某種確定目的的函數,至於這些函數怎麼用完全不需要內核去關心.
60.系統調用的實現原理:
 用戶空間無法直接執行內核代碼,它們不能直接調用內核空間中的函數,因爲內核駐留在受保護的地址空間上,如果進程可以直接在內核地址空間上讀寫的話,系統安全就會失敗控制.
 所以,應用程序應該以某種方式通知系統,告訴內核自己需要執行一個系統調用,希望系統切換到內核態,這樣內核就可以代表應用程序來執行該系統調用了.
 通知內核的機制是靠軟中斷:通過引發一個異常來促使系統切換到內核態去執行異常處理程序。此時的異常處理程序實際上就是系統調用處理程序。X86系統上的軟中斷由int 0x80指令產生.這條指令觸發一個異常導致系統切換到內核態並執行第128號異常處理程序,而該程序是系統調用處理程序.這個異常處理程序的名字叫作system_call。最近x86增加了一條叫做sysenter的指令,與int中斷指令相比,這條指令提供了更快,更專業的陷入內核執行系統調用的方式.
61.當內核接收一個用戶空間的指針時,內核 必須保證:
 a. 指針指向的內存區域屬於用戶空間,進程決不能哄騙內核去讀取內核空間的數據.
 b. 指針指向的內存區域在進程的地址空間裏,進程決不能哄騙內核去讀取其它的進程空間的數據.
 c. 如果是讀,該內核區域必須被標記爲可讀,如果是寫,該內存區域必須被標記爲可寫。進程決不能繞過內存訪問控制.
62.硬件與內核通信的機制是:中斷機制,當硬件需要和系統通信時,硬件向系統發送一箇中斷請求,中斷在本質上是一種特殊的電信號。這個中斷請求實際上並不是硬件直接發送給內核的,而是硬件先發送給CPU,再由CPU向內核發送中斷信號,內核再中斷當前的工作調用相應的中斷處理程序,從而完成內核與硬件的通信.硬件的中斷可以隨時產生,因此,處理器也需要隨時響應中斷信號.硬件中斷不用考慮與處理器的時鐘同步問題,但是“異常”需要考慮與處理器的時鐘同步問題.
63.每個中斷信號都有一個唯一的數值.這樣內核才能區分中斷信號來自於哪個硬件,更進一步,內核才能爲對應的硬件中斷信號調用其中斷處理程序.
64.中斷信號的值被稱爲“中斷請求線”(IRQ).
65."異常"必須考慮與處理器的時鐘同步問題,因此“異常”也被稱作爲“同步中斷”.在處理器遇到編程失誤或是特殊情況而需要由內核來處理的時候,處理器就產生一個“異常”,因爲許多體系結構處理“異常”與處理“中斷”的方式很相似,所以,內核對於它們的處理也很相似.
66.“中斷”必須是由硬件產生的,而“異常”可以是由軟件產生的.
67.在響應一箇中斷的時候,內核會執行一個函數,這個函數叫做“中斷處理程序”(interrupt handler)或者是“中斷服務例程”(interrupt service routine,ISR)。中斷處理程序是和特定的中斷關聯,而不是和硬件關聯的,這樣一來,如果一個硬件可以產生多種中斷,那麼它的設備驅動程序就要提供多箇中斷處理程序.
68.中斷處理程序與其它的內核函數的區別在於:中斷處理程序是被內核用來響應中斷的,而且它們只運行在我們稱之爲“中斷上下文”的特殊上下文中.
69.中斷處理程序一般被切分爲兩部分完成:上半部和下半部.
 a. 上半部用於完成有嚴格的時限的工作.
 b. 中斷處理程序中能夠被推遲到稍後完成的工作就被放到下半部中.
70.設備驅動程序:實際上就是對設備所產生的中斷進行處理的中斷處理程序的集合,所有的這個硬件設備的中斷處理程序被一起提供給內核用於內核完成該硬件設備的中斷處理.
71.中斷共享:中斷共享的函數就是“一箇中斷信號值可以由多個硬件產生”.
72.如果某個中斷信號被屏蔽,那麼當硬件設備向處理器發送對應的中斷電信號時,在處理器將這個中斷通知內核時,內核就不會去響應這個中斷信號.
73.Linux中的中斷處理程序是無需重入的.當一個給定的中斷處理程序正在執行時,這個中斷在所有的處理器上都會被屏蔽掉,以防止在同一個中斷線上接收另一個新的同樣的中斷信號.通常情況下,所有的其它的中斷都沒有被屏蔽而是打開的,所有這些不同的中斷線上的其它中斷都能夠被響應,但當前的中斷線總是被禁止的.由此可以看出,同一個中斷處理程序絕對不會被同時調用以處理嵌套的中斷,這極大地簡化了中斷處理程序的編寫.
73.內核接收到中斷後,會依次調用當前的中斷線上註冊的每一箇中斷處理程序.
74.進程上下文是針對內核而言的,它是指內核所處的操作模式,此時內核代表進程執行.
 中斷上下文.中斷上下文與進程沒有瓜葛,因爲沒有進程背景,所有中斷上下文不可以睡眠--否則又怎能被重新調度呢?因此不能從中斷上下文中調用某些函數。如果一個函數睡眠,就不能在中斷處理程序中調用它,這是對什麼表函數可以在中斷上下文中調用的限制.
 中斷上下文有嚴格的時間限制,因爲它打斷的其它的代碼的執行.中斷上下文中的代碼應當快速簡潔,儘量不要使用循環去處理複製的工作,有一點非常重要,請永遠牢記:中斷處理程序打斷了其它的代碼(甚至可能是其它的中斷線上的另一箇中斷處理程序).
75.中斷處理機制的實現.
 中斷處理系統在Linux系統中的實現是依賴於體系結構的,想必你對此不會感到特別驚訝。實現依賴於處理器,所使用的中斷處理器的類型,體系結構的設計及機器本身.
 設備產生中斷,通過中斷控制總線把電信號發送給中斷控制器,如果中斷線是激活的(它們允許被屏蔽),那麼中斷控制器就會把中斷髮往處理器.在大多數的體系結構上,這個工作就是通過電信號給處理器的特定的管腳發送一個電信號。除非在處理器上禁止該中斷,否則,處理器就會停止它正在做的事,關閉中斷系統,然後跑到內存中預定義的位置開始執行那裏的代碼。這個預定義的位置是由內核設置的,是中斷處理程序的入口.
 在內核中,中斷的旅程開始於預定義的入口點,這類似於系統調用通過預定義的異常句柄進入內核,對於每條中斷線,處理器都會跳到一個唯一的位置,這樣,內核就可以知道所接收的中斷的IRQ號了,初始入口只是在棧中保存這個號,並存放當前的寄存器的值,然後內核調用do_IRQ.從這裏開始,大多數的中斷處理代碼都是用C寫的.
76.Linux內核提供了一組接口用於操作機器上的中斷狀態,這些接口爲我們提供了能夠禁止當前處理器的中斷系統,或屏蔽掉整個機器的一條中斷線的能力.


chapter 7
77.中斷處理流程分爲:上半部和下半部兩部分.
 上半部:中斷處理程序.
 下半部:Linux中將響應中斷的一部分操作推遲到之後完成的機制.
78.上半部和下半部的區分取決於開發者自己的判斷,通常的規則是:
 a. 如果一個任務對時間非常敏感,將其入在中斷處理程序中執行.
 b. 如果一個任務和硬件相關,將其入在中斷處理程序中執行.
 c. 如果一個任務要保證不被其它的中斷(特別是相同的中斷)打斷,將其入在中斷處理程序中執行.
 d. 其它的任務,考慮放置在下半部中執行.
79. Linux內核提供的3種下半部機制:
 a. 軟中斷:是在編譯時指定的softirq_action結構體數組,一般有32項.一個軟中斷不會去搶佔另一個軟中斷,實際上,唯一可以搶佔軟中斷的是中斷處理程序,不過其它的軟中斷,甚至是其它的相同類型的軟中斷,可以在其它的處理器上同時執行.
執行軟中斷:一個註冊的軟中斷必須在被標記爲纔會執行,這被稱作“觸發軟中斷”,通常中斷處理程序會在返回前標記它的軟中斷,使其在稍後執行.在以下時刻,軟中斷會被檢查和執行:
 a). 從一個硬件中斷代碼返回時.
 b). 在ksoftirqd內核線程中.
 c). 在那些顯示檢查和執行待處理的軟中斷的代碼中,如網絡子系統中.
內核定時器和tasklet都是建立在軟中斷的基礎之上的.
 b. tasklet:基於軟中斷實現.
 c. 工作隊列:可以把中斷處理程序中的後續工作推遲到後續去完成,交由一個內核線程去執行,這個下半部在“進程上下文”中執行.工作隊列允許重新調度甚至是睡眠.
 如果推遲的工作可以需要睡眠,那麼使用任務隊列;如果推遲的工作不能進行睡眠,那麼使用軟中斷或tasklet.工作隊列最基本的表現形式就成了把需要推遲執行的任務交給一個特定的通用線程來處理這樣的一個接口。默認的工作者線程叫做:events/n。這裏的n是處理器的編號;每個處理器一個對應的線程.比如,用於處理工作隊列的工作者線程可以自行創建,但是有一個默認的工作者線程.


80.當你需要保證工作被推遲到某一個指定的時間去執行時,那麼你需要使用內核定時器機制.
81.每個處理器都有一個對應的“軟中斷輔助處理線程”:ksoftirqd/n。
82.臨界區:就是訪問和操作共享數據的代碼斷.代碼在執行完成之前不能夠被打斷.
83.處理器會保證任何的兩個處理器原子指令不會同時執行.也就是說:如果有兩個原子A,B指令要執行,那麼要麼A執行完之後再執行B,要麼B執行完後再執行A,不可以出現A執行一半再執行B然後再執行A,最後又執行B的情況.
84.內核中有可能造成併發執行原因:
 a. 中斷--中斷幾乎可以在任何時刻發生,也就是可能打斷當前正在執行的代碼.
 b. 軟中斷和tasklet--內核能在任何時刻喚醒或調度軟中斷和tasklet,打斷當前正在執行的代碼.
 c. 內核搶佔--因爲內核具有搶佔性,所以內核中的任務可能會另外一個任務搶佔.
 d. 睡眠及與用戶空間的同步--在內核執行的進程可能睡眠,這就會喚醒調度程序,從而導致調度一個新的用戶進程執行.
85.對稱多處理器--兩個或多個處理器可以同時執行代碼.以及如何安排同步的順序.
86.併發開發的難點就在於找出所有的潛在的可能發生競爭和數據同步的地方.
87.幾種安全代碼:
 a. 中斷安全代碼:在中斷處理程序中能避免被併發訪問的代碼.
 b. 對稱多處理器安全代碼:在對稱多處理器中能避免被併發訪問的代碼.
 c. 搶佔安全代碼:在內核搶佔時能避免併發訪問的代碼.
88.在併發開發中:我們實際上要保護的是數據而不是代碼.
89.線程中的局部數據不需要加鎖,因爲線程上的局部數據是存儲在線程的棧空間的中的,特定於每一個線程的棧空間中都有一份自己的線程局部數據,它們互不影響.一條原則時,如果公有數據可能同時被多個線程訪問,那麼這些公有數據需要被訪問.
90.避免死鎖的方法:
 a.操作公共數據的線程都以相同的順序去獲取鎖.
 b. 對鎖的獲取和釋放加上序號.
 c. 不要在一個線程中重複請求同一個鎖.
 d. 以獲取鎖的相反順序來釋放鎖.
 e. 加鎖的精髓在於力求簡單的方案.
91.原子操作:執行過程不會被打斷的操作.
92.讀寫內存中的一個字的操作是原子操作,也就是說,在對這個內存字的讀的過程中,不會出現對該字的寫的過程,在對該字的寫的過程中,不會出現對該字的讀的過程.
93.原子性:確保指令執行期間不會被其它的操作打斷,也就是說這個指令一次性完成,在這條指令完成的過程中,不會有其它的指令執行.
94.順序性:兩條或多條指令出現在獨立的執行線程中,甚至獨立的處理器上,但它們本該執行的順序依然要得到保持.
95.自旋鎖爲多處理器機器上提供了防止併發訪問的數據所需要的鎖保護機制.
96.Linux下的自旋鎖是不可遞歸的.
97.當一個線程在試圖獲取自旋鎖時,如果這個鎖已被其它的線程所獲取,那麼這個等待的線程不會因爲等待這個自旋鎖而休眠,相反,這個等待的線程會一直嘗試去獲取這個自旋鎖。
98.自旋鎖可以使用在中斷處理程序中(此處不能使用信號量,因爲它們會導致睡眠),在中斷程序中調用 自旋鎖時,一定要在獲取鎖之前首先禁用本地中斷(在當前的處理器上的中斷請求),否則,中斷處理程序可能會打斷正持有鎖的內核代碼,有可能會試圖爭用這個已經被持有的自旋鎖,這樣一來,中斷處理程序就會自旋,等待該鎖重新可用,但是鎖的持有者在這個中斷處理過程處理完成之前不可能運行,這正是我們在前一章節中提到的“雙重請求死鎖”。注意,需要關閉的只是當前的處理器上的中斷,如果中斷髮生在不同的機器上,即使中斷處理程序在同一鎖上自旋,也不會妨礙鎖的持有者(在不同的處理器上)最終釋放鎖.
99.要記住一點:在任何時候,我們加鎖是爲了鎖住數據,而不是爲了鎖住代碼.
100.對於自旋鎖和下半部:
 在與下半部配合使用時,必須小心地使用鎖機制。函數spin_lock_bh用於獲取指定的鎖,同時它們禁止所有的下半部的執行,相應的spin_unlock_bh函數執行相反的操作.
 由於下半部可以搶佔進程上下文中的代碼,所以,當下半部與進程上下文共享數據時,必須對進程上下文中的共享數據進行保護,所以需要加鎖的同時還要禁止下半部執行(不然下半部又可以會搶佔進程上下文)。同樣,由於中斷處理程序可以搶佔下半部,所以,如果中斷處理程序和下半部共享數據,那麼就必須在獲取恰當的鎖的同時還要禁止中斷.
 同類的tasklet不可能同時運行,所以對於同類的tasklet中的共享數據不需要加鎖保護,因爲同類的tasklet的任何必定是嚴格的串行完成,如果同一個處理器上的同類的tasklet任務A和B,在A沒有完成之前,B絕對不會開始運行,只有在A完全完成之後,B纔開始運行.但是當數據被不同類的tasklet共享時,就需要在訪問下半部中的數據前先獲得一個普通的自旋鎖。這裏不需要禁止下半部,因爲在同一個處理器上,決不會有tasklet相互搶佔的情況發生.
 對於軟中斷,無論是否同種類型,如果數據被軟中斷共享,那麼它必須得到鎖的保護,因爲即使是同種類型的兩個軟中斷也可以同時運行在一個系統的多個處理器上,但是,同一處理器上的一個軟中斷絕不會搶佔另一個軟中斷,因此根本沒有必須搶佔軟中斷.
101.信號量:信號量支持兩個原子操作P和V,前者叫做測試操作,後者叫做增加操作,後來系統把這兩個操作分別叫做down和up。down通過對信號量計數減1來請示獲得一個信號量,如果結果是0或者大於0,那麼獲取鎖成功,進入到臨界區中.如果結果是負數,那麼任務就會被放入到等待隊列,對應的進程也會進入休眠.處理器此時可以執行其它的操作.相反,在臨界區中的操作完成之後,通過up操作來釋放信號量,該操作也被稱作是提升信號量的值,因爲它會增加信號量的計數,如果在該信號量上的等待隊列不爲空,那麼處於隊列中的等待的任務就會被喚醒同時獲得該信號量.
102.內核中的 barrier的作用是保證:一個操作必須在另一個操作之前完成這一點不會被編譯器或者是處理器改變.

chapter 10
103.系統定時器:一種可編程的硬件芯片,它能以固定頻率產生中斷,這種中斷就是所謂的“定時器中斷”.它所對應的中斷處理程序負責更新系統時間,還負責執行需要週期性執行的任務.系統定時器和時鐘中斷處理程序是Linux系統內核管理機制中的中樞.
104.動態定時器: 一種用來推遲執行程序的工具,內核可以動態創建或銷燬動態定時器.
105.系統定時器以某種固定的頻率自動觸發時鐘中斷,這種頻率可以通過編程預定,稱作節拍率,當時鍾中斷髮生時,內核就通過一種特殊的中斷處理程序對其進行處理.因爲預編的節拍率對內核來說是已知的,所以內核知道兩次連接的時鐘中斷間隔的時間,這個間隔時間就被稱作:節拍(tick)。它等於節拍頻率分之一,內核就是靠這種時鐘間隔來計算牆上時間和系統時間.
106.jiffies:記錄自系統啓動以來的時鐘節拍數.
107.實時時鐘:RTC,是用來持久存放系統時間的設備,即使系統關閉後,它也可以靠主板上的微型電池提供的電力保持系統的計時,在PC體系結構中,RTC和CMOS集成在一起,而且RTC的運行和BIOS的保存設置都是通過同一個電池供電的.
108.實時時鐘的最大作用是在啓動時初始化xtime變量.
109.系統定時器:通過對電子晶振進行分頻來實現系統定時器.
110.在X86系統中,主要採用可編程中斷時鐘(PIT)。在X86系統中還包括本地APIC時鐘和時間戳計數(TSC)等時鐘資源.
111.時鐘處理程序:時鐘處理程序可以分爲兩部分,體系結構相關的部分和體系結構無關的部分。
 與體系結構相關的部分作爲系統定時器的中斷處理程序而註冊到內核中,以便產生時鐘中斷時,它能夠相應地運行。
112.動態定時器:它並不週期性地運行,它在超時後就自行銷燬,這也是這種定時器被稱爲動態定時器的原因 ,動態定時器不斷創建和銷燬,而且它的運行次數也不受限制。
113.定時器會在指定的定時值到達之後開始運行.在運行完成之後,這個定時器會被刪除,所以如果你想要一個定時函數週期性地運行下去,那麼你需要在定時器超時後重新設定定時器,也就是在定時器處理函數的最後重新設定這個定時器.
114.volatile變量可以強制使得編譯器在每次訪問變量時都重新從主內存中獲取而不是通過寄存器中的變量的別名來獲取,這樣就可以保證變量的值永遠都是最新的.
115.經驗表明,不要使用udelay來處理超過1毫秒的延遲,在延遲超過1毫秒的情況下,使用mdelay更爲安全.這些函數的實現使用基於循環的忙等待.
116.MIPS:Million Instruction Per Second。處理器每秒執行的百萬條指令數.

chapter 11
117.內存頁:內核把物理內存頁作內存管理的基本單位,儘管處理器的最小可尋址單位通常是字(甚至字節),但是,內存管理單元(MMU:管理內存並把虛擬地址轉換爲物理地址的硬件)通常以頁爲單位來進行處理,正因爲如此,MMU大小爲單位來管理系統中的頁表(這也是頁表名的由來),從物理內存的CPU角度來看,內存的最小單位是字節,但是從虛擬內存的角度來看,頁就是最小單位.
118.在32位機上,一個內存頁的大小是4K,而在64位機上,內存頁的大小爲8K。所以在32位機上,1G的內存會被分成262144個頁.
119.內存區:內核並不是對所有的內存頁都一致對待,內核使用區對具有相似特性的頁進行分組。
 ZONE_DMA:這個內存區的內存頁可以執行DMA(直接內存訪問操作).X86上爲:0 ~ 16M
 ZONE_NORMAL: 這個區包含的正常可尋址的頁.X86上爲: 16M ~896M
 ZNOE_HIGHMEM: 這個區中的頁並不能永久地映射到內核地址空間。X86上爲: > 896M
120.內核slab層:slab層把不同的對象劃分爲所謂的“調整緩存”組,其中每個高速緩存都存放不同類型的對象,每種對象類型對應於一個高速緩存,例如,一個高速緩存用於存放進程描述符,另一個高速緩存用於存放索引節點.slab由一個或多個物理上連續的頁組成.

chapter 12
121.虛擬文件系統:有時也稱作虛擬文件交換(VFS),採用面向對象的設計思路,作爲內核子系統,爲用戶空間提供了系統相關的接口,系統中所有的文件系統不但依賴於VFS共存,而且也依靠VFS系統協同工作。
122.UNIX文件系統:UNIX使用了4種與文件系統相關的傳統抽象概念:
 文件:可以看作是一個有序的字節串,字節串中的每一個字節是文件的頭,最後一個字節是文件的尾。
 目錄項:用來容納文件的結構.
 索引節點:文件相關信息,有時被稱作文件的元數據(也就是說文件的相關的數據),被存儲在一個單獨的數據結構中,這個結構被稱作索引結點( inode ).
 安裝點:安裝文件系統的特定的安裝點.
123.文件系統:從本質上講,文件系統是特殊的數據分層存儲結構,包含文件,目錄和相關的控制信息。在UNIX系統中,文件系統被安裝在一個特定的安裝點上,該安裝點在全局層次結構中被稱作命名空間,所有已安裝的文件系統都作爲根文件系統樹的枝葉出現在系統中.
124.UNIX是面向流的文件系統,其它的操作系統中有面向記錄的文件系統.UNIX將相關信息和文件本身這兩個概念加以區分.
125.VFS中的4種主要對象:
 a. 超級塊 對象:它代表一個已安裝的文件系統.該對象存儲特定文件系統的信息,通常對應於存放在磁盤特定扇區中的文件系統超級塊或文件系統控制塊.
 b. 索引節點 對象:它代表一個文件.包含了內核在操作文件或目錄時所需要的全部的信息.對於UNIX風格的文件系統來說,這些信息可以從磁盤的索引節點中直接讀入.一個索引節點代表文件系統中(雖然索引節點僅當文件被訪問時纔在內存中被創建)的一個文件,它也可以是設備或管道這樣的特殊的文件。
 c. 目錄項 對象:它代表一個目錄項,是路徑的一個組成部分.路徑中的每一個組成部分都由一個索引節點對象表示。雖然它們可以統一由索引節點表示。目錄項對象沒有對應的磁盤數據結構,VFS根據字符串的形式的路徑來現場創建它們。
 d. 文件對象:它代表由進程打開的文件.是打開的物理文件在內存中的表示.同一個物理文件如果被多個進程打開,那麼就會有多個對應的文件對象.文件對象只在內存中存在,並不存儲在磁盤上.
     VFS中將目錄當作一種特殊的文件來看待,所以不存在“目錄對象”

126.目錄項對象的狀態:被使用,未被使用,負狀態.
 a. 被使用狀態:一個使用的目錄項對應於一個有次的索引節點.
 b. 未被使用狀態:未被使用的目錄項對應於一個有效的索引節點,但是應指明,當前的VFS並沒有使用它,該目錄項對象仍然指向一個有效對象,而且被保留在緩存中以便需要時再使用它。由於該目錄項不會過早刪除,所以,在以後需要它時,不必重新創建,從而使用路徑查找更迅速,如果要回收內存的話,可以銷燬未使用的目錄項.
 c. 負狀態:目錄項沒有對應的有效的索引節點,因爲索引節點已被刪除(也即是物理文件被刪除),或路徑不再正確了,但是目錄項仍然保留,以便快速解析以後的路徑查詢。雖然負狀態的目錄項有些用處,但是如果需要的話,可以銷燬它。
127.如果系統中有大量的進程都要打開超過32個文件,爲了優化性能,管理員可以適當增大NR_OPEN_DEFAULT的值.

chapter 13
128.Linux系統中的設置類型分爲“塊設備”和“字符設備”
塊設備:能夠隨機的訪問固定大小數據片的設備,如果磁盤,軟盤驅動器,CD-ROM。它們都是以安裝文件系統的方式使用。
字符設備:字符設備按照字符流的方式被訪問,像串口和鍵盤就屬於字符設備。
這兩種設備的本質區別在於是否可以進行隨機訪問.
129.塊設備的最小的可尋址單元是扇區,扇區大小一般是2的倍數,而最常見的大小是512字節,扇區的大小是設備的物理屬性,扇區是所有的塊設備的基本單元,塊設備無法對比它還小的單元進行尋址和操作,不過許多塊設備能夠一次就傳輸多個扇區,雖然大多數設備的扇區的大小都是512字節,不過其它大小的扇區也是很常見的,比如,CD-ROM的扇區大小就是2K.
130.雖然物理磁盤都是按扇區級進行尋址的,但是內核卻是基於塊的方式來操作磁盤的,所以塊必須是扇區大小的整數倍,而且要小於頁面的大小,所以通常塊的大小是512字節或是4K.
131.爲了優化高度程序,內核會在提交I/O請求到磁盤之前所將這些請求進行“合併與排序”,從而每次I/O請求所消耗的時間.

chapter 14
132.進程的地址空間包括:
a.代碼段:可執行文件代碼的內存映射
b.數據段:可執行文件的已初始化全局變量的內存映射.
c.BSS的零頁:包含未初始化全局變量的內存映射.
d.進程用戶棧:不要和進程的內核棧混淆,進程的內核棧獨立存在並由內核維護。
e.每一個諸如C庫或動態連接程序等共享庫的代碼段,數據段和bss也會被載入進程的地址空間。
f.任何內存映射文件.
g.任何共享內存段.
h.任何匿名的內存映射,比如由malloc分配的內存.
進程地址空間中的任何有效地址都只能位於唯一一個區域,這些內存區域並不能相互覆蓋,可以看到,在執行的地址中,每個不同的內存片段都對應一個獨立的內存區域:棧,對象代碼,全局變量,被映射的文件等等.
133.Linux中線程與進程的唯一區別幾乎是:是否共享地址空間.
134.內核線程沒有進程地址空間,也沒有相關的內存描述符,所以內核線程對應的進程描述符中的MM域爲空,事實上,這也正是內核線程的真實含義--它們沒有用戶上下文。
135.平坦地址空間:描述的是地址空間範圍是一個獨立的連續空間(比如從0擴展到429496729的32位地址空間)。
136.進程的地址空間之間互不相干.兩個不同的進程可以在相同的地址空間上存放相同的數據,但是進程之間也可以共享地址空間,我們稱這樣的進程爲線程.
137.VMA:虛擬內存區域.
138.頁表:雖然應用程序操作的對象是映射到物理內存之上的虛擬內存,但是處理器直接操作的卻是物理內存,所以,當程序訪問一個虛擬地址時,首先必須將虛擬地址轉化爲物理地址,然後處理器才能解析地址訪問請求,地址的轉換工作需要通過查詢頁表來完成,概括地講,地址轉換需要將虛擬地址分段,使每段虛擬地址都作爲一個索引指向頁表,而頁表則指向下一級別的頁表或者指向最終的物理頁面.
139.Linux中使用三級頁表完成地址轉換。得用多級頁表能夠節約地址轉換所需要佔用的空間,但如果利用三級頁錶轉換地址,即使是64位機器,佔用的地址空間也是很有限的,但是如果使用靜態數組來實現頁表,那麼即使是在32位機器上,該數組也將佔用巨大的存放空間。Linux對所有的體系結構,所括對那些不支持三級頁表的體系結構都使用三級頁表進行管理.
140.三級頁表結構:   
   每個進程的地址空間描述符
         |
     頂級頁表(PGD)   --> 頁全局目錄
     |
     二級頁表(PMD)   --> 中間頁目錄 
     |
     三級頁表(PTE)   --> 指向實際的物理內存頁面


chapter 15
141.頁高度緩存:是Linux內核實現的一種主要的磁盤緩存,這主要用來減少對磁盤的IO操作,具體地講,是通過把磁盤中的數據緩存到物理內存中,把對磁盤的訪問變爲對物理內存的訪問。
142.磁盤高速緩存的價值主要存在於兩個方面:
a. 訪問磁盤的速度要遠遠低於訪問內存的速度,因此,從內存訪問數據比從磁盤訪問速度更快。
b. 數據一旦被訪問,就很有可能在短期內再次被訪問到。這種短時期內集中訪問同一片數據的原理被稱作“臨時局部原理(temporal locality)”,臨時局部原理能夠保證,如果在第一次訪問數據時緩存它,那就極有可能在短期內再次被高速緩存命中(訪問高速緩存中的數據).
143.頁高速緩存是由RAM中的物理頁組成的,緩存中的每一頁對應着磁盤中的多個塊,每當執行一次磁盤操作時,會首先檢查需要的數據是否在高速緩存中,如果在,那麼內核就直接使用高速緩存中的數據,從而避免了磁盤訪問.
144.緩衝區高速緩存:通過I/O緩衝區把獨立的磁盤塊與頁高速緩存聯繫在一起,一個緩衝就是一個單獨物理磁盤塊在內存中的表示,緩衝就是內存到磁盤塊的映射描述符,因此通過緩存磁盤塊以及緩衝I/O操作,頁高速緩存也可以減少對磁盤的訪問量.緩衝區高速緩存實際上並不是一個獨立的緩存,而是頁高速緩存的一部分.
145.當頁高速緩存中的數據比後臺磁盤中的對就數據更新時,那麼調整緩存中的這些緩存數據被稱作“髒數據”,需要在後面寫回到磁盤.


chapter 16
146.Linux是“單塊內核”(monolithic)的操作系統--也就是說,整個系統都運行於一個單獨的保護域中,但是Linux內核是模塊化的,它允許在運行時動態地向其中插入或是從中刪除代碼.這些代碼--包括子例程,數據,函數入口和函數出口被一併組合在一個單獨的二進制鏡像中,即所謂的可裝載內核模塊,或被簡稱爲“模塊”。支持模塊的好處是基本內核鏡像可以儘可能小,因爲可選的功能和驅動程序可以利用模塊的形式再提供,模塊允許我們方便地刪除和重新載入內核代碼,也方便了調試。
147.模塊被載入後,就會動態連接到內核,注意,它與用戶空間的動態連接庫類似,只有當顯示被導出後的外部函數,纔可以被動態調用。在內核中,導出內核函數需要使用特殊的命令:EXPORT_SYMBOL和
EXPORT_SYMBOL_GPL。導出的內核函數可以被模塊調用,而未導出的函數模塊則無法被調用,模塊代碼的鏈接和調用規則相比核心內核鏡像中的代碼而言,要更加嚴格,核心代碼在內核中可以調用任意非靜態接口,因爲所有的核心代碼文件被鏈接成了同一個鏡像,當然,被導出的符號表所含的函數必然也是非靜態的.導出的符號表被看作是導出的內核接口,甚至稱爲“內核API”.


chapter 17
148.設備模型:設備模型專門提供了一種獨立的機制來專門表示設備,並描述其在系統中的拓撲結構。保證能以正確的順序關閉各設備的電源是設備模型的最初動機.
149.內核事件層:實現了內核到用戶的消息通知系統,就是建立在上文一直討論的kobjects基礎之上的.


chapter 18
150.內核提供了printk這個函數用於顯示調試信息.在任何時候,任何地方都可以調用它,它可以在中斷上下文中調用,可以在進程上下文中調用,可以在持有鎖時調用,可以在多處理器上同時調用,而且調用者連鎖都不必使用.
151.神奇的SysRq
在i386和PPC上,它可以通過: ALT + PrintScreen 來訪問:該功能可以通過CONFIG_MAGIX_SYSRQ配置選項來啓用。
SysRq-b 重新啓動機器
SysRq-e 向init之外的所有的進程發送SIGTERM信號
SysRq-h 在控制檯顯示SysRq
SysRq-i 向init之外的所有的進程發送SIGKILL信號
SysRq-k 安全訪問鍵,殺死這個控制檯上的所有程序
SysRq-l 向包括init的所有的進程發送SIGKILL信號
SysRq-m 所內核信息輸出到控制檯
SysRq-o 關閉機器
SysRq-p 所寄存器的信息輸出到控制檯
SysRq-r 關閉鍵盤原始模式
SysRq-s 把所有已安裝文件系統刷新到磁盤
SysRq-t 所任務信息輸出到控制檯
SysRq-u 卸載所有已安裝文件系統.
152.內核調試的利器: kdb

chapter 19
153.人們通常所說的機器是多少位,它們其實說的是機器的字長是多少位,也就是一個字的bit數.
154.處理器的通用寄存器的大小和它的字長是相同的。對於一般的體系結構來說,它的各個部件的寬度,比如,內存總線--最少要和它的字長一樣大,地址空間的大小也等於字長.
155.Linux類型總對應於機器的字長.所以,我們可以通過 sizeof( long ) 爲4還是8來判斷是32位機還是64位機.一個指針變量的大小與寄存器的字節一致。32位機上是4字節,64位機上是8字節.
156.一個char的長度恆爲 8 bit。在Linux支持的所有的系統上,int 爲 32 bit
157.數據對齊:
 對齊是跟數據塊在內存中的位置相關的話題,如果一個變量的內存地址正好是它的長度的整數倍,那麼它就被稱爲自然對齊.舉例來說,對於一個32位類型的數據,如果它在內存中的地址剛好可以被4整除,也就是地址的最低兩位爲0,那它就是自然對齊的,也就是說同個大小爲2n字節的數據類型,它的地址的最低有效痊的後N位都應該是0.一些體系結構對對齊的要求非常嚴格,通常基於RISC的系統,載入未對齊的數據會導致處理器陷入(一種可處理的錯誤).還有一些系統可以訪問沒有對齊的數據,只不過性能會下降,編寫可移植的代碼要避免對齊問題,保證所有的類型都能夠自然對齊.
158.避免對齊引發的問題
通常編譯器會通過讓所有的數據自然對齊來避免引發對齊問題,實際上,內核開發者不用在對齊上花費太多心思,只有搞GCC的那些老兄才應該爲此犯愁。可是當程序員使用指針太多時,數據的訪問方式走出編譯器的預期時,就會引發問題了。
一個數據類型長度較小,它本來是對齊的,如果你用一個指針進行類型轉換,並且轉換後的類型長度較大,那麼通過解引用指針進行數據訪問時就會引發對齊問題(無論如何,對於某些體系結構確實存在這種問題),也就是說,下面的代碼是錯誤的:
char dog[10];
char *p = &dog[ 1 ];
unsigned long l = *( unsigned long *)p;
這個盒子將一個指向char型的指針當作指向unsigned long型的指針來用,這會引起問題,因爲此時試圖從一個不能被4整除的內存地址上載入32位的unsigned long 型的數據.
159.非標準類型的對齊
前面說到了,對於標準數據類型來說,它的地址只要是長度的整數倍就對齊了,而非標準類型的C結構體按照下列規則對齊:
 a. 對於數組,只要按照基本數據類型對齊就可以了(其實隨後的所有的元素自然能夠對齊)
 b. 對於聯合,只要它包含的長度最大的數據類型能夠對齊就可以了。
 c. 對於結構體,只要它包含的長度最大的數據類型能夠對齊就可以了,也就是結構體整體上來按結構體中長度最大的一個成員來對齊,這就是說,結構體的最終的大小要是其長度最大的成員的大小的整數倍;同時,對於結構體中的每一個成員,都要自身按照對應的規則進行對齊.
160.爲了保證結構體中的每一個成員都能夠自然對齊,結構體要進行“對齊填補”.

161.ANSI C 標準明確規定:不允許編譯器改變結構體成員的順序.
162.內核開發者需要注意結構體填補問題,特別是在整體使用時,這是指當需要通過網絡發送它們或是需要將它們寫入文件的時候,因爲不同的體系結構之間需要的填補也不盡相同,這也是爲什麼C沒有提供一個內建的結構體比較操作符的原因之一,結構體內的填充字節可能會包含垃圾信息,所以,在結構體之間進行一字節一字節的比較就不大可能了。
163.因爲結構體可能有填充對齊的問題,所以,對於不同的相同類型的結構體對象,不能直接使用
memcmp來比較,而要直接成員之間的比較.
164.字節序:
 字節序是指一個字中各個字節的順序。處理器在對字取值時,既可能將最低有效位所在的字節作爲每一個字節(最左邊的字節),也可能將其作爲最後一個字節(最右邊的字節).如果最高有效位放在最高位上,其它的字節依次放在低字節位置上,那麼這種字節序稱爲:高位優化(big endian);如果最低有效位放在最高位上,那麼這種字節序稱爲:低位優先(little endian )
165.編寫可移植的代碼
a. 編碼儘量取最大公因子:假定任何事情都可能發生,任何潛在的約束也都存在
b. 編碼儘量選取最小公約數:不要假定給定的內核特性是可用的,且僅僅需要最小的體系結構功能.


chapter 20
166.內核開發的優秀站點:





評論:
刷新評論列表

常用鏈接

隨筆分類

學習一下~~~ --佔姆士襯衫 評論內容較長,點擊標題查看 --luckycat 感覺Google Test測試一下WIN32無界面程序還算可以,測試MFC程序就沒法測試了? --ouyang --Tuotoo 爲什麼要包括在do{}while中呢? --zmm
閱讀排行榜評論排行榜
發佈了97 篇原創文章 · 獲贊 1 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章