udev子系統 - linux的裝置管理員

keyword:

  • udev
  • udevadm
  • /etc/udev/
  • /dev
  • sysfs
  • /sys

udev在user space底下以背景服務方式運作,不被在核心模式下運作代表著在裝置的命名處理上有更多的彈性。

早期linux2.6以前的做法是devfs,devfs 跑在核心模式,以靜態節點的方式呈現於檔案系統/dev,缺點是彈性不好。

linxu2.6以後,發展為sysfs + udev的架構,核心跑sysfs子系統,反映設備的狀態於/sys底下,而使用者空間跑udev,依賴/sys訊息運作,經自已的規則轉換處理後,反映於檔案系統/dev底下。

udev維持了linux系統對於裝置插拔的處理彈性,可動態的新增/移除裝置於檔案系統/dev。

udev組態檔:/etc/udev/udev.conf

handy@handy-dell /etc/udev $ ls
hwdb.d  rules.d  udev.conf

udev規則檔:/etc/udev/rules.d/*

handy@handy-dell /etc/udev $ ls rules.d/
70-persistent-net.rules  99-debug.rules  README

撰寫udev規則檔實作:

0) 起手式,先確認成功掛勾系統事件與指令稿:

udev規則檔不做過濾,直接跑自訂的指令稿

pi@raspberrypi-14:/etc/udev/rules.d $ cat z98-debug.rules 
RUN+="/home/pi/udev-debug.sh"

指令稿開發技巧,把時間寫到檔案

pi@raspberrypi-14:~ $ cat udev-debug.sh 
#!/bin/sh
LOGFILE=/home/pi/udev-debug.log
date  >> ${LOGFILE} 2>&1
exit $?

拔插usb dongle,檢視系統事件是否觸發udev rules並執行指令搞。應該要看到LOGFILE的內容有時間碼。

此步驟若正確,表示系統事件已與udev rules系統成功掛勾,後續就是視應用加入對應的事件過濾條件。

1) 指令稿加入系統變數,看看究竟發生了什麼事

pi@raspberrypi-14:~ $ cat udevrun.sh 
#!/bin/sh
LOGFILE=/home/pi/udevrun.log
echo ${SEQNUM} ${SUBSYSTEM} ${ACTION} ${DEVNAME} >> ${LOGFILE} 2>&1
#date  >> ${LOGFILE} 2>&1
exit $?

這些都是未來要寫udev規則檔時的依據。

在USB插拔幾次後,log長得像下面這樣

pi@raspberrypi-14:~ $ cat udevrun.log 
1189 tty add /dev/ttyUSB1
1205 tty add /dev/ttyUSB1
1213 tty add /dev/ttyUSB1
1216 platform change
1217 platform change
1218 tty remove /dev/ttyUSB1
1219 usb-serial remove
1220 usb remove
1221 usb remove /dev/bus/usb/001/014

來看一下FTDI 4通道的uart/usb插拔會發生什麼事

pi@raspberrypi-14:~ $ cat udevrun.log 
1222 usb add /dev/bus/usb/001/015
1224 usb add
1229 drivers add
1225 usb add
1228 drivers add
1227 module add
1226 usb add
1234 usb-serial add
1236 usb-serial add
1232 usb-serial add
1235 tty add /dev/ttyUSB3
1233 tty add /dev/ttyUSB2
1237 tty add /dev/ttyUSB4
1223 usb add
1230 usb-serial add
1231 tty add /dev/ttyUSB1

注意一下SEQNUM發生的順序。

/dev/ttyUSB0被咬住了,如何解?

2) 回頭來寫規則檔

看一下重要的DRIVER參數長得如何:

pi@raspberrypi-14:/etc/udev/rules.d $ udevadm info -n ttyUSB1 | grep DRIVER
E: ID_USB_DRIVER=ftdi_sio

規則檔本人長這樣:

pi@raspberrypi-14:/etc/udev/rules.d $ cat z99-handy.rules 
DRIVERS=="usb",SUBSYSTEM=="tty",ACTION=="add",NAME="handy",SYMLINK="ttypolific"
DRIVERS=="ftdi_sio",SUBSYSTEM=="tty",ACTION=="add",NAME="handy",SYMLINK="ttyftdi"

成功了,看看那優美的 /dev/ttyftdi 出現了

pi@raspberrypi-14:/etc/udev/rules.d $ ls /dev/tty*
/dev/tty    /dev/tty18  /dev/tty28  /dev/tty38  /dev/tty48  /dev/tty58    /dev/ttyftdi
/dev/tty0   /dev/tty19  /dev/tty29  /dev/tty39  /dev/tty49  /dev/tty59    /dev/ttyprintk
/dev/tty1   /dev/tty2   /dev/tty3   /dev/tty4   /dev/tty5   /dev/tty6     /dev/ttyUSB1
/dev/tty10  /dev/tty20  /dev/tty30  /dev/tty40  /dev/tty50  /dev/tty60    /dev/ttyUSB2
/dev/tty11  /dev/tty21  /dev/tty31  /dev/tty41  /dev/tty51  /dev/tty61    /dev/ttyUSB3
/dev/tty12  /dev/tty22  /dev/tty32  /dev/tty42  /dev/tty52  /dev/tty62    /dev/ttyUSB4
/dev/tty13  /dev/tty23  /dev/tty33  /dev/tty43  /dev/tty53  /dev/tty63
/dev/tty14  /dev/tty24  /dev/tty34  /dev/tty44  /dev/tty54  /dev/tty7
/dev/tty15  /dev/tty25  /dev/tty35  /dev/tty45  /dev/tty55  /dev/tty8
/dev/tty16  /dev/tty26  /dev/tty36  /dev/tty46  /dev/tty56  /dev/tty9
/dev/tty17  /dev/tty27  /dev/tty37  /dev/tty47  /dev/tty57  /dev/ttyAMA0

原始參考資料:

man udev
man udevadm

1) 取得裝置資訊(使用udevadm)

udevadm info -a -n ttyUSB0

-a指的是 --attribute-walk,印出所有 sysfs內抓到的屬性參數,用來寫udev規則用。

       -a, --attribute-walk
           Print all sysfs properties of the specified device that can be used
           in udev rules to match the specified device. It prints all devices
           along the chain, up to the root of sysfs that can be used in udev
           rules.

-n指的是 --name=FILE,指定device node

       -n, --name=FILE
           The name of the device node or a symlink to query, e.g.
           [/dev]/sda. Note that this option usually is not very useful, since
           udev can guess the type of the argument, so udevadm --name=sda is
           equivalent to udevadm /dev/sda.

2) 觀察裝置特徵(特別注意DRIVERS參數)

  looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/ttyUSB0/tty/ttyUSB0':
    KERNEL=="ttyUSB0"
    SUBSYSTEM=="tty"
    DRIVER==""

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/ttyUSB0':
    KERNELS=="ttyUSB0"
    SUBSYSTEMS=="usb-serial"
    DRIVERS=="pl2303"
    ATTRS{port_number}=="0"

3) 寫規則檔 /etc/udev/rules.d/*

DRIVERS=="ftdi_sio", SUBSYSTEM=="tty", ACTION=="add", NAME="handy", RUN+="/home/handy/udevtest/udevrun.sh", SYMLINK="ttyftdi"  
DRIVERS=="pl2303", SUBSYSTEM=="tty", ACTION=="add", NAME="handy", RUN+="/home/handy/udevtest/udevrun.sh", SYMLINK="ttypolific"

測試udev是否正常作動的技巧:寫一個script於udev作動時呼叫,並將得到的資訊寫到指定的檔案裡。

#!/bin/sh
LOGFILE=/home/handy/udevtest/xx.log
echo ${SEQNUM} ${SUBSYSTEM} ${ACTION} ${DEVNAME} >> ${LOGFILE} 2>&1
exit $?

記得設定為可執行 sudo chmod +x <filename>

4) 測試效果

插入硬體,並比較/dev底下的變動。如:ls /dev/tty*

疑問:linux底下對於這種比對處理,是否有更方便的做法?

解1:兩次命令列輸出各自存成文字檔,再用diff比對

解2:使用watch -n 1 -d ls /dev

results matching ""

    No results matching ""