hotplug 次系統核心,以動態方式載入適當的驅動程且 (在 udevd
的協助下)新增對應的設備檔案,處理加入與移除設備的作業。當代的硬體與虛擬化,幾乎每個物件都是熱插拔:從常見的 USB/PCMCIA/IEEE 1394 週邊到 SATA 硬碟,以及 CPU 與記憶體。
核心內的資料庫有每個設備的 ID 及其驅動程式。在啟動階段載入此資料庫,偵測各接口的週邊設備,並在運行中偵測熱插入的設備。接收到插入的設備後,送出訊息給 udevd
,讓其新增對應的條目於 /dev/
內。
熱插拔技術出現前,很容易為設備指定名稱。根據設備所在的位置命名即可。就是設備所在的接口。但每個接口都能連結設備後,這件事就有點麻煩。以數位相機與 USB 碟為例,對電腦而言,它們都是磁碟機。數位相機可能是 /dev/sdb
而 USB 碟可能是 /dev/sdc
(/dev/sda
代表電腦本身的硬式磁碟)。設備名稱不固定;依其連結的順序而命名。
此外,愈來愈多的驅動程式,以動態值指定設備的主要/次要編號,不可能再把固定款目指定給固定的設備,因為重新開機後,一切都變了。
udev 用以解決此問題。
當 udev 被核心告知有個新的設備,它參考 /sys/
裡對應的款目,搜集該設備的資訊,尤其是那些足辨別的獨特資訊 (網卡的 MAC 位址、某些 USB 設備的序號)。
有了這些資訊後,udev 會查閱 /etc/udev/rules.d/
和 /lib/udev/rules.d/
中所有的規則。在此過程中,決定如何為設備命名、使用的連結符號 (給個其他名稱),以及執行的命令。查詢所有檔案後,依序評估該等規則 (除了使用 “GOTO”指令的文件)。如此一來,一個事件就可能對應多個規則。
規則檔案的語法很簡單;每列有選擇規矩與指定變數。前者用於選擇回應的事件,後者設定採取的行動。都以逗點區隔,以運算元區隔選定的範圍 (使用比較運算元,如 ==
或 !=
) 或指定變數 (使用 =
、+=
或 :=
運算元)。
依下列變數使用比較運算元:
KERNEL
:核心指定給設備的名稱;
ACTION
:對應於事件的行動 (“add” 新增設備時,“remove” 移除設備時);
DEVPATH
:設備在 /sys/
裡的路徑;
SUBSYSTEM
:產生請求的核心次系統 (很多這類次系統,包括“usb”、“ide”、“net”、“firmware”等);
ATTR{屬性}
:屬性 檔案的內容在設備的 /sys/$devpath/
資料夾內。可在此找到 MAC 位址及其他辨識用的匯流排;
KERNELS
、SUBSYSTEMS
與 ATTRS{屬性}
係用於比較當前設備的選項變數;
PROGRAM
:被測試的程式 (若為真,則送回 0)。程式的內容儲存在標準輸出以便被 RESULT
測試使用;
RESULT
:對最後一次呼叫的 PROGRAM
產生的標準輸入進行測試。
右方的運算元可供模式表達同時匹配的多個值。例如,*
表示匹配所有的字元 (包括空字元);?
表示匹配一個字元,而 []
表示匹配一組在方括號內的字元 (或若首字元為驚嘆號則做反義的表巧,以 a-z
表示連續的字元)。
對於指定的運算元,=
指定一個值 (並取代現在的值);用在清單時,清空原來的值祗剩指定的值。:=
功能相同,且不允許再更改原變數。至於 +=
,新增一個項目在清單內。可以更改以下的變數:
指定給這些變數的值可以使用以下的替代品:
$kernel
或 %k
:相當於 KERNEL
;
$number
或 %n
:設備的序號,例如,sda3
,就是 “3”;
$devpath
或 %p
:相當於 DEVPATH
;
$attr{屬性}
或 %s{屬性}
:相當於 ATTRS{屬性}
;
$major
或 %M
:設備的核心主要編號;
$minor
或 %m
:設備核心的次要編號;
$result
或 %c
:以 PROGRAM
最後執行程式的輸出字串;
最後,%%
和 $$
分別是百分號及錢號。
以上的清單仍不完備 (祗包括最重要的參數),詳細的資料在 udev(7) 手冊頁面。
考慮給予 USB 隨身碟一個固定名稱的情況。首先,必須找到能夠識別的元素。因此,插入它並執行 udevadm info -a -n /dev/sdc
(以指定給該隨身碟的實際名稱取代 /dev/sdc)。
#
udevadm info -a -n /dev/sdc
[...]
looking at device '/devices/pci0000:00/0000:00:10.3/usb1/1-2/1-2.2/1-2.2:1.0/host9/target9:0:0/9:0:0:0/block/sdc':
KERNEL=="sdc"
SUBSYSTEM=="block"
DRIVER==""
ATTR{range}=="16"
ATTR{ext_range}=="256"
ATTR{removable}=="1"
ATTR{ro}=="0"
ATTR{size}=="126976"
ATTR{alignment_offset}=="0"
ATTR{capability}=="53"
ATTR{stat}==" 51 100 1208 256 0 0 0 0 0 192 25 6"
ATTR{inflight}==" 0 0"
[...]
looking at parent device '/devices/pci0000:00/0000:00:10.3/usb1/1-2/1-2.2/1-2.2:1.0/host9/target9:0:0/9:0:0:0':
KERNELS=="9:0:0:0"
SUBSYSTEMS=="scsi"
DRIVERS=="sd"
ATTRS{device_blocked}=="0"
ATTRS{type}=="0"
ATTRS{scsi_level}=="3"
ATTRS{vendor}=="I0MEGA "
ATTRS{model}=="UMni64MB*IOM2C4 "
ATTRS{rev}==" "
ATTRS{state}=="running"
[...]
ATTRS{max_sectors}=="240"
[...]
looking at parent device '/devices/pci0000:00/0000:00:10.3/usb1/1-2/1-2.2':
KERNELS=="9:0:0:0"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}=="iCfg"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bmAttributes}=="80"
ATTRS{bMaxPower}=="100mA"
ATTRS{urbnum}=="398"
ATTRS{idVendor}=="4146"
ATTRS{idProduct}=="4146"
ATTRS{bcdDevice}=="0100"
[...]
ATTRS{manufacturer}=="USB Disk"
ATTRS{product}=="USB Mass Storage Device"
ATTRS{serial}=="M004021000001"
[...]
依照檢測設備變數,以及父設備的變數,新增規則。以上的例子可以新增兩個規則:
KERNEL=="sd?", SUBSYSTEM=="block", ATTRS{serial}=="M004021000001", SYMLINK+="usb_key/disk"
KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{serial}=="M004021000001", SYMLINK+="usb_key/part%n"
在文件中設定這些規則後,例如把它命名為 /etc/udev/rules.d/010_local.rules
,就可以移除和再連結 USB 隨身碟。可看到 /dev/usb_key/disk
代表與 USB 隨身碟連結的磁碟,/dev/usb_key/part1
是其第一個分區。