|
-- 會員 / 註冊 --
|
|
|
|
計算機底層的秘密 ( 簡體 字) |
作者:陸小風(@碼農的荒島求生) | 類別:1. -> 程式設計 -> 綜合 |
譯者: |
出版社:電子工業出版社 | 3dWoo書號: 56343 詢問書籍請說出此書號!【有庫存】 NT售價: 590 元 |
出版日:4/1/2023 |
頁數:324 |
光碟數:0 |
|
站長推薦: |
印刷:黑白印刷 | 語系: ( 簡體 版 ) |
|
加入購物車 │加到我的最愛 (請先登入會員) |
ISBN:9787121452772 |
作者序 | 譯者序 | 前言 | 內容簡介 | 目錄 | 序 |
(簡體書上所述之下載連結耗時費功, 恕不適用在台灣, 若讀者需要請自行嘗試, 恕不保證) |
作者序: |
譯者序: |
前言:本書源自筆者的兩個疑問: 我的代碼看上去能正常運行,可這是為什么呢?計算機 在執行我寫的代碼時在底層發生了什么?
現代計算機系統的結構就像一個漢堡包一樣,實際上是被層層抽象過的,程序員在 最上層用高級語言編寫代碼時根本不用關心底層細節,這極大地提高了開發效率,但有 時遇到一些較為棘手的問題,很多人往往束手無策,這其中大部分情況是因為對底層了 解不夠而導致的,我們有時甚至都不能理解產生的問題本身,更何談解決問題呢?
這些看上去很難解決的問題在那些編程高手眼里往往不值一提,他們幾乎能脫口而 出直指本質,你一兩天都搞不定的問題在這些編程高手那里可能會被瞬間解決掉,因為 他們對自己寫下的每一行代碼到底會對計算機系統產生什么樣的影響了如指掌,如他們 非常清楚地知道分配一塊內存在底層發生的一系列故事等。英文中有一個詞很形象—— mental model (心智模型),本書更多地為你揭示那些編程高手的心智模型和計算機系統 底層的奧秘。
在講解方式上,首先筆者認為內容可視化非常重要,一圖抵千言,因此本書中有多 達 341 張圖,以圖解的方式來講解所涉及的內容;其次內容的可讀性也很重要,本書會 以通俗易懂的方式從概念的起源開始講解,不僅告訴你是什么、為什么,還會告訴你這 是怎么來的,把對內容閱讀理解的門檻降到最低。
當然,除了上述較為“功利”的目的,筆者認為有趣的東西還是值得了解一下的, 計算機系統其實就是這樣一個很有趣的東西,如果你不這么認為的話,那么很可能是你 還不夠了解它。計算機系統中的許多設計是如此的有趣,即便是出于好奇,也應該去了 解一下,就像 Linus 所說的那樣——Just for fun !
本書配套資料
一款操作系統發布后往往需要打補丁、定時升級,而一本書的出版往往與之類似, 由于筆者能力有限,因此在本書出版后可能也需要打補丁,在微信公眾號“碼農的荒島 求生”后臺回復“補丁”二字可獲取筆者關于本書相關話題的擴展內容,相信這些內容 可以更好地幫助讀者理解本書。
路線圖
本書分為 6 章:
● 第 1 章關注編程語言,重點闡述到底什么是編程語言、編譯器的工作原理,以 及如何從代碼生成最終的可執行程序。
● 第 2 章重點講解程序運行起來后,也就是運行時的奧秘,包括程序到底是以什 么樣的形式運行起來的,操作系統、進程、線程、協程到底是什么,我們為什 么需要了解這些概念,回調函數、同步、異步、阻塞、非阻塞又是怎么一回事, 這些又能賦予程序員什么樣的能力等。
● 第 3 章將帶你認識內存。程序的運行離不開內存,因此我們要了解內存的本質 是什么, 到底什么是指針, 為什么會有堆區、棧區, 函數調用的實現原理是什么, 申請內存時底層到底發生了什么, 該怎樣實現一個自己的 malloc 內存分配器等。
● 第 4 章介紹計算機系統中最重要的 CPU ,CPU 的實現原理是什么,怎樣一步步 打造出 CPU ,CPU 是如何認識數字的,CPU 空閑時在干什么,以及 CPU 是如 何演變進化的,為什么會出現復雜指令集及精簡指令集,如何利用 CPU 與棧的 組合實現函數調用、中斷處理、線程切換及系統調用等機制。
● 第 5 章講解計算機系統中的 cache,為什么需要 cache,以及程序員該如何編 寫出對 cache 友好的代碼。
● 第 6 章關注 I/O,計算機系統是如何實現 I/O 的, 程序員調用 read 函數時在底層 是如何一步步讀取到文件內容的,程序員該如何高效處理 I/O 等。
勘誤
由于筆者水平有限,書中難免會有疏漏之處,懇請廣大讀者批評指正。
在微信公眾號“碼農的荒島求生”底部菜單欄中有一項關于本書勘誤的菜單入口, 讀者可通過此渠道查看本書的 bug 或者反饋問題。
致謝
首先感謝微信公眾號“碼農的荒島求生”的忠實讀者,是你們讓我一直堅持到現在, 是你們讓我能感受到自己做的事情是有價值的,是你們讓本書出版成為可能。
其次特別感謝我的愛人, 是你的鼓勵讓我踏上了寫作之路, 在此之前我從沒想過自 己此生會與寫作有什么關聯,是你讓我發現了全新的自己,這無異于重生。
最后感謝我的父母,是你們的辛苦付出讓我遠離生活瑣事。“當你輕裝上陣時必定 有人為你負重前行”,我無以為報,謹將此書獻給你們。 |
內容簡介:本書以圖解的方式通俗易懂的講解計算機系統中各項技術的本質,包括編程語言的本質是什么、操作系統、進程線程協程等的本質是什么、到底什么是內存、什么是堆區棧區、內存分配等是怎么一回事、怎樣從晶體管構建出CPU、I/O是如何實現的等等,從根源出發,一步步講解一項技術到底是怎么來的,同時內容可視化——輔助大量精心設計的插圖,幾乎做到了平均一頁有一圖,把對技術的理解門檻降到最低。 |
目錄:第 1 章 從編程語言到可執行程序,這是怎么一回事 / 1
1.1 假如你來發明編程語言 / 2
1.1.1 創世紀: CPU 是個聰明的笨蛋 / 3
1.1.2 匯編語言出現了 / 3
1.1.3 底層的細節 vs 高層的抽象 / 4
1.1.4 套路滿滿:高級編程語言的雛形 / 6
1.1.5 《盜夢空間》與遞歸:代碼的本質 / 7
1.1.6 讓計算機理解遞歸 / 9
1.1.7 優秀的翻譯官:編譯器 / 9
1.1.8 解釋型語言的誕生 / 10
1.2 編譯器是如何工作的 / 12
1.2.1 編譯器就是一個普通程序,沒什么大不了的 / 12
1.2.2 提取出每一個符號 / 13
1.2.3 token 想表達什么含義 / 14
1.2.4 語法樹是不是合理的 / 14
1.2.5 根據語法樹生成中間代碼 / 15
1.2.6 代碼生成 / 15
1.3 鏈接器不能說的秘密 / 16
1.3.1 鏈接器是如何工作的 / 17
1.3.2 符號決議:供給與需求 / 18
1.3.3 靜態庫、動態庫與可執行文件 / 20
1.3.4 動態庫有哪些優勢及劣勢 / 25
1.3.5 重定位:確定符號運行時地址 / 27
1.3.6 虛擬內存與程序內存布局 / 29
1.4 為什么抽象在計算機科學中如此重要 / 32
1.4.1 編程與抽象 / 32
1.4.2 系統設計與抽象 / 33
1.5 總結 / 34
第 2 章 程序運行起來了,可我對其一無所知 / 35
2.1 從根源上理解操作系統、進程與線程 / 36
2.1.1 一切要從 CPU 說起 / 36
2.1.2 從 CPU 到操作系統 / 37
2.1.3 進程很好,但還不夠方便 / 40
2.1.4 從進程演變到線程 / 41
2.1.5 多線程與內存布局 / 44
2.1.6 線程的使用場景 / 44
2.1.7 線程池是如何工作的 / 45
2.1.8 線程池中線程的數量 / 46
2.2 線程間到底共享了哪些進程資源 / 47
2.2.1 線程私有資源 / 47
2.2.2 代碼區:任何函數都可放到線程中執行 / 49
2.2.3 數據區:任何線程均可訪問數據區變量 / 49
2.2.4 堆區:指針是關鍵 / 50
2.2.5 棧區:公共的私有數據 / 50
2.2.6 動態鏈接庫與文件 / 52
2.2.7 線程局部存儲: TLS / 53
2.3 線程安全代碼到底是怎么編寫的 / 55
2.3.1 自由與約束 / 55
2.3.2 什么是線程安全 / 56
2.3.3 線程的私有資源與共享資源 / 57
2.3.4 只使用線程私有資源 / 58
2.3.5 線程私有資源 + 函數參數 / 58
2.3.6 使用全局變量 / 60
2.3.7 線程局部存儲 / 61
2.3.8 函數返回值 / 62
2.3.9 調用非線程安全代碼 / 63
2.3.10 如何實現線程安全代碼 / 64
2.4 程序員應如何理解協程 / 65
2.4.1 普通的函數 / 65
2.4.2 從普通函數到協程 / 66
2.4.3 協程的圖形化解釋 / 68
2.4.4 函數只是協程的一種特例 / 69
2.4.5 協程的歷史 / 69
2.4.6 協程是如何實現的 / 70
2.5 徹底理解回調函數 / 71
2.5.1 一切要從這樣的需求說起 / 72
2.5.2 為什么需要回調 / 73
2.5.3 異步回調 / 74
2.5.4 異步回調帶來新的編程思維 / 75
2.5.5 回調函數的定義 / 77
2.5.6 兩種回調類型 / 78
2.5.7 異步回調的問題:回調地獄 / 79
2.6 徹底理解同步與異步 / 80
2.6.1 辛苦的程序員 / 80
2.6.2 打電話與發郵件 / 81
2.6.3 同步調用 / 83
2.6.4 異步調用 / 84
2.6.5 同步、異步在網絡服務器中的應用 / 86
2.7 哦!對了,還有阻塞與非阻塞 / 91
2.7.1 阻塞與非阻塞 / 92
2.7.2 阻塞的核心問題: I/O / 92
2.7.3 非阻塞與異步 I/O / 93
2.7.4 一個類比:點比薩 / 94
2.7.5 同步與阻塞 / 95
2.7.6 異步與非阻塞 / 96
2.8 融會貫通:高并發、高性能服務器是如何實現的 / 97
2.8.1 多進程 / 97
2.8.2 多線程 / 98
2.8.3 事件循環與事件驅動 / 99
2.8.4 問題 1 :事件來源與 I/O 多路復用 / 100
2.8.5 問題 2:事件循環與多線程 / 101
2.8.6 咖啡館是如何運作的: Reactor 模式 / 102
2.8.7 事件循環與 I/O / 103
2.8.8 異步與回調函數 / 103
2.8.9 協程:以同步的方式進行異步編程 / 106
2.8.10 CPU、線程與協程 / 107
2.9 計算機系統漫游:從數據、代碼、回調、閉包到容器、虛擬機 / 108
2.9.1 代碼、數據、變量與指針 / 108
2.9.2 回調函數與閉包 / 110
2.9.3 容器與虛擬機技術 / 112
2.10 總結 / 114
第 3 章 底層?就從內存這個儲物柜開始吧 / 115
3.1 內存的本質、指針及引用 / 116
3.1.1 內存的本質是什么?儲物柜、比特、字節與對象 / 116
3.1.2 從內存到變量:變量意味著什么 / 117
3.1.3 從變量到指針:如何理解指針 / 120
3.1.4 指針的威力與破壞性:能力與責任 / 122
3.1.5 從指針到引用:隱藏內存地址 / 123
3.2 進程在內存中是什么樣子的 / 124
3.2.1 虛擬內存:眼見未必為實 / 125
3.2.2 頁與頁表:從虛幻到現實 / 125
3.3 棧區:函數調用是如何實現的 / 127
3.3.1 程序員的好幫手:函數 / 128
3.3.2 函數調用的活動軌跡:棧 / 128
3.3.3 棧幀與棧區:以宏觀的角度看 / 130
3.3.4 函數跳轉與返回是如何實現的 / 131
3.3.5 參數傳遞與返回值是如何實現的 / 133
3.3.6 局部變量在哪里 / 134
3.3.7 寄存器的保存與恢復 / 134
3.3.8 Big Picture:我們在哪里 / 134
3.4 堆區:內存動態分配是如何實現的 / 136
3.4.1 為什么需要堆區 / 136
3.4.2 自己動手實現一個 malloc 內存分配器 / 137
3.4.3 從停車場到內存管理 / 138
3.4.4 管理空閑內存塊 / 139
3.4.5 跟蹤內存分配狀態 / 141
3.4.6 怎樣選擇空閑內存塊:分配策略 / 142
3.4.7 分配內存 / 144
3.4.8 釋放內存 / 146
3.4.9 高效合并空閑內存塊 / 149
3.5 申請內存時底層發生了什么 / 150
3.5.1 三界與 CPU 運行狀態 / 150
3.5.2 內核態與用戶態 / 151
3.5.3 傳送門:系統調用 / 152
3.5.4 標準庫:屏蔽系統差異 / 153
3.5.5 堆區內存不夠了怎么辦 / 154
3.5.6 向操作系統申請內存: brk / 155
3.5.7 冰山之下:虛擬內存才是終極 BOSS / 156
3.5.8 關于分配內存完整的故事 / 156
3.6 高性能服務器內存池是如何實現的 / 157
3.6.1 內存池 vs 通用內存分配器 / 158
3.6.2 內存池技術原理 / 158
3.6.3 實現一個極簡內存池 / 159
3.6.4 實現一個稍復雜的內存池 / 160
3.6.5 內存池的線程安全問題 / 161
3.7 與內存相關的經典 bug / 162
3.7.1 返回指向局部變量的指針 / 163
3.7.2 錯誤地理解指針運算 / 163
3.7.3 解引用有問題的指針 / 164
3.7.4 讀取未被初始化的內存 / 165
3.7.5 引用已被釋放的內存 / 166
3.7.6 數組下標是從 0 開始的 / 167
3.7.7 棧溢出 / 167
3.7.8 內存泄漏 / 168
3.8 為什么 SSD 不能被當成內存用 / 169
3.8.1 內存讀寫與硬盤讀寫的區別 / 169
3.8.2 虛擬內存的限制 / 171
3.8.3 SSD 的使用壽命問題 / 171
3.9 總結 / 171
第 4 章 從晶體管到 CPU,誰能比我更重要 / 173
4.1 你管這破玩意叫 CPU / 174
4.1.1 偉大的發明 / 174
4.1.2 與、或、非: AND 、OR、NOT / 174
4.1.3 道生一、一生二、二生三、三生萬物 / 175
4.1.4 計算能力是怎么來的 / 175
4.1.5 神奇的記憶能力 / 176
4.1.6 寄存器與內存的誕生 / 177
4.1.7 硬件還是軟件?通用設備 / 178
4.1.8 硬件的基本功:機器指令 / 179
4.1.9 軟件與硬件的接口:指令集 / 179
4.1.10 指揮家,讓我們演奏一曲 / 180
4.1.11 大功告成, CPU 誕生了 / 180
4.2 CPU 空閑時在干嗎 / 181
4.2.1 你的計算機 CPU 使用率是多少 / 181
4.2.2 進程管理與進程調度 / 182
4.2.3 隊列判空:一個更好的設計 / 183
4.2.4 一切都要歸結到 CPU / 184
4.2.5 空閑進程與 CPU 低功耗狀態 / 184
4.2.6 逃出無限循環:中斷 / 185
4.3 CPU 是如何識數的 / 186
4.3.1 數字 0 與正整數 / 186
4.3.2 有符號整數 / 187
4.3.3 正數加上負號即對應的負數:原碼 / 187
4.3.4 原碼的翻轉:反碼 / 188
4.3.5 不簡單的兩數相加 / 188
4.3.6 對計算機友好的表示方法:補碼 / 189
4.3.7 CPU 真的識數嗎 / 191
4.4 當 CPU 遇上 if語句 / 192
4.4.1 流水線技術的誕生 / 193
4.4.2 CPU——超級工廠與流水線 / 195
4.4.3 當 if 遇到流水線 / 196
4.4.4 分支預測:盡量讓 CPU 猜對 / 197
4.5 CPU 核數與線程數有什么關系 / 199
4.5.1 菜譜與代碼、炒菜與線程 / 199
4.5.2 任務拆分與阻塞式 I/O / 200
4.5.3 多核與多線程 / 201
4.6 CPU 進化論(上):復雜指令集誕生 / 202
4.6.1 程序員眼里的 CPU / 202
4.6.2 CPU 的能力圈:指令集 / 202
4.6.3 抽象:少就是多 / 203
4.6.4 代碼也是要占用存儲空間的 / 203
4.6.5 復雜指令集誕生的必然 / 205
4.6.6 微代碼設計的問題 / 205
4.7 CPU 進化論(中):精簡指令集的誕生 / 206
4.7.1 化繁為簡 / 206
4.7.2 精簡指令集哲學 / 207
4.7.3 CISC 與 RISC 的區別 / 208
4.7.4 指令流水線 / 209
4.7.5 名揚天下 / 210
4.8 CPU 進化論(下):絕地反擊 / 211
4.8.1 打不過就加入:像 RISC 一樣的 CISC / 211
4.8.2 超線程的絕技 / 212
4.8.3 取人之長,補己之短: CISC 與 RISC 的融合 / 214
4.8.4 技術不是全部: CISC 與 RISC 的商業之戰 / 214
4.9 融會貫通:CPU、棧與函數調用、系統調用、線程切換、中斷處理 / 215
4.9.1 寄存器 / 215
4.9.2 棧寄存器: Stack Pointer / 216
4.9.3 指令地址寄存器: Program Counter / 216
4.9.4 狀態寄存器: Status Register / 217
4.9.5 上下文: Context / 218
4.9.6 嵌套與棧 / 218
4.9.7 函數調用與運行時棧 / 220
4.9.8 系統調用與內核態棧 / 220
4.9.9 中斷與中斷函數棧 / 223
4.9.10 線程切換與內核態棧 / 224
4.10 總結 / 227
第 5 章 四兩撥千斤, cache / 228
5.1 cache,無處不在 / 229
5.1.1 CPU 與內存的速度差異 / 229
5.1.2 圖書館、書桌與 cache / 230
5.1.3 天下沒有免費的午餐: cache 更新 / 232
5.1.4 天下也沒有免費的晚餐:多核 cache 一致性 / 233
5.1.5 內存作為磁盤的 cache / 235
5.1.6 虛擬內存與磁盤 / 237
5.1.7 CPU 是如何讀取內存的 / 238
5.1.8 分布式存儲來幫忙 / 238
5.2 如何編寫對 cache 友好的程序 / 240
5.2.1 程序的局部性原理 / 240
5.2.2 使用內存池 / 241
5.2.3 struct 結構體重新布局 / 241
5.2.4 冷熱數據分離 / 242
5.2.5 對 cache 友好的數據結構 / 243
5.2.6 遍歷多維數組 / 243
5.3 多線程的性能“殺手” / 245
5.3.1 cache 與內存交互的基本單位: cache line / 246
5.3.2 性能“殺手”一: cache 乒乓問題 / 247
5.3.3 性能“殺手”二:偽共享問題 / 250
5.4 烽火戲諸侯與內存屏障 / 253
5.4.1 指令亂序執行:編譯器與 OoOE / 255
5.4.2 把 cache 也考慮進來 / 257
5.4.3 四種內存屏障類型 / 259
5.4.4 acquire-release 語義 / 263
5.4.5 C++ 中提供的接口 / 264
5.4.6 不同的 CPU,不同的秉性 / 265
5.4.7 誰應該關心指令重排序:無鎖編程 / 266
5.4.8 有鎖編程 vs 無鎖編程 / 267
5.4.9 關于指令重排序的爭議 / 267
5.5 總結 / 268
第 6 章 計算機怎么能少得了 I/O / 269
6.1 CPU 是如何處理 I/O 操作的 / 270
6.1.1 專事專辦: I/O 機器指令 / 270
6.1.2 內存映射 I/O / 270
6.1.3 CPU 讀寫鍵盤的本質 / 271
6.1.4 輪詢:一遍遍地檢查 / 272
6.1.5 點外賣與中斷處理 / 273
6.1.6 中斷驅動式 I/O / 274
6.1.7 CPU 如何檢測中斷信號 / 275
6.1.8 中斷處理與函數調用的區別 / 276
6.1.9 保存并恢復被中斷程序的執行狀態 / 277
6.2 磁盤處理 I/O 時 CPU 在干嗎 / 279
6.2.1 設備控制器 / 280
6.2.2 CPU 應該親自復制數據嗎 / 281
6.2.3 直接存儲器訪問: DMA / 281
6.2.4 Put Together / 283
6.2.5 對程序員的啟示 / 284
6.3 讀取文件時程序經歷了什么 / 285
6.3.1 從內存的角度看 I/O / 285
6.3.2 read 函數是如何讀取文件的 / 286
6.4 高并發的秘訣:I/O 多路復用 / 291
6.4.1 文件描述符 / 291
6.4.2 如何高效處理多個 I/O / 292
6.4.3 不要打電話給我,有必要我會打給你 / 293
6.4.4 I/O 多路復用 / 294
6.4.5 三劍客: select 、poll 與 epoll / 294
6.5 mmap:像讀寫內存那樣操作文件 / 295
6.5.1 文件與虛擬內存 / 296
6.5.2 魔術師操作系統 / 297
6.5.3 mmap vs 傳統 read/write 函數 / 298
6.5.4 大文件處理 / 299
6.5.5 動態鏈接庫與共享內存 / 299
6.5.6 動手操作一下 mmap / 301
6.6 計算機系統中各個部分的時延有多少 / 302
6.6.1 以時間為度量來換算 / 303
6.6.2 以距離為度量來換算 / 304
6.7 總結 / 305 |
序: |
|