(2021) 23 [持久化] I/O设备与驱动
(2021) 23 [持久化] I/O設(shè)備與驅(qū)動
南京大學(xué)操作系統(tǒng)課蔣炎巖老師網(wǎng)絡(luò)課程筆記。
視頻:https://www.bilibili.com/video/BV1HN41197Ko?p=23
講義:http://jyywiki.cn/OS/2021/slides/13.slides#/
背景
很多人 (你們的同學(xué)們、家長們) 都有一個認(rèn)識,“計(jì)算機(jī)系就是裝 (修) 電腦的”,因?yàn)榇蠹覍﹄娔X的印象只有 I/O 設(shè)備。但上了計(jì)算機(jī)系發(fā)現(xiàn)根本不是這么回事啊,根本沒有直接教怎么 “修電腦”。
但不應(yīng)該是這樣的!
- 學(xué) “計(jì)算機(jī)” 的人不僅會修電腦,還會造電腦!
只要我們有一個設(shè)備的手冊或者文檔等充足的信息,就可以結(jié)合自己在操作系統(tǒng)等課程上的知識,來查找并修復(fù)問題。
本次課的內(nèi)容和目標(biāo)
理解 “I/O 設(shè)備是什么”
- 鍵盤
- 磁盤
- 中斷控制器
- 總線
- DMA
- GPU
學(xué)習(xí) I/O 設(shè)備在操作系統(tǒng)中的抽象
- 設(shè)備 = 可以讀、寫、控制的對象
- “設(shè)備驅(qū)動程序”
常見 I/O 設(shè)備
孤獨(dú)的CPU
CPU 只是 “無情的指令執(zhí)行機(jī)器”,不斷地從內(nèi)存上進(jìn)行取指令、譯碼、執(zhí)行。
我們是通過各種各樣IO設(shè)備實(shí)現(xiàn)與CPU的交互的。
CPU眼中的IO設(shè)備
I/O 設(shè)備是一個能與 CPU 交換數(shù)據(jù)的接口
通俗來講:
- 就是 “幾組線”
- 每一組線有約定的功能 (RTFM)
- CPU 通過握手信號從線上讀出/寫入數(shù)據(jù)
- 每一組線有自己的地址
- CPU 可以直接使用指令 (in/out/MMIO) 和設(shè)備交換數(shù)據(jù)
在CPU看來,與常見的IO設(shè)備的交互,就是按照特定的規(guī)則(RTFM)來讀寫一組寄存器。比如下面介紹的鍵盤控制器和磁盤控制器。
鍵盤控制器
IBM PC/AT 8042 PS/2 (Keyboard) Controller
- “硬編碼” 到兩個 I/O port: 0x60 (data), 0x64 (status/command)
| 0xED | LED 燈控 | ScrollLock/NumLock/CapsLock |
| 0xF3 | 設(shè)置重復(fù)速度 | 30Hz - 2Hz; Delay: 250 - 1000ms |
| 0xF4/0xF5 | 打開/關(guān)閉 | N/A |
| 0xFE | 重新發(fā)送 | N/A |
| 0xFF | RESET | N/A |
參考 AbstractMachine 的鍵盤部分實(shí)現(xiàn)
磁盤控制器
ATA (Advanced Technology Attachment)
- IDE (Integrated Drive Electronics) 接口磁盤
- primary: 0x1f0 - 0x1f7; secondary: 0x170 - 0x177
總之,以上這些IO設(shè)備,在CPU看來,就是按照特定的規(guī)則(RTFM)來讀寫一組寄存器。
特殊的IO設(shè)備
中斷控制器
我們的設(shè)備中通常有許多IO設(shè)備,那我們只想在這些IO設(shè)備有有意義的數(shù)據(jù)/信號時,CPU才回去處理這些數(shù)據(jù),而在平時IO設(shè)備沒有數(shù)據(jù)時,不要打擾CPU的其他正常工作。這就要通過中斷來實(shí)現(xiàn)。
CPU有一個中斷引腳收到某個特定的電信號會觸發(fā)中斷
- 保存 5 個寄存器 (cs, rip, rflags, ss, rsp)
- 跳轉(zhuǎn)到中斷向量表對應(yīng)項(xiàng)執(zhí)行
CPU有且只有一個中斷引腳(如圖中6502的4號引腳 IRQˉ\bar{IRQ}IRQˉ?)。那CPU是怎樣管理各種不同的中斷,以及不同中斷到來時的處理優(yōu)先級的呢?這就要用到一個特殊的IO設(shè)備:中斷控制器。
系統(tǒng)中的其他設(shè)備可以向中斷控制器連線。從而實(shí)現(xiàn)上面提到的中斷優(yōu)先級的判斷、中斷屏蔽等功能。
- Intel 8259 PIC(programmable interrupt controller),(微機(jī)原理中也介紹過,配合8086),可以設(shè)置中斷屏蔽、中斷觸發(fā)等……
- APIC (Advanced PIC)(現(xiàn)代CPU中使用的中斷控制器)
- local APIC(每個CPU內(nèi)部的中斷管理): 中斷向量表, IPI, 時鐘, ……
- I/O APIC(外部IO的中斷管理): 其他 I/O 設(shè)備
總線
想法
如果你只造 “一臺計(jì)算機(jī)”,隨便給每個設(shè)備定一個端口/地址,用 mux 連接到 CPU 就行。
但如果你希望給未來留點(diǎn)空間?
- 想賣大價(jià)錢的 “大型機(jī)”
- IBM, DEC, …
- 車庫里造出來的 “微型機(jī)”
- 名垂青史的夢想家
- 都希望接入更多 I/O 設(shè)備
- 甚至是未知的設(shè)備,即可擴(kuò)展性
在多個IO設(shè)備時,每個IO設(shè)備中的每個寄存器都有一個自己的地址,那肯定不能將這些寄存器全部直連到CPU上。
總線介紹
總線提供地址到設(shè)備的轉(zhuǎn)發(fā)。把從CPU傳來的地址(總線地址)和數(shù)據(jù)轉(zhuǎn)發(fā)到相應(yīng)的設(shè)備上(按照上面說的每個設(shè)備中的每個寄存器的地址)。例如: port I/O 的端口就是總線上的地址,IBM PC 的 CPU 其實(shí)只看到這一個 I/O 設(shè)備。
這樣 CPU 只需要直連一個總線 (例如今天的 PCI總線 (Peripheral Component Interconnect) 就行了
- 總線可以負(fù)責(zé)IO設(shè)備寄存器的地址編址
- 總線可以橋接其他總線 (例如 PCI → USB)
- lspci -tv 和 lsusb -tv: 查看系統(tǒng)中總線上的設(shè)備
- 概念簡單,實(shí)際非常復(fù)雜
- 電氣特性、burst 傳輸、中斷……
例子:PCI Device Probe
DMA(Direct Memory Access)
想法
假設(shè)程序希望寫入 1 GB 的數(shù)據(jù)到磁盤
- 即便磁盤已經(jīng)準(zhǔn)備好,依然需要非常浪費(fèi)緩慢的循環(huán)
- out 指令寫入的是設(shè)備緩沖區(qū),需要去總線上繞一圈
- cache disable; store 其實(shí)很慢的
能否把 CPU 從執(zhí)行循環(huán)中解放出來?
- 比如,在系統(tǒng)里加一個 CPU,專門復(fù)制數(shù)據(jù)?
- 好像 memcpy_to_port(ATA0, buf, length);
DMA介紹
DMA可以理解為一個只能執(zhí)行memcpy這一條指令的迷你處理器,它只負(fù)責(zé)直接將內(nèi)存上的內(nèi)容搬運(yùn)的磁盤。而不需要真正的CPU的參與,不需要占用真正的CPU時間。實(shí)際實(shí)現(xiàn):直接把 DMA 控制器連接在總線和內(nèi)存。
由于DMA處理器只進(jìn)行memcpy這樣一條指令,因此它的實(shí)現(xiàn)比通用的真正的CPU簡單得多。
DMA不僅可以完成內(nèi)存和設(shè)備之間的內(nèi)容搬運(yùn),而也可以是內(nèi)存和內(nèi)存之間。即可以有:
- memory → memory
- memory → device (register)
- device (register) → memory
PCI 總線支持 DMA,即PCI中自帶DMA,這就是為什么 CPU 會有 PCIe lanes。
GPU
在CPU眼中,顯卡(GPU)也是一種IO設(shè)備,通過讀寫某些寄存器來交互。
一切皆可計(jì)算
for (int i = 1; i <= H; i++) {for (int j = 1; j <= W; j++)putchar(j <= i ? '*' : ' ');putchar('\n'); }難辦的是性能
- NES: 6502 @ 1.79Mhz; IPC = 0.43
- 屏幕共有 256 x 240 = 61K 像素 (256 色)
- 60FPS → 每一幀必須在 ~10K 條指令內(nèi)完成
- 如何在有限的 CPU 運(yùn)算力下實(shí)現(xiàn) 60Hz?
既然能做一個只專注于 memcpy 的硬件DMA,為什么不能做一個畫圖的硬件?在輸出圖形時,可以認(rèn)為對每個像素點(diǎn)做的是相同的計(jì)算指令,不同的數(shù)據(jù)的計(jì)算,因此,可以認(rèn)為是一種并行計(jì)算。因此,我們也可以用一個專注于處理并行計(jì)算的非通用功能的處理器:GPU,只用來處理圖形相關(guān)的這類并行計(jì)算。
現(xiàn)代GPU:一個通用計(jì)算設(shè)備
一個完整的眾核多處理器系統(tǒng)
- 注重大量并行相似的任務(wù)
- 程序使用例如 OpenGL, CUDA, OpenCL, … 書寫
- 程序保存在內(nèi)存 (顯存) 中
- nvcc: 把 main 編譯/鏈接成 ELF; kernel 編譯成 GPU 指令
- 數(shù)據(jù)也保存在內(nèi)存 (顯存) 中
- 可以輸出到視頻接口 (DP, HDMI, …)
- 也可以通過 DMA 傳回系統(tǒng)內(nèi)存
gpgpu:通用圖形處理器
通過對處理圖形顯示這類并行計(jì)算任務(wù)的推廣,出現(xiàn)了通用圖形處理器。它們不再只關(guān)注圖形處理,而是可以針對更廣范圍的并行計(jì)算,比如神經(jīng)網(wǎng)絡(luò)的計(jì)算、矩陣計(jì)算。
通用圖形處理器(General-purpose computing on graphics processing units,簡稱GPGPU),是一種利用處理圖形任務(wù)的圖形處理器來計(jì)算原本由中央處理器處理的通用計(jì)算任務(wù)。這些通用計(jì)算常常與圖形處理沒有任何關(guān)系。由于現(xiàn)代圖形處理器強(qiáng)大的并行處理能力和可編程流水線,令流處理器可以處理非圖形數(shù)據(jù)。特別在面對單指令流多數(shù)據(jù)流(SIMD),且數(shù)據(jù)處理的運(yùn)算量遠(yuǎn)大于數(shù)據(jù)調(diào)度和傳輸?shù)男枰獣r,通用圖形處理器在性能上大大超越了傳統(tǒng)的中央處理器應(yīng)用程序。
I/O設(shè)備的抽象
I/O設(shè)備是操作系統(tǒng)中的對象(文件)
無論何種 I/O 設(shè)備,都是可以讀 (read) 寫 (write) 的字節(jié)序列 (流或數(shù)組)。
I/O設(shè)備應(yīng)該是操作系統(tǒng)中怎樣的對象?操作系統(tǒng)最簡單,或者說最不負(fù)責(zé)任的做法,當(dāng)然是可以直接將IO設(shè)備的寄存器,或者PCI總線的控制寄存器,暴露給應(yīng)用程序。微內(nèi)核的操作系統(tǒng)就是這么做的,微內(nèi)核的系統(tǒng)只做最少、最必要的部分。
但如果操作系統(tǒng)再負(fù)責(zé)一點(diǎn),將我們的每個IO設(shè)備(比如鍵盤、磁盤等)都抽象成操作系統(tǒng)中的一個對象。
操作系統(tǒng):設(shè)備 = 支持以下三種操作的對象 (文件)
- read: 從設(shè)備某個指定的位置讀出數(shù)據(jù)
- write: 向設(shè)備某個指定位置寫入數(shù)據(jù)
- ioctl: 讀取/設(shè)置設(shè)備的狀態(tài)
而以上這三種操作,恰恰就是文件系統(tǒng)中,一個文件應(yīng)該支持的操作。 Everything is a file !
設(shè)備驅(qū)動程序:實(shí)現(xiàn)抽象
設(shè)備驅(qū)動程序把對設(shè)備的 read/write/ioctl 系統(tǒng)調(diào)用 “翻譯” 成設(shè)備的寄存器命令序列。
以 “面向?qū)ο蟆?的方式訪問 I/O 設(shè)備,設(shè)備 = 支持 read, write, ioctl, … 功能的對象。
例子:/dev 中的對象
- /dev/pts/[x] - pseudo terminal
- /dev/zero - “零” 設(shè)備
- /dev/null - “null” 設(shè)備,一般不想要的輸出可以直接重定位到這個設(shè)備。
- /dev/random,/dev/urandom- 隨機(jī)數(shù)生成器
未必一定要有物理設(shè)備
設(shè)備驅(qū)動程序:將設(shè)備抽象為一個對象和操作,未必一定要有物理設(shè)備,比如/dev/null, /dev/urandom。就是操作系統(tǒng)中不需要物理設(shè)備的一種設(shè)備/文件/對象的抽象。
試一試
試一試執(zhí)行命令:head -c 512 [device] | xxd,并觀察它們的strace來查看訪問設(shè)備的系統(tǒng)調(diào)用。
tty查看當(dāng)前終端,輸出/dev/pts/3,即當(dāng)前終端是3號終端。
如果我們再打開一個終端并向之前的3號終端輸出,是可以直接出現(xiàn)在3號終端上的:echo Hello > /dev/pts/3。
也就是說,每個打開的終端也被看做是在設(shè)備目錄/dev下的一個設(shè)備。
存儲設(shè)備的抽象
磁盤 (存儲設(shè)備) 的訪問特性與其他的設(shè)備的讀寫略有不同,是以塊為單位的。
- 訪問者通常是文件系統(tǒng) (維護(hù)磁盤上的數(shù)據(jù)結(jié)構(gòu))
- 大量并發(fā)的訪問 (操作系統(tǒng)中的進(jìn)程都要訪問文件系統(tǒng))
對比一下終端和 GPU,的確是很不一樣的設(shè)備
- 終端:小數(shù)據(jù)量、直接流式傳輸
- GPU:大數(shù)據(jù)量、DMA 傳輸
總結(jié)
本次課內(nèi)容與目標(biāo)
- 理解 “什么是 I/O 設(shè)備”
- 終端、鍵盤、鼠標(biāo)、總線、DMA、GPU……
- 理解 “I/O 設(shè)備在操作系統(tǒng)中的抽象”
- 可以讀/寫/控制的對象
Takeaway messages
- 如果你 “自己造一臺計(jì)算機(jī)”,你會發(fā)現(xiàn)這一切都是自然的
- “不容易理解” 的部分是隨時間積累的復(fù)雜性
總結(jié)
以上是生活随笔為你收集整理的(2021) 23 [持久化] I/O设备与驱动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国发生过多少次对外战争?
- 下一篇: 马卡洛夫手枪改装的PB消音手枪