《Linux內核設計與實現(原書第3版)》詳細描述了Linux內核的設計與實現。內核代碼的編寫者、開發(fā)者以及程序開發(fā)人員都可以通過閱讀《Linux內核設計與實現(原書第3版)》受益,他們可以更好理解操作系統(tǒng)原理,并將其應用在自己的編碼中以提高效率和生產率。
《Linux內核設計與實現(原書第3版)》詳細描述了Linux內核的主要子系統(tǒng)和特點,包括Linux內核的設計、實現和接口。從理論到實踐涵蓋了Linux內核的方方面面,可以滿足讀者的各種興趣和需求。
作者Robert Love是一位Linux內核核心開發(fā)人員,他分享了在開發(fā)Linux2.6內核過程中頗具價值的知識和經驗《Linux內核設計與實現(原書第3版)》的主題包括進程管理、進程調度、時間管理和定時器、系統(tǒng)調用接口、內存尋址、內存管理和頁緩存、VFS、內核同步、移植性相關的問題以及調試技術。同時《Linux內核設計與實現(原書第3版)》也涵蓋了Linux2.6內核中頗具特色的內容,包括CFS調度程序、搶占式內核、塊I/O層以及I/O調度程序。
暢銷圖書新版,翻譯版、影印版同步上市;詳細描述Linux內核的主要子系統(tǒng)和特點;涵蓋Linux內核從理論到實踐的方方面面。
RobertLove是一位資深的開源社區(qū)達人,很早就開始使用Linux。目前他是Google公司高級軟件工程師,是開發(fā)Android移動平臺內核的團隊成員;他曾在Novell公司任職Linux桌面系統(tǒng)的首席架構師;他之前也曾是MontaVista和Ximain公司的內核開發(fā)工程師。他參與的內核項目包括搶占式內核、進程調度器、內核事件層、通知機制、VM改進,以及設備驅動程序。他是《Linuxjournal》雜志的編輯。另外他還著有《Linux System Programming》和《Linux in aNutshell》。
陳莉君,西安郵電學院教授,十多年來一直致力于推動Linux在中國的發(fā)展,多年從事Linux內核的教學和研究,并積極跟蹤Linux內核的發(fā)展動向,對Linux內核版本的不斷演化有著深刻的理解。著譯作品有《Linux操作系統(tǒng)原理與應用》、《Linux操作系統(tǒng)內核分析》、《深入分析Linux內核源代碼》、《深入理解Linux內核》和《Linux內核編程》等。
譯者序
序言
前言
作者簡介
第1章 Linux內核簡介1
1.1 Unix的歷史1
1.2 追尋Linus足跡:Linux簡介2
1.3 操作系統(tǒng)和內核簡介3
1.4 Linux內核和傳統(tǒng)Unix內核的比較5
1.5 Linux內核版本7
1.6 Linux內核開發(fā)者社區(qū)8
1.7 小結8
第2章 從內核出發(fā)10
2.1 獲取內核源碼10
2.1.1 使用Git10
2.1.1 安裝內核源代碼10
2.1.3 使用補丁11
2.2 內核源碼樹11
2.3 編譯內核12
2.3.1 配置內核12
2.3.2 減少編譯的垃圾信息14
2.3.3 衍生多個編譯作業(yè) 14
2.3.4 安裝新內核14
2.4 內核開發(fā)的特點15
2.4.1 無libc庫抑或無標準頭文件15
2.4.2 GNU C16
2.4.3 沒有內存保護機制18
2.4.4 不要輕易在內核中使用浮點數18
2.4.5 容積小而固定的棧18
2.4.6 同步和并發(fā)18
2.4.7 可移植性的重要性19
2.5 小結19
第3章 進程管理20
3.1 進程20
3.2 進程描述符及任務結構 21
3.2.1 ?配進程描述符22
3.2.2 進程描述符的存放23
3.2.3 進程狀態(tài)23
3.2.4 設置當前進程狀態(tài)25
3.2.5 進程上下文25
3.2.6 進程家族樹25
3.3 進程創(chuàng)建26
3.3.1 寫時拷貝27
3.3.2 fork()27
3.3.3 vfork()28
3.4 線程在Linux中的實現28
3.4.1 創(chuàng)建線程29
3.4.2 內核線程30
3.5 進程終結31
3.5.1 刪除進程描述符32
3.5.2 孤兒進程造成的進退維谷32
3.6 小結34
第4章 進程調度35
4.1 多任務35
4.2 Linux 的進程?度36
4.3 策略36
4.3.1 I/O消耗型和處理器消耗型的進程36
4.3.2 進程優(yōu)先級37
4.3.3 時間片38
4.3.4 調度策略的活動38
4.4 Linux調度算法39
4.4.1 調度器類39
4.4.2 Unix 系統(tǒng)中的進程調度40
4.4.3 公平調度41
4.5 Linux調度的實現42
4.5.1 時間記賬42
4.5.2 進程選擇44
4.5.3 調度器入口48
4.5.4 睡眠和喚醒49
4.6 搶占和上下文切換51
4.6.1 用戶搶占53
4.6.2 內核搶占53
4.7 實時調度策略54
4.8 ?調度相關的系統(tǒng)調用54
4.8.1 與調度策略和優(yōu)先級相關的系統(tǒng)調用55
4.8.2 與處理器綁定有關的系統(tǒng)調用55
4.8.3 放棄處理器時間56
4.9 小結56
第5章 系統(tǒng)調用57
5.1 與內核通信57
5.2 API、POSIX和C庫57
5.3 系統(tǒng)調用58
5.3.1 系統(tǒng)調用號59
5.3.2 系統(tǒng)調用的性能59
5.4 系統(tǒng)調用處理程序60
5.4.1 指定恰當的系統(tǒng)調用60
5.4.2 參數傳遞60
5.5 系統(tǒng)調用的實現61
5.5.1 實現系統(tǒng)調用61
5.5.2 參數驗證62
5.6 系統(tǒng)調用上下文64
5.6.1 綁定一個系統(tǒng)調用的最后步驟65
5.6.2 從用戶空間訪問系統(tǒng)調用67
5.6.3 為什么不通過系統(tǒng)調用的方式實現68
5.7 小結68
第6章 內核數據結構69
6.1 鏈表69
6.1.1 單向鏈表和雙向鏈表69
6.1.2 環(huán)形鏈表70
6.1.3 沿鏈表移動71
6.1.4 Linux 內核中的實現71
6.1.5 操作鏈表73
6.1.6 遍歷鏈表75
6.2 隊列78
6.2.1 kfifo79
6.2.2 創(chuàng)建隊列79
6.2.3 推入隊列數據79
6.2.4 摘取隊列數據80
6.2.5 獲取隊列?度80
6.2.6 重置和撤銷隊列80
6.2.7 隊列使用舉例 81
6.3 映射 81
6.3.1 初始化一個idr82
6.3.2 分配一個新的UID82
6.3.3 查找UID83
6.3.4 刪除UID84
6.3.5 撤銷idr84
6.4 二叉樹84
6.4.1 二叉搜索樹84
6.4.2 自平衡二叉搜索樹 85
6.5 數據結構以及選擇 87
6.6 算法復雜度88
6.6.1 算法88
6.6.2 大o 符號88
6.6.3 大θ符號89
6.6.4 時間復雜度89
6.7 小結 90
第7章 中斷和中斷處理91
7.1 中?91
7.2 中斷處理程序92
7.3 上半部與下半部的對比93
7.4 注冊中斷處理程序93
7.4.1 中斷處理程序標志94
7.4.2 一個中斷例子95
7.4.3 釋放中斷處理程序95
7.5 編寫中斷處理程序96
7.5.1 共享的中斷處理程序97
7.5.2 中斷處理程序實例97
7.6 中斷上下文99
7.7 中斷處理機制的實現100
7.8 /proc/interrupts102
7.9 中斷控制103
7.9.1 禁止和激活中斷103
7.9.2 禁止指定中斷線105
7.9.3 中斷系統(tǒng)的狀態(tài)105
7.10 小結106
第8章 下半部和推后執(zhí)行的工作107
8.1 下半部107
8.1.1 為什么要用下半部108
8.1.2 下半部的環(huán)境108
8.2 軟中斷110
8.2.1 軟中斷的實現111
8.2.2 使用軟中斷113
8.3 tasklet114
8.3.1 tasklet的實現114
8.3.2 使用tasklet116
8.3.3 老的BH機制119
8.4 工作隊列120
8.4.1 工作隊列的實現121
8.4.2 使用工作隊列124
8.4.3 老的任務隊列機制126
8.5 下半部機制的選擇127
8.6 在下半部之間加鎖128
8.7 禁止下?部128
8.8 小結129
第9章 內核同步介紹131
9.1 臨界區(qū)和競爭條件131
9.1.1 為什么我們需要保護132
9.1.2 單個變量133
9.2 加鎖134
9.2.1 造成并發(fā)執(zhí)行的原因135
9.2.2 了解要保護些什么136
9.3 死鎖137
9.4 爭用和擴展性138
9.5 小結140
第10章 內核同步方法141
10.1 原子操作141
10.1.1 原子整數操作142
10.1.2 64位原子操作144
10.1.3 原子位操作145
10.2 自旋鎖147
10.2.1 自旋鎖方法148
10.2.2 其他針對自旋鎖的操作149
10.2.3 自旋鎖和下半部150
10.3 讀-寫自旋鎖150
10.4 信號量152
10.4.1 計數信號量和二值信號量153
10.4.2 創(chuàng)建和初始化信號量154
10.4.3 使用信號量154
10.5 讀-寫信號量155
10.6 互斥體156
10.6.1 信號量和互斥體158
10.6.2 自旋鎖和互斥體158
10.7 完成變量158
10.8 BLK:大內核鎖159
10.9 順序鎖160
10.10 禁止搶占161
10.11 順序和屏障162
10.12 小結165
第11章 定時器和時間管理166
11.1 內核中的時間概念166
11.2 節(jié)拍率:HZ167
11.2.1 理想的HZ值168
11.2.2 高HZ的優(yōu)勢169
11.2.3 高HZ的劣勢169
11.3 jiffies170
11.3.1 jiffies的內部表示171
11.3.2 jiffies 的回繞172
11.3.3 用戶空間和HZ173
11.4 硬時鐘和定時器174
11.4.1 實時時鐘174
11.4.2 系統(tǒng)定時器174
11.5 時鐘中斷處理程序174
11.6 實際時間176
11.7 定時器178
11.7.1 使用定時器178
11.7.2 定時器競爭條件180
11.7.3 實現定時器180
11.8 延遲執(zhí)行181
11.8.1 忙等待181
11.8.2 短延遲182
11.8.3 schedule_timeout()183
11.9 小結185
第12章 內存管理186
12.1 頁186
12.2 區(qū)187
12.3 獲得頁189
12.3.1 獲得填充為0的頁190
12.3.2 釋放頁191
12.4 kmalloc()191
12.4.1 gfp_mask標志192
12.4.2 kfree()195
12.5 vmalloc()196
12.6 slab層197
12.6.1 slab層的設計198
12.6.2 slab分配器的接口200
12.7 在棧上的靜態(tài)分配203
12.7.1 單頁內核棧203
12.7.2 在棧上光明正大地工作203
12.8 高端內存的映射204
12.8.1 永久映射204
12.8.2 臨時映射204
12.9 每個CPU的分配205
12.10 新的每個CPU接口206
12.10.1 編譯時的每個CPU數據206
12.10.2 運行時的每個CPU數據207
12.11 使用每個CPU數據的原因208
12.12 分配函數的選擇209
12.13 小結209
第13章 虛擬文件系統(tǒng)210
13.1 通用文件系統(tǒng)接口210
13.2 文件系統(tǒng)抽象層211
13.3 Unix文件系統(tǒng)212
13.4 VFS 對象及其數據結構213
13.5 超級塊對象214
13.6 超級塊操作215
13.7 索引節(jié)點對象217
13.8 索引節(jié)點操作219
13.9 目錄項對象222
13.9.1 目錄項狀態(tài)222
13.9.2 目錄項緩存223
13.10 目錄項操作224
13.11 文件對象225
13.12 文件操作226
13.13 和文件系統(tǒng)相關的數據結構230
13.14 和進程相關的數據結構232
13.15 小結233
第14章 塊I/O層234
14.1 剖析一個塊設備234
14.2 緩沖區(qū)和緩沖區(qū)頭235
14.3 bio結構體237
14.3.1 I/O向量238
14.3.2 新老方法對比239
14.4 請求隊列240
14.5 I/O調度程序240
14.5.1 I/O調度程序的工作241
14.5.2 Linus 電梯241
14.5.3 最終期限I/O調度程序242
14.5.4 預測I/O調度程序244
14.5.5 完全公正的排隊I/O調度程序244
14.5.6 空操作的I/O調度程序245
14.5.7 I/O調度程序的選擇245
14.6 小結246
第15章 進程地址空間247
15.1 地址空間247
15.2 內存描述符248
15.2.1 分配內存描述符249
15.2.2 撤銷內?描述符250
15.2.3 mm_struct 與內核線程250
15.3 虛擬內存區(qū)域251
15.3.1 VMA標志251
15.3.2 VMA 操作253
15.3.3 內存區(qū)域的樹型結構和內存區(qū)域的鏈表結構254
15.3.4 實際使用中的內存區(qū)域254
15.4 操作內存區(qū)域255
15.4.1 find_vma()256
15.4.2 find_vma_prev()257
15.4.3 find_vma_intersection()257
15.5 mmap()和do_mmap():創(chuàng)建地址區(qū)間258
15.6 mummap()和do_mummap():刪除地址區(qū)間259
15.7 頁表260
15.8 小結261
第16章 頁高速緩存和頁回寫262
16.1 緩存手段262
16.1.1 寫緩存262
16.1.2 緩存回收263
16.2 Linux 頁高速緩存264
16.2.1 address_space對象264
16.2.2 address_space 操作266
16.2.3 基樹267
16.2.4 以前的頁散列表268
16.3 緩沖區(qū)高速緩存268
16.4 flusher線程268
16.4.1 膝上型計算機模式270
16.4.2 歷史上的bdflush、kupdated 和pdflush270
16.4.3 避免擁塞的方法:使用多線程271
16.5 小結271
第17章 設備與模塊273
17.1 設?類型273
17.2 模塊274
17.2.1 Hello,World274
17.2.2 構建模塊275
17.2.3 安裝模塊277
17.2.4 產生模塊依賴性277
17.2.5 載入模塊278
17.2.6 管理配置選項279
17.2.7 模塊參數280
17.2.8 導出符號表282
17.3 設備模型283
17.3.1 kobject283
17.3.2 ktype284
17.3.3 kset285
17.3.4 kobject、ktype和kset的相互關系285
17.3.5 管理和操作kobject286
17.3.6 引用計數287
17.4 sysfs288
17.4.1 sysfs中添加和刪除kobject 290
17.4.2 向sysfs中添加文件291
17.4.3 內核事件層293
17.5 小結294
第18章 調試295
18.1 準備開始295
18.2 內核中的bug296
18.3 通過打印來調試296
18.3.1 健壯性296
18.3.2 日志等級297
18.3.3 記錄緩沖區(qū)298
18.3.4 syslogd和klogd298
18.3.5 從printf()到printk()的轉換298
18.4 oops298
18.4.1 ksymoops300
18.4.2 kallsyms300
18.5 內核調試配置選項301
18.6 引發(fā)bug并打印信息301
18.7?神奇的系統(tǒng)請求鍵302
18.8 內核調試器的傳奇303
18.8.1 gdb303
18.8.2 kgdb304
18.9 探測系統(tǒng)304
18.9.1 用UID作為選擇條件304
18.9.2 使用條件變量305
18.9.3 使用統(tǒng)計量305
18.9.4 重復頻率限制305
18.10 用二分查找法找出引發(fā)罪惡的變更306
18.11 使用Git進行二分搜索307
18.12 當所有的努力都失敗時:社區(qū)308
18.13 小結308
第19章 可移植性309
19.1 可移植操作系統(tǒng)309
19.2 Linux移植史310
19.3 字長和數據類型311
19.3.1 不透明類型313
19.3.2 指定數據類型314
19.3.3 長度明確的類型314
19.3.4 char型的符號問題315
19.4 數據對齊315
19.4.1 避免對齊引發(fā)的問題316
19.4.2 非標準類型的對齊316
19.4.3 結構體填補316
19.5 字節(jié)順序318
19.6 時間319
19.7 頁長度320
19.8 處理器排序320
19.9 SMP、內核搶占、高端內存321
19.10 小結321
第20章 補丁、開發(fā)和社區(qū)322
20.1 社區(qū)322
20.2 Linux編碼風格322
20.2.1 縮進323
20.2.2 switch 語句323
20.2.3 空格324
20.2.4 花括號325
20.2.5 每行代碼的長度326
20.2.6 命名規(guī)范326
20.2.7 函數326
20.2.8 注釋326
20.2.9 typedef327
20.2.10 多用現成的東西328
20.2.11 在源碼中減少使用ifdef328
20.2.12 結構初始化328
20.2.13 代碼的事后修正329
20.3 管理系統(tǒng)329
20.4 提交錯誤報告329
20.5 補丁330
20.5.1 創(chuàng)建補丁330
20.5.2 用Git創(chuàng)建補丁331
20.5.3 提交補丁331
20.6 小結332
參考資料333
16.4.1 膝上型計算機模式
膝上型計算機模式是一種特殊的頁回寫策略,該策略主要意圖是將硬盤轉動的機械行為最小化,允許硬盤盡可能長時間地停滯,以此延長電池供電時間。該模式可通過/proc/sys/vm/laptop_mode文件進行配置。通常,上述配置文件內容為0,也就是說膝上型計算機模式關閉,如果需要啟用膝上型計算機模式,則向配置文件中寫入1。
膝上型計算機模式的頁回寫行為與傳統(tǒng)方式相比只有一處變化。除了當緩存中的頁面太舊時要執(zhí)行回寫臟頁以外,flusher還會找準磁盤運轉的時機,把所有其他的物理磁盤I/O、刷新臟緩沖等通通寫回到磁盤,以便保證不會專門為了寫磁盤而去主動激活磁盤運行。
上述回寫行為變化要求dirty_expire_interval和dirty_writeback_interval兩閾值必須設置得更大,比如10分鐘。因為磁盤運轉并不很頻繁,所以用這樣長的回寫延遲就能保證膝上型計算機模式可以等到磁盤運轉機會寫入數據。因為關閉磁盤驅動器是節(jié)電的重要手段,膝上模式可以延長膝上計算機依靠電池的續(xù)航能力。其壞處則是系統(tǒng)崩潰或者其他錯誤會使得數據丟失。
多數Linux發(fā)布版會在計算機接上電池或拔掉電池時,自動開啟或禁止膝上型計算機模式以及其他需要的回寫可調節(jié)開關。因此機器可在使用電池電源時自動進入膝上型計算機模式,而在插上交流電源時恢復到常規(guī)的頁回寫模式。
16.4.2歷史上的bdflush、kupdated和pdflush
在2.6版本前,flusher線程的工作是分別由bdflush和kupdated兩個線程共同完成。
當可用內存過低時,bdflush內核線程在后臺執(zhí)行臟頁回寫操作。類似flusher,它也有一組閾值參數,當系統(tǒng)中空閑內存消耗到特定閾值以下時,bdflush線wakeup_bdflush()函數喚醒。
bdflush和當前的flusher線程之間存在兩個主要區(qū)別。第一個區(qū)別是系統(tǒng)中只有一個bdfiush后臺線程,而fiusher線程的數目卻是根據磁盤數量變化的(這在16.5節(jié)中會談到);第二個區(qū)別是bdflush線程基于緩沖,它將臟緩沖寫回磁盤。相反,flusher線程基于頁面,它將整個臟頁寫回磁盤。當然,頁面可能包含緩沖,但是實際I/O操作對象是整頁,而不是塊。因為頁在內存中是更普遍和普通的概念,所以管理頁相比管理塊要簡單。
因為只有在內存過低和緩沖數雖過大時,bdflush例程才刷新緩沖,所以kupdated例程被引入,以便周期地寫回臟頁。它和pdflush線程的wb_writeback()函數提供同樣的服務。
在2.6內核中,buflush和kupdated已讓路給了pdflush線程——page dirty flush(比以前兩個更容易令人混淆的名字)的縮寫。Pdflush線程的執(zhí)行和今天的flusher線程類似。其主要區(qū)別在于,pdflush線程數目是動態(tài)的,默認是2個到8個,具體多少取決于系統(tǒng)I/O的負載。Pdflush線程與任何任務都無關,它們是面向系統(tǒng)所有磁盤的全局任務。這樣做的好處是實現簡單,可帶來的問題是,pdflush線程很容易在擁塞的磁盤上絆住,而現代硬件發(fā)生擁塞更是家常便飯。采用每個磁盤一個刷新線程可以使得I/O操作同步執(zhí)行,簡化了擁塞邏輯,也提升了性能。
……