概述:
Linux 使用者常常會很難鑒別同一類型的裝置名稱,比如 eth0, eth1, sda, sdb 等等。通過觀察這些裝置的核心裝置名稱,使用者通常能知道這些是什麼類型的裝置,但是不知道哪一個裝置是他們想要的。例如,在一個充斥著本地磁碟和光纖磁碟的裝置名稱清單 (/dev/sd*
) 中,使用者無法找到一個序號為“35000c50000a7ef67”的磁碟。在這種情況下,udev 就能動態地在 /dev
目錄裡產生自己想要的、標識性強的裝置檔案或者裝置連結,以此協助使用者方便快捷地找到所需的裝置檔案。
udev 簡介
什麼是 udev?
udev 是 Linux2.6 核心裡的一個功能,它替代了原來的 devfs,成為當前 Linux 預設的裝置管理工具。udev 以守護進程的形式運行,通過偵聽核心發出來的 uevent 來管理 /dev
目錄下的裝置檔案。不像之前的裝置管理工具,udev 在使用者空間 (user space) 運行,而不在核心空間 (kernel space) 運行。
使用 udev 的好處:
我們都知道,所有的裝置在 Linux 裡都是以裝置檔案的形式存在。在早期的 Linux 版本中,/dev
目錄包含了所有可能出現的裝置的裝置檔案。很難想象 Linux 使用者如何在這些大量的裝置檔案中找到匹配條件的裝置檔案。現在 udev 只為那些串連到 Linux 作業系統的裝置產生裝置檔案。並且 udev 能通過定義一個 udev 規則 (rule) 來產生匹配裝置屬性的裝置檔案,這些裝置屬性可以是核心裝置名稱、匯流排路徑、廠商名稱、型號、序號或者磁碟大小等等。
- 動態管理:當裝置添加 / 刪除時,udev 的守護進程偵聽來自核心的 uevent,以此添加或者刪除
/dev
下的裝置檔案,所以 udev 只為已經串連的裝置產生裝置檔案,而不會在 /dev
下產生大量虛無的裝置檔案。
- 自訂命名規則:通過 Linux 預設的規則檔案,udev 在 /dev/ 裡為所有的裝置定義了核心裝置名稱,比如
/dev/sda、/dev/hda、/dev/fd
等等。由於 udev 是在使用者空間 (user space) 運行,Linux 使用者可以通過自訂的規則檔案,靈活地產生標識性強的裝置檔案名稱,比如 /dev/boot_disk、/dev/root_disk、/dev/color_printer
等等。
- 設定裝置的許可權和所有者 / 組:udev 可以按一定的條件來設定裝置檔案的許可權和裝置檔案所有者 / 組。在不同的 udev 版本中,實現的方法不同,在“如何配置和使用 udev”中會詳解。
下面的流程圖顯示 udev 添加 / 刪除裝置檔案的過程。
圖 1. udev 工作流程圖:
相關術語:
- 裝置檔案:由於本文以較通俗的方式講解 udev,所以裝置檔案是泛指在
/dev/
下,可被應用程式用來和裝置驅動互動的檔案。而不會特別地區分裝置檔案、裝置節點或者裝置特殊檔案。
- devfs:devfs是 Linux 早期的裝置管理工具,已經被 udev 取代。
- sysfs:sysfs是 Linux 2.6 核心裡的一個虛擬檔案系統
(/sys)
。它把裝置和驅動的資訊從核心的裝置模組匯出到使用者空間 (userspace)。從該檔案系統中,Linux 使用者可以擷取很多裝置的屬性。
- devpath:本文的 devpath是指一個裝置在 sysfs檔案系統
(/sys)
下的相對路徑,該路徑包含了該裝置的屬性檔案。udev 裡的多數命令都是針對 devpath操作的。例如:sda的 devpath是 /block/sda
,sda2 的 devpath是 /block/sda/sda2
。
- 核心裝置名稱:裝置在 sysfs裡的名稱,是 udev 預設使用的裝置檔案名稱。
回頁首
如何配置和使用 udev
下面會以 RHEL4.8 和 RHEL5.3 為平台,分別描述 udev 的配置和使用:
下載和安裝 udev
從 Fedora3 和 Red Hat Enterprise4 開始,udev 就是預設的裝置管理工具,無需另外下載安裝。
清單 1. 檢查 udev 在 RHEL4.8 裡的版本和運行情況
[root@HOST_RHEL4 dev]# rpm -qa |grep -i udev udev-039-10.29.el4 [root@HOST_RHEL4 ~]# uname -r 2.6.9-89.ELsmp [root@HOST_RHEL4 ~]# ps -ef |grep udev root 21826 1 0 Dec09 ? 00:00:00 udevd |
清單 2. 檢查 udev 在 RHEL5.3 裡的版本和運行情況
[root@HOST_RHEL5 ~]# rpm -qa |grep -i udev udev-095-14.19.el5 [root@HOST_RHEL5 sysconfig]# uname -r 2.6.18-128.el5 [root@HOST_RHEL5 sysconfig]# ps -ef|grep udev root 5466 1 0 18:32 ? 00:00:00 /sbin/udevd -d |
如果 Linux 使用者想更新 udev 包,可以從 http://www.kernel.org/pub/linux/utils/kernel/hotplug/下載並安裝。
udev 的設定檔 (/etc/udev/udev.conf)
清單 3. RHEL 4 . 8下 udev 的設定檔
[root@HOST_RHEL4 dev]# cat /etc/udev/udev.conf # udev.conf # The main config file for udev # # This file can be used to override some of udev's default values # for where it looks for files, and where it places device nodes. # # WARNING: changing any value, can cause serious system breakage! # # udev_root - where in the filesystem to place the device nodes udev_root="/dev/" # udev_db - The name and location of the udev database. udev_db="/dev/.udev.tdb" # udev_rules - The name and location of the udev rules file udev_rules="/etc/udev/rules.d/" # udev_permissions - The name and location of the udev permission file udev_permissions="/etc/udev/permissions.d/" # default_mode - set the default mode for all nodes that have no # explicit match in the permissions file default_mode="0600" # default_owner - set the default owner for all nodes that have no # explicit match in the permissions file default_owner="root" # default_group - set the default group for all nodes that have no # explicit match in the permissions file default_group="root" # udev_log - set to "yes" if you want logging, else "no" udev_log="no" |
Linux 使用者可以通過該檔案設定以下參數:
- udev_root:udev 產生的裝置所存放的目錄,預設值是
/dev/
。建議不要修改該參數,因為很多應用程式預設會從該目錄調用裝置檔案。
- udev_db:udev 資訊存放的資料庫或者所在目錄,預設值是
/dev/.udev.tdb
。
- udev_rules:udev 規則檔案的名字或者所在目錄,預設值是
/etc/udev/rules.d/
。
- udev_permissions:udev 許可權檔案的名字或者所在目錄,預設值是
/etc/udev/permissions.d/
。
- default_mode/ default_owner/ default_group:如果裝置檔案的許可權沒有在許可權檔案裡指定,就使用該參數作為預設許可權,預設值分別是:
0600/root/root
。
- udev_log:是否需要 syslog記錄 udev 日誌的開關,預設值是 no。
清單 4. RHEL5.3 下 udev 的設定檔
[root@HOST_RHEL5 ~]# cat /etc/udev/udev.conf # udev.conf # The initial syslog(3) priority: "err", "info", "debug" or its # numerical equivalent. For runtime debugging, the daemons internal # state can be changed with: "udevcontrol log_priority=<value>". udev_log="err" |
udev_log:syslog記錄日誌的層級,預設值是 err。如果改為 info 或者 debug 的話,會有冗長的 udev 日誌被記錄下來。
實際上在 RHEL5.3 裡,除了設定檔裡列出的參數 udev_log外,Linux 使用者還可以修改參數 udev_root和 udev_rules( 請參考上面的“RHEL4.8 的 udev 設定檔”),只不過這 2 個參數是不建議修改的,所以沒顯示在 udev.conf 裡。
可見該版本的 udev.conf 改動不小:syslog預設會記錄 udev 的日誌,Linux 使用者只能修改日誌的層級 (err、info、degub 等 );裝置的許可權不能在 udev.conf 裡設定,而是要在規則檔案 (*.rules) 裡設定。
通過 udev 設定裝置檔案的許可權
在 RHEL4.8 的 udev,裝置的許可權是通過許可權檔案來設定。
清單 5. RHEL4.8 下 udev 的許可權檔案
[root@HOST_RHEL4 ~]# cat /etc/udev/permissions.d/50-udev.permissions …… # disk devices hd*:root:disk:0660 sd*:root:disk:0660 dasd*:root:disk:0660 ataraid*:root:disk:0660 loop*:root:disk:0660 md*:root:disk:0660 ide/*/*/*/*/*:root:disk:0660 discs/*/*:root:disk:0660 loop/*:root:disk:0660 md/*:root:disk:0660 # tape devices ht*:root:disk:0660 nht*:root:disk:0660 pt[0-9]*:root:disk:0660 npt*:root:disk:0660 st*:root:disk:0660 nst*:root:disk:0660 …… |
RHEL4.8 裡 udev 的許可權檔案會為所有常用的裝置設定許可權和 ownership,如果有裝置沒有被許可權檔案設定許可權,udev 就按照 udev.conf 裡的預設許可權值為這些裝置設定許可權。由於篇幅的限制,只顯示了 udev 許可權檔案的一部分,該部分設 置了
所有可能串連上的磁碟裝置和磁帶裝置的許可權和 ownership。
而在 RHEL5.3 的 udev,已經沒有許可權檔案,所有的許可權都是通過規則檔案 (*.rules)
來設定,在下面的規則檔案配置過程會介紹到。
udev 的規則和規則檔案
規則檔案是 udev 裡最重要的部分,預設是存放在 /etc/udev/rules.d/
下。所有的規則檔案必須以“.rules
”為尾碼名。RHEL 有預設的規則檔案,這些預設規則檔案不僅為裝置產生核心裝置名稱,還會產生標識性強的符號連結。例如:
[root@HOST_RHEL5 ~]# ls /dev/disk/by-uuid/ 16afe28a-9da0-482d-93e8-1a9474e7245c |
但這些連結名較長,不易調用,所以通常需要自訂規則檔案,以此產生易用且標識性強的裝置檔案或符號連結。
此外,一些應用程式也會在 /dev/
下產生一些方便調用的符號連結。例如規則 40-multipath.rules 為磁碟產生下面的符號連結:
[root@ HOST_RHEL5 ~]# ls /dev/mpath/* /dev/mpath/mpath0 /dev/mpath/mpath0p1 /dev/mpath/mpath0p2 |
udev 按照規則檔案名稱的字母順序來查詢全部規則檔案,然後為匹配規則的裝置管理其裝置檔案或檔案連結。雖然 udev 不會因為一個裝置匹配了一條規則而停止解析後面的規則檔案,但是解析的順序仍然很重要。通常情況下,建議讓自己想要的規則檔案最先被解析。比如,建立一個名為 /etc/udev/rules.d/10-myrule.rules
的檔案,並把你的規則寫入該檔案,這樣 udev 就會在解析系統預設的規則檔案之前解析到你的檔案。
RHEL5.3 的 udev 規則檔案比 RHEL4.8 裡的更完善。受篇幅的限制,同時也為了不讓大家混淆,本文將不對 RHEL4.8 裡的規則檔案進行詳解,下面關於規則檔案的配置和執行個體都是在 RHEL5.3 上進行的。如果大家需要配置 RHEL4 的 udev 規則檔案,可以先參照下面 RHEL5.3 的配置過程,然後查詢 RHEL4 裡的使用者手冊 (man udev) 後進行配置。
在規則檔案裡,除了以“#”開頭的行(注釋),所有的非空行都被視為一條規則,但是一條規則不能擴充到多行。規則都是由多個 索引值對(key-value pairs)組成,並由逗號隔開,索引值對可以分為 條件匹配索引值對( 以下簡稱“匹配鍵 ”) 和 賦值索引值對( 以下簡稱“賦值鍵 ”),一條規則可以有多條匹配鍵和多條賦值鍵。匹配鍵是匹配一個裝置屬性的所有條件,當一個裝置的屬性匹配了該規則裡所有的匹配鍵,就認為這條規則生效,然後按照賦值鍵的內容,執行該規則的賦值。下面是一個簡單的規則:
清單 6. 簡單說明索引值對的例子
KERNEL=="sda", NAME="my_root_disk", MODE="0660" |
KERNEL 是匹配鍵,NAME 和 MODE 是賦值鍵。這條規則的意思是:如果有一個裝置的核心裝置名稱為 sda,則該條件生效,執行後面的賦值:在 /dev
下產生一個名為 my_root_disk
的裝置檔案,並把裝置檔案的許可權設為 0660。
通過這條簡單的規則,大家應該對 udev 規則有直觀的瞭解。但可能會產生疑惑,為什麼 KERNEL 是匹配鍵,而 NAME 和 MODE 是賦值鍵呢?這由中間的操作符 (operator) 決定。
僅當操作符是“==”或者“!=”時,其為匹配鍵;若為其他動作符時,都是賦值鍵。
- RHEL5.3 裡 udev 規則的所有操作符:
“==”:比較鍵、值,若等於,則該條件滿足;
“!=”: 比較鍵、值,若不等於,則該條件滿足;
“=”: 對一個鍵賦值;
“+=”:為一個表示多個條目的鍵賦值。
“:=”:對一個鍵賦值,並拒絕之後所有對該鍵的改動。目的是防止後面的規則檔案對該鍵賦值。
- RHEL5.3 裡 udev 規則的匹配鍵
ACTION: 事件 (uevent) 的行為,例如:add( 添加裝置 )、remove( 刪除裝置 )。
KERNEL: 核心裝置名稱,例如:sda, cdrom。
DEVPATH:裝置的 devpath 路徑。
SUBSYSTEM: 裝置的子系統名稱,例如:sda 的子系統為 block。
BUS: 裝置在 devpath 裡的匯流排名稱,例如:usb。
DRIVER: 裝置在 devpath 裡的裝置驅動名稱,例如:ide-cdrom。
ID: 裝置在 devpath 裡的識別號。
SYSFS{filename}: 裝置的 devpath 路徑下,裝置的屬性檔案“filename”裡的內容。
例如:SYSFS{model}==“ST936701SS”表示:如果裝置的型號為 ST936701SS,則該裝置匹配該 匹配鍵。
在一條規則中,可以設定最多五條 SYSFS 的 匹配鍵。
ENV{key}: 環境變數。在一條規則中,可以設定最多五條環境變數的 匹配鍵。
PROGRAM:調用外部命令。
RESULT: 外部命令 PROGRAM 的返回結果。例如:
PROGRAM=="/lib/udev/scsi_id -g -s $devpath", RESULT=="35000c50000a7ef67" |
調用外部命令 /lib/udev/scsi_id
查詢裝置的 SCSI ID,如果返回結果為 35000c50000a7ef67,則該裝置匹配該 匹配鍵。
- RHEL5.3 裡 udev 的重要賦值鍵
NAME:在 /dev
下產生的裝置檔案名稱。只有第一次對某個裝置的 NAME 的賦值行為生效,之後匹配的規則再對該裝置的 NAME 賦值行為將被忽略。如果沒有任何規則對裝置的 NAME 賦值,udev 將使用核心裝置名稱來產生裝置檔案。
SYMLINK:為 /dev/
下的裝置檔案產生符號連結。由於 udev 只能為某個裝置產生一個裝置檔案,所以為了不覆蓋系統預設的 udev 規則所產生的檔案,推薦使用符號連結。
OWNER, GROUP, MODE:為裝置設定許可權。
ENV{key}:匯入一個環境變數。
- RHEL5.3 裡 udev 的值和可調用的替換操作符
在索引值對中的鍵和操作符都介紹完了,最後是值 (value)。Linux 使用者可以隨意地定製 udev 規則檔案的值。例如:my_root_disk, my_printer
。同時也可以引用下面的替換操作符:
$kernel, %k:裝置的核心裝置名稱,例如:sda、cdrom。
$number, %n:裝置的核心號碼,例如:sda3 的核心號碼是 3。
$devpath, %p:裝置的 devpath路徑。
$id, %b:裝置在 devpath裡的 識別碼。
$sysfs{file}, %s{file}:裝置的 sysfs裡 file 的內容。其實就是裝置的屬性值。
例如:$sysfs{size} 表示該裝置 ( 磁碟 ) 的大小。
$env{key}, %E{key}:一個環境變數的值。
$major, %M:裝置的 major 號。
$minor %m:裝置的 minor 號。
$result, %c:PROGRAM 返回的結果。
$parent, %P:父裝置的裝置檔案名稱。
$root, %r:udev_root的值,預設是 /dev/
。
$tempnode, %N:臨時裝置名稱。
%%:符號 % 本身。
$$:符號 $ 本身。
清單 7. 說明替換操作符的規則例子
KERNEL=="sd*", PROGRAM="/lib/udev/scsi_id -g -s %p", \ RESULT=="35000c50000a7ef67", SYMLINK="%k_%c" |
該規則的執行:如果有一個核心裝置名稱以 sd 開頭,且 SCSI ID 為 35000c50000a7ef67
,則為裝置檔案產生一個符號連結“sda_35000c50000a7ef67”.
回頁首
制定 udev 規則和查詢裝置資訊的執行個體:
如何尋找裝置的資訊 ( 屬性 ) 來制定 udev 規則:
當我們為指定的裝置設定規則時,首先需要知道該裝置的屬性,比如裝置的序號、磁碟大小、廠商 ID、裝置路徑等等。通常我們可以通過以下的方法獲得:
- 查詢sysfs檔案系統:
前面介紹過,sysfs 裡包含了很多裝置和驅動的資訊。
例如:裝置 sda 的 SYSFS{size} 可以通過 cat /sys/block/sda/size
得到;SYSFS{model} 資訊可以通過 cat /sys/block/sda/device/model
得到。
- udevinfo命令:
udevinfo 可以查詢 udev 資料庫裡的裝置資訊。例如:用 udevinfo 查詢裝置 sda 的 model 和 size 資訊:
清單 8. 通過 udevinfo 查詢裝置屬性的例子
[root@HOST_RHEL5 rules.d]# udevinfo -a -p /block/sda | egrep "model|size" SYSFS{size}=="71096640" SYSFS{model}=="ST936701SS " |
- 其他外部命令:
清單 9. 通過 scsi_id 查詢磁碟的 SCSI_ID 的例子
[root@HOST_RHEL5 ~]# scsi_id -g -s /block/sda 35000c50000a7ef67 |
udev 的簡單規則:
清單 10. 產生網卡裝置檔案的規則
SUBSYSTEM=="net", SYSFS{address}=="AA:BB:CC:DD:EE:FF", NAME="public_NIC" |
該規則表示:如果存在裝置的子系統為 net,並且地址 (MAC address) 為“AA:BB:CC:DD:EE:FF”,為該裝置產生一個名為 public_NIC 的裝置檔案。
清單 11. 為指定大小的磁碟產生符號連結的規則
SUBSYSTEM=="block", SYSFS{size}=="71096640", SYMLINK ="my_disk" |
該規則表示:如果存在裝置的子系統為 block,並且大小為 71096640(block),則為該裝置的裝置檔案名稱產生一個名為 my_disk 的符號連結。
清單 12. 通過外部命令為指定序號的磁碟產生裝置檔案的規則
KERNEL=="sd*[0-9]", PROGRAM=="/lib/udev/scsi_id -g -s %p", \ RESULT=="35000c50000a7ef67", NAME +="root_disk%n" |
該規則表示:如果存在裝置的核心裝置名稱是以 sd 開頭 ( 磁碟裝置 ),以數字結尾 ( 磁碟分割 ),並且通過外部命令查詢該裝置的 SCSI_識別碼為“35000c50000a7ef67”,則產生一個以 root_disk 開頭,核心號碼結尾的裝置檔案,並替換原來的裝置檔案(如果存在的話)。例如:產生裝置名稱 /dev/root_disk2
,替換原來的裝置名稱 /dev/sda2
。
運用這條規則,可以在 /etc/fstab
裡保持系統磁碟分割名稱的一致性,而不會受驅動載入順序或者磁碟標籤被破壞的影響,導致作業系統啟動時找不到系統磁碟分割。
其他常用的 udev 命令:
- udevtest:
udevtest
會針對一個裝置,在不需要 uevent 觸發的情況下類比一次 udev
的運行,並輸出查詢規則檔案的過程、所執行的行為、規則檔案的執行結果。通常使用 udevtest
來調試規則檔案。以下是一個針對裝置 sda 的 udevtest
例子。由於 udevtest
是掃描所有的規則檔案 ( 包括系統內建的規則檔案 ),所以會產生冗長的輸出。為了讓讀者清楚地瞭解udevtest
,本例只在規則目錄裡保留一條規則:
清單 13. 為 udevtest 保留的規則
KERNEL=="sd*", PROGRAM="/lib/udev/scsi_id -g -s %p", RESULT=="35000c50000a7ef67", \ NAME="root_disk%n", SYMLINK="symlink_root_disk%n" |
清單 14. udevtest 的執行過程
[root@HOST_RHEL5 rules.d]# udevtest /block/sda main: looking at device '/block/sda' from subsystem 'block' run_program: '/lib/udev/scsi_id -g -s /block/sda' run_program: '/lib/udev/scsi_id' (stdout) '35000c50000a7ef67' run_program: '/lib/udev/scsi_id' returned with status 0 udev_rules_get_name: reset symlink list udev_rules_get_name: add symlink 'symlink_root_disk' udev_rules_get_name: rule applied, 'sda' becomes 'root_disk' udev_device_event: device '/block/sda' already in database, \ validate currently present symlinks udev_node_add: creating device node '/dev/root_disk', major = '8', \ minor = '0', mode = '0660', uid = '0', gid = '0' udev_node_add: creating symlink '/dev/symlink_root_disk' to 'root_disk' |
可以看出,udevtest
對 sda 執行了外部命令 scsi_id
, 得到的 stdout 和規則檔案裡的 RESULT 匹配,所以該規則匹配。然後 ( 類比 ) 產生裝置檔案 /dev/root_disk
和符號連結 /dev/symlink_root_disk
,並為其設定許可權。
- start_udev:
start
_dev
命令重啟 udev
守護進程,並對所有的裝置重新查詢規則目錄下所有的規則檔案,然後執行所匹配的規則裡的行為。通常使用該命令讓新的規則檔案立即生效:
清單 15. start_udev 的執行過程
[root@HOST_RHEL5 rules.d]# start_udev Starting udev: [ OK ] |
start
_udev
一般
沒有標準輸出,所有的 udev 相關資訊都按照設定檔 (udev.conf)
的參數設定,由 syslog記錄。
回頁首
小結:
udev 是高效的裝置管理工具,其最大的優勢是動態管理裝置和自訂裝置的命名規則,因此替代 devfs 成為 Linux 預設的裝置管理工具。通過閱讀本文,Linux 使用者能夠瞭解到 udev 的工作原理和流程,靈活地運用 udev 規則檔案,從而方便地管理 Linux 裝置檔案。
參考資料
- 有關 Udev 更多資訊,請參考:http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html。
- 在 developerWorks Linux 專區 尋找為 Linux 開發人員(包括 Linux 新手入門)準備的更多參考資料,查閱我們 最受歡迎的文章和教程。
- 在 developerWorks 上查閱所有 Linux 技巧 和 Linux 教程。
關於作者
黃懋,IBM 中國系統和技術實驗室,Open Systems Interoperability Validation Lab 儲存測試團隊的成員。3-4 年 Linux/Unix 平台和 SAN/NAS 儲存產品的測試經驗。