device number 由 major number 和 minor number 組成[註1]。
major 號碼識別使用的驅動程式,例如 /dev/null 和 /dev/zero 使用 driver 1、virtual consoles 和 serial terminals 使用 driver 4、 vcs1 和 vcsa1 使用 driver 7[註1]。現代 Linux kernel 允許多個驅動程式共用同一個 major 號碼,但看到的大部分還是一個 major 對一個 driver。
minor 號碼由 major 號碼的驅動程式使用,來控制數個 device,kernel 本身只知道對應到某個驅動程式。kernel 用
minor 號碼決定確切是那個 device,取決於驅動程式寫法,device 可以直接用指標,或使用 minor number 對應。
kernel 內部用 dev_t 存 major 和 minor,在 Linux 2.6 是 32-bit,其中 major 12-bit、minor 20-bit (兩者在之前的 kernel 都只有 8-bit),但程式不能做這樣假設,應該用 <linux/kdev_t.h> 的 macros MAJOR(dev_t dev) 和 MINOR(dev_t dev) 分別取得,並用 MKDEV(int major, int minor) 轉換成 dev_t。
簡單來說就是:註冊時用 0 會動態指定,mknod 時再去 /proc/devices 查實際使用的 major number。
取得 device number
有些常見設備的 major number 是靜態指定的,可查 kernel 原始碼的 Documentation/devices.txt。新驅動程式可以自己選一個看起來沒正在使用的 device number,或者動態取得。如果驅動程式擴散使用,自選 device number 就不適合,比較適合動態取得。動態取得的缺點是不能事先建立 device node,但通常不需要事先建立。
如果事先知道要用的 device number,用
int register_chrdev_region(
dev_t first,
unsigned int count,
char *name);
first 是範圍起始的 device number,其中 minor number 部份通常是 0,但非必要。count 是連續的數目,如果大的話可能會跨到下個 major 號碼,仍可以正常使用。name 是關聯的 device 名稱,出現在 /proc/devices 和 sysfs。成功回傳 0,失敗回傳負值。
不知道要用的 device number,則動態取得,用
int alloc_chrdev_region(
dev_t *dev,
unsigned int firstminor,
unsigned int count,
char *name);
此時,dev 只用在輸出,成功時放範圍起始的 device number。firstminor 是請求的第一個 minor number,通常為 0。
device number 不用了要釋出,通常在 module 的 cleanup 函式。
void unregister_chrdev_region(dev_t first, unsigned int count);
Linux 2.4 引入可選用的 device file system (devfs),但 ...。
不用 devfs 時,major 號碼由驅動程式登記時指定,出現在 /proc/devices。major 可設為 0 動態配置沒用的 major 號碼:
int register_chrdev(
unsigned int major,
const char *name,
struct file_operations *fops);
回傳動態配置的 major 號碼,0 使用指定 major 號碼成功,負值表示失敗。多於 256 driver?register_chrdev_region()?
再來要 mknod 建立 device node 時,去 /proc/devices 查使用的 major:
major=`awk "\\$2==\"$module\" {print \\$1}" /proc/devices`
mknod /dev/${device}0 c $major 0
在 /dev ,使用指定的驅動程式
註:register_chrdev_region() 和 alloc_chrdev_region() 在 kernel 內部都是呼叫 __register_chrdev_region() 註冊一個範圍 major:minor 給 char 驅動程式,如果 major 為 0 動態取得 major。alloc_chrdev_region() 強制動態取得 major 並回傳。register_chrdev_region() 則會無法得知動態取得的 major。另外 register_chrdev() 註冊一個 major 的整個 minor,除了 __register_chrdev_region() 外,還進行 cdev_alloc()、cdev_add()。
char devices
| name | major | minor | 說明 |
| /dev/mem | 1 | 1 | ? |
| /dev/kmem | 1 | 2 | ? |
| /dev/null | 1 | 3 | 沒輸出 |
| /dev/zero | 1 | 5 | 提供無限的 0 |
| /dev/random | 1 | 8 | ? |
| /dev/urandom | 1 | 9 | ? |
| /dev/ptypN | 2 | N | 舊式 BSD Pseudo-TTY 對,pty 是 master,tty 是 slave。第四個字母順序是 pqrstuvwxyzabcde,第五個字母是 0123456789abcdef。Unix98 用 major 128 和 136 以上,並使用 PTY
master multiplex (/dev/ptmx) 動態取得 PTY。 |
| /dev/ttypN | 3 | N |
| /dev/ttyN | 4 | N | virtual console |
| /dev/ttySN | 4 | 64+N | UART |
| /dev/tty | 5 | 0 | Current TTY device
|
| /dev/console | 5 | 1 | System Console
|
| /dev/ptmx | 5 | 2 | PTY master multiplex |
| /dev/psaux | 10 | 1 | PS/2 型式滑鼠 |
| /dev/rtc | 10 | 135 | Real Time Clock |
| /dev/input/eventN | 13 | 64+N | ? |
| /dev/fbN | 29 | N | frame buffer |
| /dev/ttyPN | 57 | N | Hayes ESP serial card |
| /dev/ppp | 108 | 0 | ? |
| /dev/ttySA | 204 | 5+N | ? |
| /dev/ttyAM | 204 | 16+N | ? |
| /dev/ttyCPM | 204 | 46+N | ? |
/dev/ttyAMA /dev/ttySAC | 204 | 64+N | ? |
| /dev/ttyPSC | 204 | 148+N | ? |
| /dev/ttyUL | 204 | 187+N | ? |
| /dev/ttymxc | 207 | 16 | ? |
block devices
| name | major | minor | 說明 |
| /dev/ram | 1 | 1 | ? |
| /dev/ramN | 1 | N | ? |
| /dev/loopN | 7 | N | ? |
參考
- LDD ch3
- https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
註
- char 或 block 設備透過 /dev 下類型為 c 或 b 的檔案存取,ls -l /dev 可列出並看到 major 及 minor 號碼出現在時間之前。