人們日常生活中打交道最多的就是嵌入式系統(tǒng),目前廣泛使用的手機、MP3播放器、智能家用電器、無人機、自動駕駛汽車、機器人等都用到了嵌入式系統(tǒng),嵌入式系統(tǒng)的開發(fā)占整個計算機系統(tǒng)開發(fā)的比重也越來越大。本書詳細講解嵌入式Linux驅動開發(fā)和設備端系統(tǒng)構建,并配套全書實例源代碼和作者QQ答疑服務。
《嵌入式Linux驅動開發(fā)實踐》共分12章,內容包括嵌入式系統(tǒng)概述、搭建Linux安全開發(fā)環(huán)境、必會的嵌入式開發(fā)應用層技術、內核模塊開發(fā)、字符設備驅動、驅動模塊的并發(fā)控制、塊設備驅動、Linux平臺驅動、基于AArch64的內核和文件系統(tǒng)、設備樹、I2C驅動實戰(zhàn)、SPI驅動實戰(zhàn)。
《嵌入式Linux驅動開發(fā)實踐》適合作為嵌入式Linux驅動開發(fā)初學者的入門書,以及嵌入式Linux開發(fā)人員的參考書,也適合作為高等院校電子、通信、自動化、計算機等專業(yè)“嵌入式操作系統(tǒng)”課程的教材和教學參考書。
嵌入式開發(fā)的涉及面很廣,必須集中某個知識點學透相關知識,才能從事這方面的基本開發(fā)工作,然后在工作中不斷提高。那應該如何進行學習呢?筆者的經(jīng)驗是分兩大塊來學,分別是主機端的驅動和設備端的系統(tǒng)構建。
本書內容經(jīng)過精心設計,使用Linux虛擬機來學習驅動開發(fā),并使用QEMU軟件模擬一個開發(fā)板來學習設備端系統(tǒng)的構建,降低讀者學習成本和入門門檻。
隨著超大規(guī)模集成電路的發(fā)展,計算機處理器技術不斷提高,計算機芯片的處理能力越來越強,體積越來越小,計算機技術廣泛應用到生活的方方面面。與人們日常生活打交道最多的就是嵌入式系統(tǒng),從目前廣泛使用的手機、MP3播放器、家用電器到無人機、自動駕駛汽車、機器人,嵌入式系統(tǒng)的應用無處不在。嵌入式系統(tǒng)的開發(fā)占整個計算機系統(tǒng)開發(fā)的比重也越來越大。
嵌入式系統(tǒng)開發(fā)與傳統(tǒng)的計算機程序開發(fā)不同。嵌入式系統(tǒng)開發(fā)涉及軟件和硬件的開發(fā),是一個協(xié)同工作的統(tǒng)一體。目前,已經(jīng)有許多的嵌入式系統(tǒng)硬件和操作系統(tǒng)軟件,其中應用最廣泛的是ARM/AArch64嵌入式處理器和Linux系統(tǒng)。
寫作思路
嵌入式開發(fā)的涉及面很廣,要在一本書中講述所有內容是不可能的?梢赃@么說,如果誰看到某本書囊括很多內容,但篇幅又不大,那基本上是蜻蜓點水,毫無深度。嵌入式開發(fā)必須集中某個知識點學透相關知識,才能從事這方面的基本開發(fā),然后在工作中不斷提高。那應該如何進行學習呢?筆者的經(jīng)驗是分兩大塊來學,分別是主機端的驅動和設備端系統(tǒng)的構建。
通常嵌入式產(chǎn)品都會有一個主機端運行的用戶程序(或稱客戶端),它通過主機端的驅動和設備端應用程序通信,指示設備端完成某個功能;而設備端也是一個Linux系統(tǒng),它除運行設備端應用程序外,還要運行設備端驅動程序,以此讓設備端應用程序和設備端的某個硬件設備通信。但初學者(比如,學生朋友)不可能購買所有的設備端硬件(費用太高)來學習設備端驅動,因此我們可以把學習驅動的過程放在主機端來,也就是在主機端的Linux虛擬機中學習驅動開發(fā),一旦學會,以后轉到設備端開發(fā)驅動程序大同小異,甚至只是換個編譯器而已。
另外,筆者為了讓初學者節(jié)省學習成本,把主機端的驅動開發(fā)也盡量做成虛擬驅動開發(fā),也就是說沒有硬件,不需要購買昂貴的開發(fā)板,也可以學習驅動開發(fā)。區(qū)別就是看不到實際硬件的工作結果,但我們可以用數(shù)據(jù)來表示,比如驅動模塊收到上層發(fā)來的數(shù)據(jù)1,就表示燈亮了。在實際工作中,我們只需要替換調用一下廠家提供的硬件操作API函數(shù)即可,這些函數(shù)我們可以現(xiàn)學現(xiàn)用,沒必要在初學階段去掌握。
除驅動開發(fā)外,設備端的系統(tǒng)構建也是一個難點,幸運的是,我們依然可以使用QEMU這個軟件來模擬一個開發(fā)板,并在QEMU上編譯一個內核并啟動這個內核,再加上文件系統(tǒng),等等。這些步驟與實際在開發(fā)板上的操作區(qū)別不大。開發(fā)板最多就是多了一個Uboot(類似于計算機的BIOS)開機引導程序,而這個開機引導程序開發(fā)板廠家也是會提供的。
基于這兩點(驅動和基于內核的系統(tǒng)構建),我們以此為重點貫穿本書,并給出較多的實例和詳細的說明,相信讀者能夠快速進入嵌入式開發(fā)的大門。
源碼資源下載
本書配套示例源碼需要用微信掃描下面的二維碼獲取,也可按掃描出來的頁面提示輸入你的電子郵箱,把下載鏈接發(fā)送到郵箱中再下載。
如果在學習和下載資源的過程中遇到問題,可以發(fā)送郵件至booksaga@163.com,郵件主題寫“嵌入式Linux驅動開發(fā)實踐”。
作者答疑服務
如果在學習過程中遇到問題,也可以向作者寫信或加作者QQ,作者聯(lián)系方式參見下載資源中的相關文件。
作 者
2023年12月
朱文偉,名校計算機專業(yè)統(tǒng)招碩士,20多年C\C 、Java開發(fā)經(jīng)驗。主導開發(fā)過密碼、圖形、人工智能等產(chǎn)品。精通Linux、Windows系統(tǒng)開發(fā)及數(shù)據(jù)庫開發(fā)技術。著有圖書:
《高性能Linux網(wǎng)絡編程核心技術揭秘》《Linux C/C 服務器開發(fā)實踐》《Qt 6.x從入門到精通》《PyQt 5從入門到精通》《Linux C與C 一線開發(fā)實踐》《Visual C 2017從入門到精通》《Windows C/C 加密解密實戰(zhàn)》《密碼學原理與Java實現(xiàn)》《OpenCV 4.5計算機視覺開發(fā)實戰(zhàn)(基于VC )》《OpenCV 4.5計算機視覺開發(fā)實戰(zhàn):基于Python》。
第 1 章 嵌入式系統(tǒng)概述 1
1.1 嵌入式系統(tǒng) 1
1.2 Linux操作系統(tǒng) 2
1.3 Linux作為嵌入式操作系統(tǒng)的優(yōu)勢 2
1.4 嵌入式系統(tǒng)的開發(fā)流程 4
1.5 嵌入式Linux系統(tǒng)的體系結構 5
1.5.1 嵌入式處理器 5
1.5.2 嵌入式外圍硬件設備 5
1.5.3 嵌入式操作系統(tǒng) 6
1.5.4 設備驅動 6
1.5.5 嵌入式應用軟件 6
1.6 嵌入式Linux系統(tǒng)的設計與實現(xiàn) 6
1.7 Linux操作系統(tǒng)內核 7
1.7.1 Linux內核的組成 7
1.7.2 Linux內核各部分的工作機制 8
1.8 Linux設備驅動程序 12
1.8.1 Linux設備驅動概述 12
1.8.2 設備驅動的功能 13
1.8.3 設備的分類 13
1.8.4 驅動的分類 14
1.8.5 設備驅動與內核的關系 16
1.8.6 設備驅動的結構 17
1.8.7 設備驅動的設計和實現(xiàn)步驟 19
第 2 章 搭建Linux驅動開發(fā)環(huán)境 22
2.1 準備虛擬機環(huán)境 22
2.1.1 在VMware下安裝Linux 22
2.1.2 開啟登錄時的root賬號 25
2.1.3 關閉內核自動更新 27
2.1.4 解決Ubuntu上的vi方向鍵問題 27
2.1.5 關閉防火墻 27
2.1.6 配置安裝源 28
2.1.7 安裝網(wǎng)絡工具包 28
2.1.8 安裝基本開發(fā)工具 29
2.1.9 啟用SSH 29
2.1.10 做個快照 30
2.1.11 連接虛擬機Linux 31
2.1.12 和虛擬機互傳文件 42
2.2 安裝編譯工具 42
2.3 使用VS Code開發(fā)內核驅動程序 43
2.4 使用Visual C 2017開發(fā)應用程序 48
第 3 章 嵌入式開發(fā)必會應用層技術 51
3.1 Linux啟動過程 51
3.2 圖形模式與命令模式的切換方式 53
3.3 在文件中搜索 53
3.4 Linux關機和重啟 54
3.5 開機自啟動 55
3.6 查看Ubuntu的內核版本 56
3.7 查看Ubuntu操作系統(tǒng)的版本 57
3.8 配置文件的區(qū)別 57
3.9 讓/etc/profile文件修改后立即生效 58
3.10 測試Web服務器的性能 59
3.10.1 架設Web服務器Apache 59
3.10.2 在Windows下測試Web服務器的性能 60
3.10.3 在Linux下測試Web服務器的性能 61
3.11 Linux中的文件權限 66
3.12 環(huán)境變量的獲取和設置 66
3.13 解析命令行參數(shù)函數(shù) 69
第 4 章 內核模塊開發(fā) 70
4.1 Linux內核概述 70
4.2 內核模塊簡介 71
4.2.1 何為內核模塊 71
4.2.2 增加內核功能的兩種方法 72
4.2.3 使用模塊的優(yōu)缺點 72
4.2.4 常用的模塊操作命令 72
4.2.5 Linux內核程序結構 73
第 5 章 字符設備驅動 76
5.1 Linux設備框架 76
5.2 字符設備的概念 77
5.3 字符設備驅動 80
5.3.1 file_operations結構體 80
5.3.2 字符設備驅動開發(fā)步驟 82
5.3.3 設備號的分配 85
5.4 驅動開發(fā)的常用函數(shù) 86
5.4.1 copy_from_user函數(shù) 86
5.4.2 copy_to_user函數(shù) 87
5.4.3 printk函數(shù) 88
5.4.4 register_chrdev函數(shù) 89
5.4.5 register_chrdev_region函數(shù) 91
5.4.6 alloc_chrdev_region函數(shù) 91
5.4.7 cdev_init函數(shù) 91
5.4.8 cdev_alloc函數(shù) 92
5.4.9 cdev_add函數(shù) 93
5.4.10 cdev_del函數(shù) 95
5.4.11 宏class_create 95
5.4.12 device_create函數(shù) 96
5.4.13 device_del函數(shù) 96
5.4.14 unregister_chrdev函數(shù) 97
5.4.15 實戰(zhàn)字符設備驅動 97
5.5 字符設備的ioctl接口 106
5.5.1 什么是ioctl接口 106
5.5.2 為什么要引入ioctl接口 106
5.5.3 ioctl如何使用 106
5.5.4 定義命令 107
5.5.5 ioctl的基本應用 109
5.5.6 ioctl處理結構體 112
5.6 Linux虛擬驅動框架設計 114
5.7 虛擬LED驅動的實現(xiàn) 116
第 6 章 驅動模塊的并發(fā)控制 122
6.1 嵌入式Linux系統(tǒng)的空間組成 122
6.1.1 操作系統(tǒng)內核 122
6.1.2 操作系統(tǒng)的空間組成及模式 123
6.1.3 用戶空間訪問內核空間及模式切換 123
6.2 進程的基本概念 124
6.2.1 進程和線程的定義 124
6.2.2 進程的類型 125
6.2.3 進程的內存結構 125
6.2.4 多任務機制 126
6.2.5 進程與程序 126
6.2.6 進程標識符 127
6.2.7 線程標識符 129
6.2.8 線程組及其標識符TGID 129
6.2.9 進程描述符 131
6.2.10 會話、進程組以及控制終端 138
6.3 PID的管理 139
6.3.1 PID散列表 140
6.3.2 PID命名空間 140
6.3.3 局部ID和全局ID 142
6.3.4 進程PID結構 143
6.3.5 pid_link哈希表存儲 143
6.4 進程切換分析 145
6.4.1 進程的模式和分類 145
6.4.2 進程的5種基本狀態(tài) 146
6.4.3 進程的切換過程分析 147
6.5 內核進程和線程管理編程 148
6.5.1 獲得進程PID結構體 148
6.5.2 從命名空間下的PID找到對應的PID結構體 149
6.5.3 獲取進程的進程號 150
6.5.4 改變PID結構體的count字段 151
6.5.5 獲取進程描述符信息 152
6.5.6 釋放進程所占用的Cache空間 153
6.5.7 喚醒進程 154
6.5.8 創(chuàng)建一個新的內核線程 156
6.5.9 終止指定進程 158
6.5.10 結束當前正在執(zhí)行的進程 159
6.6 并發(fā)控制的基本概念 160
6.6.1 什么是并發(fā) 160
6.6.2 臨界資源與臨界區(qū) 160
6.6.3 原子操作 160
6.6.4 并發(fā)控制的內容 161
6.6.5 為何要并發(fā)控制 161
6.7 設備驅動的并發(fā)控制機制 162
6.7.1 并發(fā)控制的基礎操作 162
6.7.2 自旋鎖 164
6.7.3 信號量 165
6.7.4 其他的并發(fā)控制機制 166
6.7.5 驅動并發(fā)控制的設計方法 167
6.8 內核同步編程 170
6.8.1 設置原子類型的變量并讀取 170
6.8.2 遞增遞減原子變量值 171
6.8.3 初始化信號量 172
6.8.4 獲取信號量并減1(不可中斷) 173
6.8.5 獲取信號量并減1(可中斷) 174
6.8.6 在指定的時間內獲取信號量 175
6.8.7 釋放信號量 176
第 7 章 塊設備驅動 178
7.1 塊設備的概念 178
7.1.1 什么是塊設備 178
7.1.2 常用的塊設備 178
7.1.3 塊設備和字符設備的區(qū)別 179
7.1.4 塊設備相關的幾個單位 180
7.1.5 塊設備的訪問 181
7.2 塊設備驅動程序的概念 182
7.2.1 什么是塊設備驅動程序 182
7.2.2 為什么需要了解塊設備驅動 182
7.2.3 塊設備驅動的組成部分 183
7.2.4 塊設備驅動框架 184
7.3 塊設備驅動的關鍵數(shù)據(jù)結構 187
7.3.1 通用硬盤結構gendisk 187
7.3.2 塊設備對象結構block_device 188
7.3.3 bio結構 189
7.3.4 請求隊列request_queue結構 189
7.3.5 請求結構request 190
7.3.6 磁盤操作結構block_device_operations 191
7.4 塊設備驅動中的I/O請求處理函數(shù) 192
7.4.1 使用請求隊列處理I/O請求 192
7.4.2 不使用請求隊列處理I/O請求 192
7.5 塊設備驅動編寫的步驟 193
7.6 重要函數(shù) 193
7.6.1 注冊 193
7.6.2 塊設備操作 194
7.7 實戰(zhàn)案例:用RAM模擬一個塊設備 195
第 8 章 Linux平臺驅動 203
8.1 平臺設備驅動模型 203
8.1.1 驅動的分隔與分離 203
8.1.2 驅動的分層 205
8.1.3 基本概念 205
8.1.4 什么是platform總線 206
8.2 platform驅動 209
8.2.1 platform_driver結構體 209
8.2.2 platform主要函數(shù) 211
8.2.3 platform驅動框架 212
8.2.4 platform_device的注冊過程 214
8.3 platform設備 215
8.4 實現(xiàn)platform驅動 218
第 9 章 基于AArch64的內核和文件系統(tǒng) 229
9.1 認識QEMU 230
9.1.1 QEMU是什么 230
9.1.2 QEMU的兩種執(zhí)行模式 231
9.1.3 QEMU的用途 231
9.1.4 使用QEMU虛擬機的幾種選擇 231
9.2 不編譯運行AArch64程序 232
9.2.1 準備一個現(xiàn)成的AArch64程序 233
9.2.2 安裝Linux版的QEMU 233
9.2.3 下載交叉編譯器 238
9.2.4 讓AArch64程序運行起來 238
9.3 編譯運行AArch64程序 240
9.4 制作簡易文件系統(tǒng) 244
9.4.1 BusyBox簡介 245
9.4.2 編譯/安裝BusyBox 246
9.4.3 制作根文件系統(tǒng)的映像文件 249
9.5 非嵌入式方式啟動內核 250
9.5.1 BusyBox啟動過程簡要分析 254
9.5.2 在新內核系統(tǒng)中運行C程序 256
9.6 基本功能的完善 257
9.6.1 掛載proc支持ifconfig 257
9.6.2 掛載sysfs支持lspci 259
9.6.3 實現(xiàn)文件系統(tǒng)可寫 264
9.7 QEMU用戶網(wǎng)絡模式 265
9.7.1 不使用-net選項 265
9.7.2 使用-net選項 269
9.8 QEMU橋接網(wǎng)絡模式 270
9.8.1 網(wǎng)橋的概念 270
9.8.2 TUN/TAP的工作原理 270
9.8.3 帶TAP的QEMU系統(tǒng)架構 272
9.8.4 brctl的簡單用法 273
9.8.5 三個網(wǎng)絡配置選項 275
9.8.6 實戰(zhàn)橋接模式網(wǎng)絡 277
9.8.7 手工命令創(chuàng)建TAP網(wǎng)卡 282
9.8.8 使用qemu-ifup 284
9.9 QEMU運行國產(chǎn)操作系統(tǒng) 285
9.9.1 安裝Windows版的QEMU 286
9.9.2 UEFI固件下載 286
9.9.3 安裝麒麟操作系統(tǒng) 287
9.9.4 運行麒麟系統(tǒng) 289
第 10 章 設備樹 291
10.1 設備樹的概念 291
10.1.1 什么是設備樹 291
10.1.2 設備樹的起源 292
10.1.3 Linux內核對硬件的描述方式 294
10.1.4 設備樹和內核的關系 295
10.2 DTS文件和DTSI文件 295
10.3 DTB文件和DTC文件 296
10.4 設備樹框架 297
10.4.1 布局與節(jié)點 298
10.4.2 節(jié)點名 299
10.4.3 引用 300
10.4.4 cpus節(jié)點 301
10.5 屬性 302
10.5.1 兼容性屬性 303
10.5.2 model屬性 305
10.5.3 status屬性 305
10.5.4 #address-cells和#size-cells 305
10.5.5 reg屬性 306
10.5.6 ranges屬性 307
10.5.7 name屬性 308
10.5.8 device_type屬性 308
10.5.9 address屬性 308
10.5.10 interrupts屬性 309
10.6 設備樹操作常用API 311
10.6.1 device_node 311
10.6.2 查找節(jié)點API 312
10.6.3 提取通用屬性API 312
10.6.4 提取addr屬性API 313
10.6.5 提取resource屬性API 314
10.6.6 提取GPIO屬性API 315
10.6.7 提取irq屬性API 315
10.6.8 從設備樹中提取MAC地址 315
10.7 編寫設備樹并編譯 315
第 11 章 I2C驅動實戰(zhàn) 317
11.1 I2C的基本概念 317
11.1.1 總線的定義 317
11.1.2 什么是I2C 318
11.1.3 I2C總線 318
11.1.4 I2C總線規(guī)范 318
11.1.5 I2C總線的特點 319
11.2 I2C驅動實戰(zhàn) 320
第 12 章 SPI驅動實戰(zhàn) 334
12.1 SPI概述 334
12.1.1 什么是SPI 334
12.1.2 SPI工作模式 337
12.1.3 SPI傳輸機制 338
12.1.4 I2C和SPI的對比 340
12.2 SPI驅動軟件架構 340
12.2.1 SPI通用設備驅動程序 341
12.2.2 SPI控制器驅動程序 342
12.2.3 SPI協(xié)議驅動程序 342
12.2.4 隊列化 343
12.3 SPI虛擬驅動實戰(zhàn) 343