跟我一起寫udev規則(譯)

 目錄

介紹 
      關於本文檔
      更新歷史
概念
     術語: devfs, sysfs, nodes, etc.
     爲什麼?
     內置固定命名設計
編寫規則
     規則文件和語義
     規則語法
     基本規則
     sysfs匹配屬性
     設備級聯結構
     字符串替換
     字符串匹配
從sysfs中查找合適信息
     sysfs樹
     udevinfo
     其他方法
高級話題
     權限和所有權控制
     使用外部程序命名設備
     發生特定事件時運行外部程序
     環境交互
     另外選項
例子
     USB打印機
     USB相機
     USB硬盤
     USB讀卡器
     USB Palm導航儀
     CD/DVD驅動
     網卡
測試和調試
     讓你的規則跑起來
     udevtest
作者及聯繫方式


介紹
關於本文檔

udev面向2.6以上的linux內核在用戶空間提供動態的/dev下固定設備命名方案. 之前的/dev實現: devfs現在已被廢棄, udev成爲繼任者. udev vs devfs是一個敏感的談話內容,在進行比較之前你應該讀一下這個文檔(http://kernel.org/pub/linux/utils /kernel/hotplug/udev_vs_devfs).

幾年間你爲之使用udev規則的設備發生改變了,如同規則自身的彈性一樣. 在現代系統中udev爲系統外的類型設備提供了固定的命名方法, 避免了爲這些設備提供定製規則. 但是一些用戶仍然需要額外的定製級別.

本文檔假設你已經安裝了udev並使用缺省配置運行ok. 這通常通過你的linux發行版做到的.

本文檔不會覆蓋規則書寫的方方面面, 只集中介紹所有主要概念. 更多細節信息可以在udev的man頁中找到.

本文檔使用各種例子(一些完全是虛構的)來闡述觀點和概念. 不是所有語法都會顯式的在附帶文本中描述,請確信通過查看例子規則來獲取完整的理解.

更新歷史
(略)

概念
語義: devfs, sysfs, nodes等
僅僅是基本介紹,可能並不完全準確.
在典型的基於linux的系統中,/dev目錄用來存儲文件一樣的設備節點, 它們指向系統中特定的設備. 每一個節點指向系統的一部分(一個設備), 可能存在也可能不存在. 用戶空間應用程序可以使用這些設備節點跟系統硬件打交道,例如, X服務器"監聽"/dev/input/mice來根據用戶的鼠標移動來移動可視鼠標指針.
原來的/dev目錄僅僅在設備可能在系統中出現時產生,因此/dev目錄一般非常大. 隨之而來的devfs提供了一種易於管理的途徑(注意它僅僅在硬件插入到系統中時產生/dev)以及其他功能,但系統會出現無法容易修復的問題.

udev是一種新的管理/dev目錄的方法,它的設計清除了以前的/dev實現的一些問題並提供了魯棒的路徑向後兼容. 爲了創建並命名系統中相應的/dev設備結點,udev需要依賴於根據用戶提供的規則從sysfs中得到的匹配信息. 本文着重規則書寫的過程,udev相關的任務由用戶自己完成.

sysfs是2.6內核中一個新的文件系統, 它由內核管理,並導出當前系統中插入的設備基本信息. udev可使用這些信息創建對應的硬件設備結點. sysfs掛載在/sys下而且是可瀏覽的. 你可能很希望在使用udev之前刺探下存儲在那兒的有關文件. 本文中我將交替使用/sys和sysfs術語.

爲什麼?
udev規則具有彈性非常強大,這裏是一些你使用規則可以達到的結果:
1. 重命名設備節點的缺省名字爲其他名字
2. 通過創建符號鏈接到缺省設備節點來提供一個可選的固定的設備節點名字
3. 基於程序的輸出命名設備節點
4. 改變設備節點的權限和所有權
5. 但設備節點被創建或刪除時(通常是添加設備或拔出設備時)執行一個腳本
6. 重命名網絡接口

當存在的特定設備沒有設備節點時,這不是書寫規則的工作範圍. 即使沒有匹配的規則,udev也會利用內核提供的缺省名字來創建設備節點.

擁有固定命名設備節點有很多好處. 假設你有兩個USB存儲設備:一個數碼相機,一個是USB閃存盤. 這些設備通過被賦予/dev/sda和/dev/sdb設備節點,準確的賦值取決於它們連接到系統的順序. 這可能爲一些用戶造成麻煩,如果每個設備每次都可以固定命名,比如/dev/camera和/dev/flashdisk,用戶就會獲益.

內置固定命名方法
udev爲系統外的一些設備類型提供了固定命名,這是一個很有用的特徵,在某些情況下意味着你不用書寫任何規則.

udev爲存儲設備在/dev/disk目錄下提供了系統外命名方法. 要查看它爲你的存儲硬件創建的固定命名,你可以使用下列命名:
#ls -lR /dev/disk
所有存儲類型都可以這麼用. 例如udev爲我的根分區創建了固定命名鏈接:/dev/disk/by-id/scsi-SATA_ST3120827AS_4MS1NDXZ- part3. 但我插入我的USB閃存盤udev就會創建另外一個固定命名節點:/dev/disk/by-id/usb- Prolific_Technology_Inc._USB_Mass_Storage_Device-part1.

規則書寫
規則文件和語義
爲決定如何命名設備以及執行什麼另外動作,udev會讀取一系列規則文件. 這些文件保存在/etc/udev/rules.d目錄下並且都必須有.rules後綴名.

缺省udev規則存儲在/etc/udev/rules.d/50-udev.rules裏面. 你可能發現整個文件很有意思, 它包含了少量例子,一些缺省規則提供了devfs風格的/dev佈局, 但是你不應該直接在這個文件裏面書寫規則.

/etc/udev/rules.d/下面的文件通過lexical順序解析,在某些情況下規則的解析順序很重要. 通常來說你希望你的規則可以在缺省規則之前解析, 所以我建議你創建一個文件/etc/udev/rules.d/10-local.rules並把自己的所有規則寫到這裏面去.

在一個規則文件中, 以"#"開頭的行被認爲是註釋. 每一個非空的行都是一條規則. 規則不能跨越多行.

一個設備可以被多條規則匹配到, 這有着很實用的優點, 例如, 我們可以寫兩個匹配同一個設備的規則, 每一個規則爲設備提供了它自己的可選命名. 即使分開在不同的文件種, 兩個可選命名也都會被創建, 要明白udev在找到一個匹配規則後不會停止處理其他規則, 它仍然會繼續查找並嘗試應用已知的每條規則, 這很重要.

規則語法
每條規則通過一系列鍵值對創建,這些鍵值對通過逗號分隔. 匹配鍵是用來識別要應用規則的設備的條件, 但規則中對應設備的所有匹配鍵被處理後,就會應用規則並且賦值鍵的行爲也會觸發. 每條規則應該包含至少一個匹配鍵和至少一個賦值鍵.

這是用來闡述上面內容的一個例子規則:
  KERNEL=="hdb",NAME="my_spare_disk"
上述規則包含一個匹配鍵(KERNEL)以及一個賦值鍵(NAME). 這些鍵和它們的屬性的語義將在稍後具體說明. 注意到匹配鍵通過連等號(==)與它的值聯繫起來, 賦值鍵通過等號(=)與它的值關聯.

注意udev不支持任何形式的行連接符, 不要在你的規則種插入任何斷行符,這將會導致udev把你的一條規則看做是多條規則但不會按預料工作.

基本規則
udev提供一些用來書寫精確匹配規則的匹配鍵, 其中一些常用鍵將在下面介紹, 其他將在文檔的後面說明. 要得到完整列表可以查看udev的手冊頁.
KERNEL - 爲設備匹配的內核名字
SUBSYSTEM - 匹配設備的子系統
DRIVER - 匹配支持設備的驅動名稱
在你使用一系列匹配鍵來準確匹配設備後,udev通過賦值鍵爲接下來發生的事給你提供更好的控制. 你可以查看udev的手冊頁查看完整的賦值鍵列表. 最基本的賦值鍵在下面說明, 其他的將在文檔結束時說明.
NAME - 設備節點應該使用的名字
SYMLINK - 一個設備節點可選名字的符號鏈接列表
正如之前所說,udev會爲設備創建一個真正的設備節點. 如果你希望爲設備節點提供可選名字,你得通過SYMLINK使用符號鏈接功能, 實際上是維護一個符號鏈接列表,這些符號鏈接都會指向真實的設備節點. 爲了維護這些鏈接我們介紹一個新的附加操作符: +=. 你可以在一個規則中附加多個符號鏈接到列表中,每個鏈接通過空格分開.
 KERNEL=="hdb", NAME="my_spare_disk"
上面規則意思是:匹配一個設備命名爲hdb的設備,把它重新命名爲my_spare_disk. 設備節點出現在/dev/my_spare_disk.
 KERNEL=="hdb", DRIVER=="ide-disk", SYMLINK+="sparedisk"
上面規則意思是:匹配一個內核命名爲hdb以及驅動爲ide-disk的設備,命名設備節點爲缺省名字並創建一個指向它的sparedisk符號鏈接。注 意到我們沒有指明設備節點名字,於是udev使用缺省名字。 爲了保留標準/dev佈局,你自己的規則通常沒有NAME但會創建一些SYMLINK並/或執行其他賦值操作.
 KERNEL=="hdc", SYMLINK+="cdrom cdrom0"
上面規則很可能就是你要寫的典型規則。 它在/dev/cdrom和/dev/cdrom0創建了兩個符號鏈接,都指向/dev/hdc. 再一次地,沒有NAME賦值鍵,所以使用缺省的內核名字(hdc).

匹配sysfs屬性
到目前爲止介紹的匹配鍵僅僅提供了有限的匹配能力. 實際上我們需要更加優良的控制:我們想基於設備的高級屬性來識別設備, 如供應商編碼, 產品編號, 序列號, 存儲能力, 分區數等等.

一些驅動導出這些信息到sysfs, udev允許我們使用ATTR鍵通過稍捎不同的語法來合併sysfs匹配到自己的規則中.

這裏有一個匹配sysfs中單個屬性例子. 更多細節將在稍後的幫助你基於sysfs屬性書寫規則的文檔中提供. 
 SUBSYSTEM=="block", ATTR{size}=="234441648", SYMLINK+="my_disk"
 
設備級聯
linux內核實際上以樹狀結構展示設備, 這個信息通過sysfs顯露出來,在書寫規則時這非常有用. 例如我的硬盤設備的展示是一個SCSI磁盤設備的孩子, 這個SCSI磁盤設備又是一個ATA控制器設備的孩子, 該控制器又是PCI總線設備的孩子. 你很有可能發現你需要從一個討論中的設備的雙親那裏引用信息, 比如我的硬盤設備的序列號在設備級別並不暴露出來, 而是在SCSI磁盤級別通過它的直接雙親展現。

目前介紹的四個主要匹配鍵(KERNEL/SUBSYSTEM/DRIVER/ATTR)僅僅跟對應設備的值匹配, 並不跟雙親設備的值匹配. udev提供了在樹中向上查找的匹配鍵變量:
KERNELS - 爲設備匹配的內核名字,或任何雙親設備中的內核名
SUBSYSTEMS - 匹配設備的子系統名,或任何雙親設備中的子系統名
DRIVERS - 匹配支持設備的驅動名,或任何支持雙親設備的驅動名
ATTRS - 匹配設備的sysfs屬性,或任何雙親設備的sysfs屬性
由於在心裏要考慮到級聯結構,你可能感覺到規則書寫變的有點複雜了. 歇一會吧,有工具可以幫助我們的,稍微獻上.

字符串替換
但寫的規則潛在的要處理多個相似的設備時, udev的printf-like string substitution operators就非常有用了. 你可以在你的規則裏面的任何賦值裏面包含這些操作符, udev在它們執行時會計算.

最常用的操作符是%k和%n. %k計算設備的內核名, 例如設備的"sda3"將(缺省)出現在/dev/sda3. %n計算設備(存儲設備的分區號)的內核號碼, 例如"3"將被換成"/dev/sda3".

udev也提供了一些高級功能替換操作符. 在讀完本文剩下內容後可以查詢udev的手冊頁. 以上例子中的操作符也有一個可選的語法 - $kernel和$number. 因此如果你希望在規則中匹配字符%, 你必需寫成%%, 如果你希望匹配字符$, 你必須寫成$$.

爲闡述下字符串替換的概念,我們來展示幾個例子:
 KERNEL=="mice", NAME="input/%k"
 KERNEL=="loop0", NAME="loop/%n", SYMLINK+="%k"

第一條規則確保鼠標設備節點一定出現在/dev/input目錄下(缺省是在/dev/mice下面). 第二條規則確保名字爲loop0的設備節點在/dev/loop/0創建,也會照常創建一個符號鏈接/dev/loop0.

上面規則的使用都比較可疑,因爲他們都可以通過不使用任何替換操作符來重寫. 這些替換的真正威力將會在下一節顯現.

字符串匹配
不僅有字符串精確匹配, udev也允許你使用shell風格的模式匹配. 支持的3種模式爲:
* - 匹配任何字符, 匹配0次或多次
? - 匹配任何字符,但只匹配一次.
[] - 匹配任何單個字符, 這些字符在方括號裏面指定, 範圍是受限的.
這裏有一些例子, 注意字符串替換符的使用:
 KERNEL=="fd[0-9]*", NAME="floppy/%n", SYMLINK+="%k"
 KERNEL=="hiddev*", NAME="usb/%k"

第一條規則匹配所有軟盤驅動並確保設備節點放置在/dev/floppy目錄下, 也創建一個缺省名字的符號鏈接. 第二條規則確保hiddev設備節點放在/dev/usb目錄下面.

從sysfs中查找信息
sysfs樹
從sysfs中獲取有意思信息的概念在之前的例子中已經觸摸到了. 爲了基於這些信息書寫規則,你首先需要知道屬性名和他們的當前值.

sysfs實際上是一個非常簡單的結構,邏輯上以目錄形式區分. 每一個目錄包含一定量的文件(屬性),這些文件往往都僅僅包含一個值. 也會有一些符號鏈接,它們把設備鏈到雙親那裏, 級聯結構已在上面說明了. 

一些目錄被引向頂層設備路徑,這些目錄展示了擁有對應設備節點的實際設備. 頂層設備路徑可以被分類爲包含dev文件的sysfs目錄, 下列命令可以列舉出這些文件:
#find /sys -name dev
例如, 在我的系統中, /sys/block/sda目錄就是我的硬盤設備路徑, 它通過/sys/block/sda/device符號鏈接鏈向它的雙親SCSI磁盤設備.

但你書寫基於sysfs信息的規則時, 只是簡單的匹配鏈條上一部分文件的屬性內容. 例如, 我可以這樣讀我的硬盤的大小:
#cat /sys/block/sda/size
234441648

在一個udev規則裏面, 我可以使用ATTR{size}=="234441648"來識別這個磁盤. 因爲udev反覆遍歷設備鏈的入口,我可以通過ATTRS匹配另外鏈中的屬性(如:/sys/class/block/sda/device屬性), 但是在處理鏈中不同部分時會有一些告誡, 稍後描述.

雖然這對於關於sysfs的結構和udev如何匹配值的介紹很有用, 但對sysfs的全面梳理是個既耗時也沒必要的事.

udevinfo
敲入udevinfo大概就是你用來創建規則的最直接的工具了。你需要知道的全部就是設備的sysfs設備路徑. 下面是一個精簡的例子:
udevinfo -a -p /sys/block/sda

  looking at device '/block/sda':
  KERNEL=="sda"
  SUBSYSTEM=="block"
  ATTR{stat}==" 128535 2246 2788977 766188 73998 317300 3132216 5735004 0 516516 6503316"
  ATTR{size}=="234441648"
  ATTR{removable}=="0"
  ATTR{range}=="16"
  ATTR{dev}=="8:0"

  looking at parent device '/devices/pci0000:00/0000:00:07.0/host0/target0:0:0/0:0:0:0':
  KERNELS=="0:0:0:0"
  SUBSYSTEMS=="scsi"
  DRIVERS=="sd"
  ATTRS{ioerr_cnt}=="0×0"
  ATTRS{iodone_cnt}=="0×31737"
  ATTRS{iorequest_cnt}=="0×31737"
  ATTRS{iocounterbits}=="32"
  ATTRS{timeout}=="30"
  ATTRS{state}=="running"
  ATTRS{rev}=="3.42"
  ATTRS{model}=="ST3120827AS "
  ATTRS{vendor}=="ATA "
  ATTRS{scsi_level}=="6"
  ATTRS{type}=="0"
  ATTRS{queue_type}=="none"
  ATTRS{queue_depth}=="1"
  ATTRS{device_blocked}=="0"

  looking at parent device '/devices/pci0000:00/0000:00:07.0':
  KERNELS=="0000:00:07.0"
  SUBSYSTEMS=="pci"
  DRIVERS=="sata_nv"
  ATTRS{vendor}=="0×10de"
  ATTRS{device}=="0×037f"

正如你看到的, udevinfo簡單的產生一個你可以在udev規則中作爲匹配鍵的屬性列表. 從上面例子中我可以爲該設備產生下面兩條規則:
SUBSYSTEM=="block", ATTR{size}=="234441648", NAME="my_hard_disk"
SUBSYSTEM=="block", SUBSYSTEMS=="scsi", ATTRS{model}=="ST3120827AS", NAME="my_hard_disk"

你可能發現到例子中使用了顏色, 這是爲了闡述把設備屬性和單個雙親設備屬性並在一起是合法的, 你不能混合匹配多個雙親設備屬性,這樣你的規則不能工作. 例如,下列規則是不合理的,因爲它試圖匹配兩個雙親設備屬性:
SUBSYSTEM=="block", ATTRS{model}=="ST3120827AS", DRIVERS=="sata_nv", NAME="my_hard_disk"
通常有大量的屬性提供給你,你必需挑選其中一些來創建你的規則. 一般來講,你希望挑選那些能夠固定標誌你的設備並便於理解的屬性. 上例中我選取的是我的磁盤大小和它的模型號,而沒有使用沒有意義的諸如ATTRS{iodone_cnt}=="0×31737"數字.

仔細觀察下udevinfo的輸出級聯結構效果, 設備的綠色部分使用了標準匹配鍵如KERNEL和ATTR, 藍色部分和栗色部分使用了雙親遍歷變量如SUBSYSTEMS和ATTRS, 這就是爲什麼級聯結構的複雜性實際上是很容易處理的原因,只要確保使用udevinfo建議的準確值就行了.

另外一點需要指出的是udevinfo輸出的文本屬性之間用空格填充(例如上面的ST3120827AS)是很常見的. 在你的規則中你可以添加額外的空格,也可以像我那樣去掉.

使用udevinfo的唯一複雜之處在於要求你知道頂級設備路徑(例如上面例子中的/sys/block/sda), 這通常並不明顯. 但是, 因爲你通常是爲已經存在的設備節點寫規則, 你可以自己使用udevinfo查找設備路徑:
#udevinfo -a -p $(udevinfo -q path -n /dev/sda)

可選方法
雖然udevinfo差不多是列舉你要構建的規則的準確屬性的最直接方式, 但一些用戶仍然樂於使用其他工具, 諸如usbview的工具可以顯示類似的信息集, 這些信息也可以用在規則中.

高級話題
控制權限和所有權
udev允許你在規則中使用另外的賦值來控制每個設備的所有權和權限屬性.

GROUP賦值允許你定義哪個Unix組應該擁有設備節點. 這裏有一個例子規則, 它定義video組擁有framebuffer設備:
KERNEL=="fb[0-9]*", NAME="fb/%n", SYMLINK+="%k", GROUP="video"
OWNER鍵可能用處不大, 它允許你定義哪個Unix用戶應該具有設備節點的擁有權限. 假設有個臨時情況要你讓join擁有軟盤設備,你可以使用:
KERNEL="fd[0-9]*", OWNER="join"
udev缺省用Unix的0660權限(擁有者和組員擁有讀寫功能)創建設備節點. 需要的話你可以在特定設備的規則中使用包含MODE賦值鍵覆蓋這些缺省值. 作爲例子,下面的規則定義了inotify節點可以被每個人讀寫:
KERNEL=="inotify", NAME="misc/%k", SYMLINK+="%k", MODE="0666"

使用外部程序來命名設備
某些情況下你可能要求比udev標準規則提供的更多彈性, 這種情況下你可以請求udev運行一個程序並運用程序的標準輸出來提供設備命名.

要使用這個功能,你只需簡單的在PROGRAM賦值中指定要運行程序(以及任何闡述)的完整路徑, 然後在NAME/SYMLINK賦值中使用一些%c替換.

下列例子引用一個位於/bin/device_namer的虛構程序. device_namer帶一個表示內核名字的命令行參數, 基於內核名device_namer做一些魔幻變換接着產生一些輸出到普通stdout管道,這些輸出被分割爲很多小塊, 每一小塊是一個單詞,塊之間用一個空格分開.

在我們的第一個例子中, 我們假設device_namer輸出塊的數目, 每一個形成當前設備的一個符號鏈接(名字可選).
KERNEL=="hda", PROGRAM="/bin/device_namer %k", SYMLINK+="%c"
下一個例子假設device_namer輸出兩塊,第一塊是設備名字, 第二塊是另外的符號鏈接名字. 我們現在介紹%c[N]替換, 它引向輸出的第N塊:
KERNEL=="hda", PROGRAM="/bin/device_namer %k", NAME="%c{1}", SYMLINK+="%c{2}"
再下個例子假設device_namer輸出設備名的一部分, 後面跟着的是快數, 它將形成另外的符號鏈接. 我們現在介紹%c{N+}替換, 它將被計算爲塊N, N+1, N+2…直到輸出結束.
KERNEL=="hda", PROGRAM="/bin/device_namer %k", NAME="%c{1}", SYMLINK+="%c{2+}"
輸出塊也可在賦值鍵中使用,而不僅僅是NAME和SYMLINK中. 下面例子使用一個虛構程序來決定哪個Unix組擁有設備:
KERNEL=="hda", PROGRAM="/bin/who_owns_device %k", GROUP="%c"

特定事件發生時運行外部程序
另外一個書寫udev規則的原因是爲了在設備連接或者斷開時運行一個特定程序. 例如, 你可能想在你的數碼相機連到系統時執行一個腳本來自動下載相機裏面的所有照片.

不要把這個跟上述的PROGRAM功能弄混淆了, PROGRAM是用來運行產生設備名字(除此之外不應該做其他事情)的程序. 但這些程序執行時, 設備節點設備節點還沒有被創建, 所以對設備的任何形式的操作都是不可能的.

這裏介紹的功能允許你在設備節點到位後運行一個程序. 該程序可以作用在設備上, 但是它不準在時間週期外運行,因爲當程序運行時udev會正常中止. 這個限制的一個權宜之計是確保你的程序立即分離自身.

這裏有個展示RUN賦值的例子:
KERNEL=="sdb", RUN+="/usr/bin/my_program"
當/usr/bin/my_program執行時, udev環境的各部分可作爲環境變量可用,包括諸如SUBSYSTEM的鍵值. 你也可以使用ACTION環境變量來檢測設備是否連接或斷開, 它的值是"add"和"remove"其中的一個.

udev並不在任何激活終端中運行這些程序,也不再shell上下文中執行. 確信你的程序是被標記爲可執行的, 如果它是個shell腳本請確保它以適當的shabang開頭(比如: #!/bin/sh)也不要期望任何標準輸出出現在你的終端上.

環境交互
udev爲環境變量提供了一個ENV鍵, 即可用來匹配也可用來賦值.

在賦值情況下,你可以設置稍後匹配的環境變量. 你也可以設置環境變量,供通過上面提供的技巧觸發的任何外部程序使用. 一個虛構的設置環境變量的例子規則如下:
KERNEL=="fd0", SYMLINK+="floppy", ENV{some_var}="value"
在匹配情況下你可以確保規則僅僅依賴一個環境變量的值而運行. 注意udev看到的環境跟你在控制檯上得到的用戶環境不一樣. 一個虛構的涉及環境匹配的規則如下:
KERNEL=="fd0", ENV{an_env_var}=="yes", SYMLINK+="floppy"
上面規則僅僅在udev環境中的$an_env_var的值設爲"yes"時才創建/dev/floppy鏈接.

另外選項
另外一個有用的賦值是OPTIONS列表. 不多的可用的選項有:
all_partitions - 爲一個塊設備創建所有可能的分區, 而不是初始檢測到的那些分區.
ignore_device - 完全忽略事件.
last_rule - 確保後面的所有規則不會有效.
例如, 下面規則設置我的硬盤節點的組所有權,並且後面的規則對它沒有任何效果:
KERNEL=="sda", GROUP="disk", OPTIONS+="last_rule"

例子
USB打印機
我啓動我的打印機, 它就被賦予了一個設備節點/dev/lp0. 我對這樣的單調的名字不滿意並打算使用udevinfo幫我寫一個規則來提供一個可選名字:
# udevinfo -a -p $(udevinfo -q path -n /dev/lp0)
  looking at device '/class/usb/lp0':
  KERNEL=="lp0"
  SUBSYSTEM=="usb"
  DRIVER==""
  ATTR{dev}=="180:0"

  looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb1/1-1':
  SUBSYSTEMS=="usb"
  ATTRS{manufacturer}=="EPSON"
  ATTRS{product}=="USB Printer"
  ATTRS{serial}=="L72010011070626380"

我的規則變成了這樣:
SUBSYSTEM=="usb", ATTRS{serial}=="L72010011070626380", SYMLINK+="epson_680"
   
USB相機
跟大多數相機一樣, 我的相機標識自己爲一個通過USB總線來連接通過SCSI傳輸的外部硬盤. 爲了訪問我的相片我先掛載驅動然後拷貝圖片文件到我的硬盤中.

不是所有相機都這樣工作, 其中一些使用非存儲協議,如gphoto2支持的相機. 在gphoto情況下,你不用爲你的設備寫任何規則,因爲純粹由用戶空間控制(而不是指定的內核驅動).

USB相機設備的一個通常的複雜性在於它們通常標識自己是一個單分區磁盤,即帶有/dev/sdb1的/dev/sdb. sdb節點對我毫無用處,但對sdb1有興趣 - 這纔是我要掛載的. 這裏有個麻煩因爲sysfs是鏈式的, udevinfo爲/dev/sdb1產生的有用屬性跟爲/dev/sdb產生的一樣, 這導致你的規則潛在的匹配到原始磁盤和分區, 這不是你想要的, 你的規則應該明確下.

爲解決這個問題,你需要簡單的考慮下sdb和sdb1之間有什麼區別. 令人驚奇的簡單: 只是名字上的區別, 所以我們可在NAME域上使用一個簡單的模式匹配.
# udevinfo -a -p $(udevinfo -q path -n /dev/sdb1)
  looking at device '/block/sdb/sdb1':
  KERNEL=="sdb1"
  SUBSYSTEM=="block"

  looking at parent device '/devices/pci0000:00/0000:00:02.1/usb1/1-1/1-1:1.0/host6/target6:0:0/6:0:0:0':
  KERNELS=="6:0:0:0"
  SUBSYSTEMS=="scsi"
  DRIVERS=="sd"
  ATTRS{rev}=="1.00"
  ATTRS{model}=="X250,D560Z,C350Z"
  ATTRS{vendor}=="OLYMPUS "
  ATTRS{scsi_level}=="3"
  ATTRS{type}=="0"

我的規則: 
KERNEL=="sd?1", SUBSYSTEMS=="scsi", ATTRS{model}=="X250,D560Z,C350Z", SYMLINK+="camera"

USB硬盤
USB硬盤跟USB相機差不多,但典型的使用方式不同. 在相機例子中, 我有講到我對sdb節點沒有興趣, 它僅僅在分區(比如使用fdisk)時纔有用, 但我爲什麼要爲我的相機分區呢?

當然如果你有一個100GB的USB硬盤, 你希望爲它分區是很好理解的, 這種情況下我們可以使用udev的字符串替換長處:
KERNEL=="sd*", SUBSYSTEM=="scsi", ATTRS{model}=="USB 2.0 Storage Device", SYMLINK+="usbhd%n"
這個規則創建諸如下面的符號鏈接:
/dev/usbhd - 可被fdisk使用的node
/dev/usbhd1 - 第一塊分區(可掛載)
/dev/usbhd2 - 第二塊分區(可掛載)

USB讀卡器
USB讀卡器(CompactFlash, SmartMedia等)屬於USB存儲設備的另外一種範圍, 它有不同的使用需求.

這些設備特別的在媒介改變時不用通知主機. 所以如果你插入沒有媒介的設備,接着插入一張卡, 計算機不會意識到這點, 你也不會有媒介的可掛載的sdb1分區節點.

一個可能的解決辦法是使用all_partttions選項優點, 它將爲每個規則匹配的塊設備創建16個分區節點:
KERNEL="sd*", SUBSYSTEM=="scsi", ATTRS{model}=="USB 2.0 CompactFlash Reader", SYMLINK+="cfrdr%n", OPTIONS+="all_partitions"
你將得到這些命名節點:cfrdr, cfrdr1, cfrdr2, cfrdr3, …, cfrdr15.

USB Palm導航儀
這些設備作爲USB串行設備工作, 所以缺省的你僅僅得到ttyUSB1設備節點. palm工具依賴於/dev/pilot, 一些用戶希望使用一條規則提供這個.

Carsten Clasohm的博客上有完整的源. Carsten的規則如下:
SUBSYSTEM=="usb", ATTRS{product}=="Palm Handheld", KERNEL=="ttyUSB*", SYMLINK+="pilot"
注意到產品字符串因產品不同而不同, 所以確保你檢查(使用udevinfo)了哪一個可以應用到你自己的產品.

CD/VCD驅動
我的電腦有兩個光驅: 一個DVD只讀驅動(hdc)和一個DVD刻錄機(hdd). 我不希望這些設備節點改變除非我物理性的重新接線, 但是一些用戶喜歡擁有諸如/dev/dvd這麼方便的設備節點.

我們都知道KERNEL是這些設備的名字, 規則書寫就很簡單了. 這是我的系統上一些規則:
SUBSYSTEM=="block", KERNEL=="hdc", SYMLINK+="dvd", GROUP="cdrom"
SUBSYSTEM=="block", KERNEL=="hdd", SYMLINK+="dvdrw", GROUP="cdrom"

網卡
儘管它們都是通過名字引用,網卡往往沒有與之關聯的設備節點. 儘管這樣,規則書寫過程還是相同的.

在規則中簡單的匹配網卡MAC地址是有意義的,因爲它們是唯一的. 但是, 確信你使用的是udevinfo顯示的準確MAC地址, 因爲如果你沒有精確匹配,你的規則不會工作.
# udevinfo -a -p /sys/class/net/eth0
  looking at class device '/sys/class/net/eth0':
  KERNEL=="eth0"
  ATTR{address}=="00:52:8b:d5:04:48"

這是我的規則:
KERNEL=="eth*", ATTR{address}=="00:52:8b:d5:04:48", NAME="lan"
爲了讓這個規則生效你得重啓網絡驅動, 你可以卸載並重新加載模塊或簡單的重啓系統即可. 你還得需要重新配置你的系統使用"lan"取代"eth0". 我以前遇到過這樣的麻煩(網卡不能重命名)直到我去除了所有對eth0的引用. 在此之後你應該可以在任何ifconfig或類似的工具的調用中使用"lan"而不是"eth0"了.

測試和調試
讓你的規則跑起來
假定你在一個有inotify支持的最近內核上工作, udev將自動監視你的規則目錄並且自動挑取你對規則文件的任何修改.

儘管這樣, udev也不會自動重新處理所有設備並試圖應用新規則. 例如, 如果你寫了個規則來爲你的已連接相機添加一個額外符號鏈接, 你不能指望這個符號鏈接可以馬上顯現出來.

爲使符號鏈接顯示, 你可以斷開並重連你的相機或者在非可移除設備情況下運行udevtrigger.

如果你的內核沒有inotify支持, 新規則不會自動被檢測到. 這種情況下你必需在做出更改後運行udevcontrol reload_rules使之生效.

udevtest
如果你知道sysfs中的頂級設備路徑, 你可以使用udevtest來顯示udev將要執行的動作, 這可能會幫你調試你的規則. 例如, 假設你想調試作用在/sys/class/sound/dsp上的規則:
# udevtest /class/sound/dsp
main: looking at device '/class/sound/dsp' from subsystem 'sound'
udev_rules_get_name: add symlink 'dsp'
udev_rules_get_name: rule applied, 'dsp' becomes 'sound/dsp'
udev_device_event: device '/class/sound/dsp' already known, remove possible symlinks
udev_node_add: creating device node '/dev/sound/dsp', major = '14', minor = '3', mode = '0660', uid = '0', gid = '18'
udev_node_add: creating symlink '/dev/dsp' to 'sound/dsp'

注意/sys前綴在udevtest命令行中被刪除了, 這是因爲udevtest在設備路徑上操作. 還要留意的是udevtest是一個純粹的測試/調試工具, 它不創建任何設備節點無論輸出怎麼建議.

作者以及聯繫方式
本文由Daniel Drake<[email protected]>寫就. 歡迎反饋信息.

對於技術支持你可以致信給linux-hotplug郵件列表: [email protected]

Copyright (C) 2003-2006 Daniel Drake.
This document is licensed under the GNU General Public License, Version 2.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章