2011-11-04

Linux 的 udev 是個有趣又有用的玩具(1)

This is a series of step-by-step tutorial articles discussing how to hook up a portable general purpose Bourne shell script to uevents generated by the udev deamon.  It was written for readers in Taiwan, however, I'm  inserting  as many as  possible  English keywords to make it also comprehensible for all.

Udev 是自 kernel 2.6 以降,取代傳統靜態(static)的 devfs,改以動態(dynamic)的方式來管理 /dev/ 之下所有裝置的子系統。除此之外,它也取代了原先 hotplug 的功能,經常性地監看系統狀態,並於週邊元件發生異動,例如插入或拔除 USB 裝置、記憶卡、外接電源等設備時,尋找並載入合適的核心模組(kernel module)、建立並命名 device nodes、建立 links,或作出其它適當的反應。

典型的 Linux 系統在 /etc/udev/rules.d/ 資料夾下會有許多,在檔案名稱上具有先後順序(lexicographical order)的規則檔案 *.rules。這些規則所做的,通常是,裝備的重新命名(renaming),權限(permission)的設定,連結(links)的產生與移除等工作,例如新增一個數位電視裝置(dvb)時,將其權限更改為 666,或是固定把某一顆硬碟(HDD)的第一個 partition 永遠都取個暱名(persistent naming)叫做: 
/dev/disk/by-id/usb-ST316002_3AS_34331B612222-part1
以避免有時是 /dev/sda1、/dev/sdc1,有時候又成為 /dev/hda1 的困擾。然而,如果沒有這些 rules 的話,kernel 還是會賦予預設的名稱與權限。

本文的重點不(not)在於如何撰寫(writing)或修改 udev 規則(rules),也不在於如何更改新增裝置的名稱及權限。而在於,脫離 udev rules 的特殊語法(syntax),另外以我們已經熟悉的 Bourne-shell-script 來設計當週邊設備有所異動時,我們想要的反應及動作,例如,發出聲響(sound effects),跳出視窗(pop-up windows),或是自動 mount 之後,備份特定檔案(file backup)等。

如果你在指令行下
$ ps -e | grep udev
可以看到 udevd 的話,就表示說,你的 Linux 系統上已經有一個所謂的 udev deamon 正在管理核心(kernel)裡所傳送出來的 uevents。這時,/dev/ 資料夾裡的檔案是動態配置的(dynamically polulated),譬如說,如果系統上沒有 sdg 這個 block device,那麼在 /dev/ 裡也不會有 sdg 這個檔案。如果你的 kernel 版本(可以 uname -r 查詢)是 >= 2.6.13 的話,那麼你就可以跟著本文一步一步地來玩一些有趣的實驗,這可能包括像是插入(insert)或拔出(unplug)一個隨身碟(thumb-drive)或一張記憶卡(SD card)時,讓電腦發出你所要的聲音(sound),或是跳出一個視窗(pop-up window),或者是自動 mount 一個 partition,或是甚至於在 mount 之後自動播放影音或是自動瀏覽照片(media-players)。雖然我們不建議像 autorun.bat 這種極具安全疑慮(security issues)的功能,但是這自然也是很容易辦到的。

因為我個人所使用的是 kernel 版本 2.6.27.59 上的 Debian Etch 或 Lenny,所以我們接下來就以此平台為基礎,開始做一些簡單有趣的實驗。

我們先以 root 的權限在 /etc/udev/rules.d/ 這個資料夾裡建立一個新檔:
$ sudo vim /etc/udev/rules.d/z99_test.rules
檔案內容只要一行:
RUN+="/home/nobody/my_script"
在這裡我們假設 nobody 是你的帳號名稱(user name),my_script 是放在 HOME 裡的任何一個可執行的程式。我們將用 shell-script 來示範這個程式如何被 udev 執行。其實在你建好 z99_test.rules 之後,udev 就已經開始執行 my_script 這個程式了。雖然此刻它還不存在,但是 udev 系統在找不到這個檔案的時候也不會抱怨。每次有電腦周邊元件異動時(uevent),例如,usb device 插入或拔出時,udev 系統都會去 /etc/udev/rules.d/ 資料夾裡依序查看並執行所有的 rules,當輪到我們剛剛才建立的 z99_test.rules 時,它便會去執行 my_script。

接下來我們便要來寫個叫做 my_script 的程式:
$ cd ~/
$ vim my_script
裡面的內容就先放兩行就好了:
#!/bin/sh
aplay /usr/share/sounds/KDE_Beep_Bottles.wav
其中第一行是 UNIX 標準的 shell-script "shebang",它是用來告訴系統,這個 script 是要由 /bin/sh 來執行。第二行則呼叫一個聲音播放程式出來播放一個聲音檔。儲存這個檔案,再做(必要!):
$ chmod 755 my_script
然後先測試一下,是否有聲音:
$ ./my_script
如果你的系統上沒有 aplay 或是那個 wav 檔,就以任何你系統上有的程式或聲音檔來取代。但是我們還是建議用 aplay 跟沒有壓縮的 wav 檔案。因為 aplay 檔案小、啟動快,且會用 ALSA 的 dmix 播放聲音檔,這樣的話,就不會因為可能正在聽別的東西,而使得這個程式要發出的聲音被擋住,不能播放。aplay(1) 在 Debain/Etch 是包含於 alsa-utils 這個 package 裡面。

再來,你就可以拿任何一個 usb 隨身碟插進去,試試看囉!你每次一插進去一個隨身碟,大概會聽到這個聲音檔被播放約 7 次,拔出隨身碟時,也大概會聽到 3 次這個聲音。

如果發生原因不明的問題的話,可以試試看:
$ sudo /etc/init.d/udev reload
我們剛剛建立了 /etc/udev/rules.d/z99_test.rules 之後,沒有執行以上的 reload,是因為它是新建立的規則檔案(new rule files),但是如果這個檔案的內容經過修改的話,則必須執行以上的 reload,修改過的內容才會生效。

如果讓 my_script 的聲音播放指令在背景執行的話,例如:
#!/bin/sh
aplay /usr/share/sounds/KDE_Beep_Bottles.wav &
那麼你很可能就不會聽得見那麼多次。這是因為這個指令被放到背景執行(backgrounding)之後,這個程式就結束了,而 udev 系統很快地再次執行這一行指令時,也會一再地放到背景去執行……,這些聲音就都重疊在一起,不能清楚地算出是被執行了幾次。

值得注意的是,如果在 my_script 這個程式裡,因為打錯字(typo)而執行錯誤的話,我們將完全無法察覺,也看不見任何的錯誤訊息(error messages),這是因為 udev 執行這個程式的時候,沒有 tty 可用,所以這個程式裡所產生的任何文字輸出(character output),我們都看不到。唯一的辦法是,讓這個程式把所有的訊息輸出到我們指定的 log file 裡。關於這一點,我們以後會介紹非常方便的 stdout 以及 stderr 的 redirection 方法。

基本架構已經建立,從現在開始,每當系統上「發生了什麼事件」(uevent),udev 系統都會去 /etc/udev/rules.d/ 資料夾裡依序查看並執行所有的 rules,當輪到我們剛剛才建立的 z99_test.rules 時,我們的程式 my_script 就會被執行一次。只要我們 shell-script 程式技巧足夠充分,對 udev 系統足夠了解,就可以在這個 script 裡為所欲為。在這一系列文章的下個單元,我們要繼續探討如何讓這個程式做更有用的事情。

下個單元:Linux 的 udev 是個有趣又有用的玩具(2)

References:

[1] Markus Gattol, Udev or how I manage my Gadgets,
     http://www.markus-gattol.name/ws/udev.html

[2] Daniel Drake, Writing udev rules,
     http://reactivated.net/writing_udev_rules.html

[3] ArchWiki, udev,
     https://wiki.archlinux.org/index.php/Udev

[4]  udev - Wikipedia, the free encyclopedia
      http://en.wikipedia.org/wiki/Udev

[5]  Chapter 20. Dynamic Kernel Device Management with udev http://doc.opensuse.org/products/opensuse/openSUSE/opensuse-reference/cha.udev.html

[6]  Udev - ALSA wiki
      http://alsa.opensrc.org/Udev

沒有留言:

張貼留言