--- title: LDD 3e 閱讀筆記 tags: security lang: zh_tw --- # LDD 3e 閱讀筆記 [TOC] # Intro - Linux Device Drivers 3rd Edition, O'REILLY 閱讀筆記 ![](https://pic.pimg.tw/liwecan/494b12cceed27.jpg) # Driver & Device 概念 1. Driver 跟 Device 的關係有點像是 Class 跟 Object 2. Driver 的流程如下 - 申請 Device numbers - 實作 File Operation, 像是 read, write 實際上怎麼做 - 申請 Device 編號 3. Linux 中**所有東西(Process, Device, Driver)都是檔案**, 或說, 都像是檔案 Device 會出現在 `/dev` 底下, 可以像是對待一個 File 般的對待此 Device 4. Driver 分成三種 - Char Driver 以 `ls -al /dev` 觀察會看到屬於此類 Driver 的 Device 的權限第一個字會是 `c` 例如以下 ``` crw-rw-rw- 1 root tty 5, 0 Mar 15 14:55 tty ``` - Block Driver 以 `ls -al /dev` 觀察會看到屬於此類 Driver 的 Device 的權限第一個字會是 `b` 例如以下 ``` brw-rw---- 1 root disk 8, 0 Mar 15 00:18 sda ``` - Network Driver ## Major/Minor Numbers 概念 一個 Device 會有 Major/Minor Numbers 以下是一小段 `ls -al /dev` 的輸出 ``` # ls -al /dev ... crw-rw-rw- 1 root root 5, 0 Mar 14 23:55 tty crw-rw-rw- 1 root root 1, 9 Mar 14 23:55 urandom crw-rw-rw- 1 root root 1, 5 Mar 14 23:55 zero ``` `tty` 是一個 **Device**, 由 **Driver** 5 管理 `urandom` `zero` 是兩個不同的 **Device**, 都由 **Driver** 1 管理, 可以再用 9 跟 5 這兩個 minor numbers 來區分這兩個 **Device** - Major Number: Driver 編號 - Minor Number: 用來分辨 Device 的編號 ## Important Data Structures ### File Operations ```c struct file_operations ``` - 定義於 `<linux/fs.h>` 這邊紀錄幾個 File Operations, 不會全部列出 - asynchronous function return 後, read/write 還沒完成 - `poll` `poll` 用來查詢 I/O 會不會暫停 若設定 `poll` 為 NULL, 表示此 device 不會被暫停 `poll` 相關 syscalls: - poll - epoll - select - `ioctl` 像是執行 device 自訂的行為, 而不僅僅只是 I/O - `mmap` 是要求把 device memory mapping 到 process's address space - 若 `open` 為 NULL, 則 `open` call 會成功, 但 driver 不會知道被 open - `flush` 發生在 close 此 device 的 fd 的複製品時, 極少 driver 使用到此 File Operation 若 `flush` 為 NULL, kernel 就只是 ignore 掉此呼叫, 而不是回傳任何錯誤代碼 - `release` 是發生在 close 此 device 的 fd 時, 跟 open 一樣可以是 NULL - File Operation `fsync` 是與 system call fsync 有關, 若此為 NULL, 則 system call fsync 會 return -EINVAL - `fasync` 是發生在 FASYNC flag 改變時 - `lock` 是 file 不能缺少的 File Operation, 但 driver 不用實作這個 - 若 char device 指定 File Operation `readdir` 會? ### The file Structure ```c struct file ``` - 定義於 `<linux/fs.h>` - 注意這個 struct 跟 C library 中的 `FILE` 無關 - C lib 中的 `FILE` 只出現在 user program - `<linux/fs.h>` 中的 `file` 只出現在 kernel - kernel 用此結構去代表一個 open file, 呼叫到某 file 的 file operations 時, 會將此代表此 file 的 **struct file 指標**傳進對應的 file operation 中 以下簡單介紹幾個欄位 - `f_mode` 此檔案的讀寫權限設定, 但你不需要在 File Operations `read` `write` 中添加判斷 `f_mode` 的 code, kernel 會先自行判斷可不可讀/寫後才呼叫 `read`/`write` - `f_pos` 目前讀/寫到的位置 - `f_op` 此為 `struct file_operations *`, 且可以 overwrite, 這就表示了可以達到**同屬於一個 Driver 的 Devices 們行為卻不同** ### The inode Structure - `inode` 是用來表示 file 的結構, 跟 `file` 不同, `file` 是用來表示已開啟的 file - 一個 file 可以被多重 open, 此時會有多個 `file` 結構都代表此 open file - 但只有一個 `inode` 結構代表此 file 以下簡單介紹幾個欄位 - `i_rdev` 表示實際的 device number - `i_cdev` 是一個 `struct cdev *`, `cdev` 用來表示 char devices 結構 # Char Device Driver 可以配合 [書中 scull 範例 code](https://github.com/starpos/scull/tree/master/scull) 觀賞 以下是寫 Char Device Driver 的幾個步驟 ## 申請 Device Numbers 使用 `register_chrdev_region()` 或 `alloc_chrdev_region()` 來取得可用的 device numbers - `register_chrdev_region()` - 是直接指定 device number, 但若指定的編號已經被占用, 就會失敗 - `alloc_chrdev_region()` - 則是交給 kernel 決定 device number 在範例 code 中就是 `main.c` 的 ```c dev_t dev = 0; /* * Get a range of minor numbers to work with, asking for a dynamic * major unless directed otherwise at load time. */ if (scull_major) { dev = MKDEV(scull_major, scull_minor); result = register_chrdev_region(dev, scull_nr_devs, "scull"); } else { result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev); } if (result < 0) { printk(KERN_WARNING "scull: can't get major %d\n", scull_major); return result; } ``` ## 實作 File Operations 將此 Char Device Driver 要實現的功能做好 e.g. `read` `write` `open` `release` 在範例 code 中就是 `main.c` 的 ```c struct file_operations scull_fops = { .owner = THIS_MODULE, .llseek = scull_llseek, .read = scull_read, .write = scull_write, .unlocked_ioctl = scull_ioctl, .open = scull_open, .release = scull_release, }; ``` 以及 `scull_llseek` `scull_read` ... 這些 function 的實作 這邊可以觀察到, 其實這就是物件導向的寫法 ## 註冊 Char Device 使用 `cdev_init()` `cdev_add()` 來向 kernel 註冊 Char Device 在範例 code 中就是 `main.c` 的 ```c /* * Set up the char_dev structure for this device. */ static void scull_setup_cdev(struct scull_dev *dev, int index) { int err, devno = MKDEV(scull_major, scull_minor + index); cdev_init(&dev->cdev, &scull_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &scull_fops; err = cdev_add (&dev->cdev, devno, 1); /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE "Error %d adding scull%d", err, index); } ``` ## 註銷 Char Device 最後如果此 Driver 要移除了, 就是跟創造的過程反過來 - 註銷 Char Device - 註銷 Device Numbers 使用 `cdev_del()` 來註銷 Char Device 範例 code ```c int i; dev_t devno = MKDEV(scull_major, scull_minor); /* Get rid of our char dev entries */ if (scull_devices) { for (i = 0; i < scull_nr_devs; i++) { scull_trim(scull_devices + i); cdev_del(&scull_devices[i].cdev); } kfree(scull_devices); } ``` ## 註銷 Device Numbers 使用 `unregister_chrdev_region()` 來註銷 Device Numbers 範例 code ```c /* cleanup_module is never called if registering failed */ unregister_chrdev_region(devno, scull_nr_devs); ``` # Remapping RAM - p.430