[轉載請註明出處] http://kezeodsnx.pixnet.net/blog
作者: kezeodsnx
前情提要
如前述,udev的功能是動態的對/dev/下的device node做增減的動作。動態增減是為了解決以往/dev下有太多沒用到的device node,而devfs又有一些"先天的缺陷":
1. 屬於kernel space
2. 存在一些可解和解不了的bug
3. 作者消失,維護者也不見了
4....
在種種原因下,udev取代了devfs。當然 devfs也不是沒有優點,如比較小,比較快(速度),比較省(memory),但anyway,過去就讓它過去了。
來吧
基本上,rule是用來在某些條件成立後,執行指令,包括增減device node,並執行外部程式等。比如說,在插入usb disk時,建立/dev/sdb,並mount起來,然後彈出檔案總管。大致條列如下:
1. 將kernel預設的device node改個名字,如把/dev/sdb改成 /dev/apacer-8G (by NAME)
2. 為預設device node建立symbolic link (by SYMLINK)
3. 更彈性的用法,用外部程式來替device node命名 (by PROGRAM)
4. 改變device node的permission和ownership (by GROUP and MODE)
5. device node增減時,執行shell script (by RUN)
時機
通常改變rule是為了改變系統預設的行為,因此,會希望在預設動作前,先完成自己想要做的事。而udev是根據字母排序來parse rule。在rule裡,#開頭的是註解,且每條rule都要在同一行,每條非空行都被視為一條rule。同一個device可以match多條rule,udev會apply每條match的rule。
Syntax
每條rule是由一串(key, value)組成,由comma(“,”)區隔。兩種key:
1. match key(==): 用來識別device,並決定是否要執行該條rule
2. assignment key(=): 當所有的match key都成立,則執行assignment key指定的指令。
每條rule至少要有一個match key和assignment key。
比如說:
KERNEL==”sdb”, NAME=”usb_disk”
意思是如果kernel report sdb這個device,則建立usb_disk這個device node (就不會有sdb這個node了)。不過這是比較沒鑑別的rule,因為sdb會隨著插入的順序而assign給不同的usb disk,因此最好是使用可uniquely identified的match key,見下述。
常用的match key如下:
KERNEL: kernel所給的預設名字 (如,sda, lp等)
SUBSYSTEM: device所屬的subsystem,如net, block, usb (可由sysfs得知)
DRIVER: 該device所使用的driver
常用的assignment key如下:
NAME: 該device node的名字
SYMLINK: device node的另一個名字,用symbolic link。這個link可以有很多個,所以可以用+=來做assignmet。
PROGRAM: 指定所需執行的外部程式,其stdout可用來指定device node的名字,用%c表示。若不止一個output,也可用%c{1},%c{2}或%c{2+}等表示式。
RUN: 在某些rule成立時,再多執行另一個外部程式。這和PROGRAM有點像,只是PROGRAM是用來產生device node的名字。
正規表示法
match key也有類似正規表示法的東西:
*:match任一個字母,0個或多個
?:match一個字母
[]: match 括號內任一個字母,可以是某個range,如[a-p],[0-9],或是[!1-3]
例如:
KERNEL=”sd*| hd[a-e]”, NAEM=”block/%k”
字串代換
類似printf的表示法:
%k: kernel給的預設名字,如sdb1
%n:kernel給的device number,若%k是sdb1,則%n是1
%c:外部程式的stdout
特殊字元%與$若要拿來當match key,需寫為:%%和$$
Sysfs登場
以上都是解釋,接下來就是真正的應用了。每個device插入系統時,會export一大堆訊息給sysfs,因此在sysfs裡有許多可用的訊息,而且是可以在rule裡直接剪下貼上的,因此udev和sysfs的關係是相當的密切。先從sysfs裡看看有什麼可以挖的吧!
使用udevinfo來query sysfs:
先插入一個usb disk,看看kernel給了什麼預設的名字:
user@user-ubuntu:/etc/udev/rules.d$ cat /proc/partitions
major minor #blocks name
8 0 244198584 sda
8 1 78180291 sda1
8 2 1 sda2
8 5 158200056 sda5
8 6 7815591 sda6
7 0 10240000 loop0
8 16 7823360 sdb
8 17 7819328 sdb1
這樣可知道,是在sdb,而真正要mount的是sdb1,來查一下:
udevinfo -a -p /sys/block/sdb/sdb1 會看到如下的output (只挑其中兩段做例子):
looking at device '/devices/pci0000:00/0000:00:1d.7/usb4/4-3/4-3:1.0/….':
KERNEL=="sdb1"
SUBSYSTEM=="block"
DRIVER==""
ATTR{start}=="8064"
ATTR{size}=="15638656"
ATTR{stat}==" 78 2219 2765 164 4 ……."
looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4/4-3/4-3:1.0/…':
KERNELS=="52:0:0:0"
SUBSYSTEMS=="scsi"
DRIVERS=="sd"
ATTRS{device_blocked}=="0"
ATTRS{type}=="0"
ATTRS{scsi_level}=="0"
ATTRS{vendor}==" "
ATTRS{model}=="USB FLASH DRIVE "
ATTRS{rev}=="PMAP"
ATTRS{state}=="running"
ATTRS{timeout}=="30"
ATTRS{iocounterbits}=="32"
ATTRS{iorequest_cnt}=="0xe3"
ATTRS{iodone_cnt}=="0xe3"
其實這些資訊都是透過driver export給sysfs,而udevinfo只是透過sysfs裡所記錄的內容,整理過後程現出來有關這個device的屬性。而這些屬性都是可以用來做match key。還有一個原則是每段的屬性是不能交互寫在同一個rule。比如說:
KERNEL==”sdb1”, ATTR{size}=="15638656", NAME=”apacer-disk”
SUBSYSTEMS=="scsi", ATTRS{model}=="USB FLASH DRIVE ", NAME=”apacer-disk”
這兩條rule都是對的,但不能這樣用:
KERNEL==”sdb1”, ATTR{size}=="15638656", ATTRS{model}=="USB FLASH DRIVE ", NAME=”apacer-disk”
Permission and ownership
這個比較直覺,就是設定而已
KERNEL==”sdb1”, ATTR{size}=="15638656", NAME=”apacer-disk”, GROUP=”user”, MODE=”0755”
結果如下:
brwxr-xr-x 1 root user 8, 33 2009-07-03 21:52 apacer-8g
Naming by External Program
比較複雜的情況下,可以存在一支命名程式,使用其stdout來當作device node的名字。在rule中,甚至可以傳參數給這支外部程式。其stdout用%c來表;
示,若有多組output,可用%c{1},%c{2}或%c{2+}這樣的表示方式。舉例如下
SUBSYSTEM=="block", ATTR{size}=="15638656", PROGRAM=”/usr/bin/usb-naming %k”, NAME=”%c”
%k是kernel預設的名字,usb-naming接收此名字後,output結果至stdout。
Run external program upon events
當收到某個udev event後,可再執行某個外部指令。這個跟PROGRAM類似,但不是做naming。比如說,在mount完usb後,做個記錄,可以這麼寫:
SUBSYSTEM=="block", ATTR{size}=="15638656", NAME=”apacer-8g”, RUN+=”/usr/bin/mount-log %k”。
特殊選項
all_partition: 意思大概是為block device建立所有可能的partition,而不是只建一開始偵測到的,不知道這個有什麼應用。
ignore_device:忽略這個event。這個還蠻好用。有些usb有不止一個partition,比如說一個是開機,一個是data。如果每次udev都把這兩個 partition mount起來,但是開機 那個永遠用不到,那就很煩。可用這個選項來忽略開機partition的event。比如:
SUBSYSTEM=="block", ATTR{size}=="1440", OPTIONS+=”ignore_device”
last_rule:對該device不再match之後的rule
Reference
留言列表