[轉載請註明出處] 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 nodepermissionownership (by GROUP and MODE)

5. device node增減時,執行shell script (by RUN)

 

時機

通常改變rule是為了改變系統預設的行為,因此,會希望在預設動作前,先完成自己想要做的事。而udev是根據字母排序來parse rule。在rule裡,#開頭的是註解,且每條rule都要在同一行,每條非空行都被視為一條rule。同一個device可以match多條ruleudevapply每條matchrule

 

Syntax

每條rule是由一串(key, value)組成,由comma(“,”)區隔。兩種key:

1. match key(==): 用來識別device,並決定是否要執行該條rule

2. assignment key(=): 當所有的match key都成立,則執行assignment key指定的指令。

每條rule至少要有一個match keyassignment key

比如說:

        KERNEL=”sdb”, NAME=”usb_disk”

意思是如果kernel report sdb這個device,則建立usb_disk這個device node (就不會有sdb這個node)。不過這是比較沒鑑別的rule,因為sdb會隨著插入的順序而assign給不同的usb disk,因此最好是使用可uniquely identifiedmatch 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,若%ksdb1,則%n1

%c:外部程式的stdout

特殊字元%$若要拿來當match key,需寫為:%%$$

 

Sysfs登場

以上都是解釋,接下來就是真正的應用了。每個device插入系統時,會export一大堆訊息給sysfs,因此在sysfs裡有許多可用的訊息,而且是可以在rule裡直接剪下貼上的,因此udevsysfs的關係是相當的密切。先從sysfs裡看看有什麼可以挖的吧!

使用udevinfoquery 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 exportsysfs,而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”

%kkernel預設的名字,usb-naming接收此名字後,output結果至stdout

 

Run external program upon events

當收到某個udev event後,可再執行某個外部指令。這個跟PROGRAM類似,但不是做naming。比如說,在mountusb後,做個記錄,可以這麼寫:

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起來,但是開機 那個永遠用不到,那就很煩。可用這個選項來忽略開機partitionevent。比如:

SUBSYSTEM=="block", ATTR{size}=="1440", OPTIONS+=”ignore_device”

last_rule:對該device不再match之後的rule

 

Reference

Writing udev rules

kezeodsnx 發表在 痞客邦 PIXNET 留言(0) 人氣()