udev是什麼
udev 是Linux kernel 2.6系列的設備管理器。它主要的功能是管理/dev目錄底下的設備節點。它同時也是用來接替devfs及hotplug的功能,這意味着它要在添加/刪除硬件時處理/dev目錄以及所有用戶空間的行爲,包括加載firmware時。
udev系統由三個部分組成:
libudev函數庫,可以用來獲取設備的信息,/usr/include/libudev.h。
udevd守護進程,處於用戶空間,用於管理虛擬/dev
管理命令udevadm,用來診斷出錯情況,/usr/bin/udevadm。
udev運行方式
udev是一個通用的內核設備管理器。它以守護進程的方式運行於Linux系統,並監聽在新設備初始化或設備從系統中移除時,內核(通過netlinksocket)所發出的uevent。
系統提供了一套規則用於匹配可發現的設備事件和屬性的導出值。匹配規則可能命名並創建設備節點,並運行配置程序來對設備進行設置。udev規則可以匹配像內核子系統、內核設備名稱、設備的物理等屬性,或設備序列號的屬性。規則也可以請求外部程序提供信息來命名設備,或指定一個永遠一樣的自定義名稱來命名設備,而不管設備什麼時候被系統發現。下一部分介紹怎麼通過編寫udev規則文件來管理udev設備
Udev工作流程
udev規則文件編寫
主要的udev配置文件是/etc/udev/udev.conf。這個文件通常很短,可能只是包含幾行#開頭的註釋,然後有如下選項:
udev_root=“/dev/”
udev_rules=“/etc/udev/rules.d/”
udev_log=“err“
其中udev_rules非常重要,表示udev規則存儲的目錄,這個目錄存儲的是以.rules結束的文件。每一個文件處理一系列規則來幫助udev分配名字給設備文件以保證能被內核識別。/etc/udev/rules.d下面可能有好幾個udev規則文件,這些文件一部分是udev包安裝的,另外一部分則是可能是別的硬件或者軟件包生成的。
下面介紹udev規則文件的語法。
udev鍵/值對操作符
操作符 | 匹配或賦值 | 解釋 |
== | 匹配 | 相等比較 |
!= | 匹配 | 不等比較 |
= | 賦值 | 分配一個特定的值給該鍵,他可以覆蓋之前的賦值。 |
+= | 賦值 | 追加特定的值給已經存在的鍵 |
:= | 賦值 | 分配一個特定的值給該鍵,後面的規則不可能覆蓋它 |
udev常用的匹配賦值鍵
ACTION | 事件 (uevent) 的行爲,例如:add( 添加設備 )、remove( 刪除設備 )。 |
KERNEL | 內核設備名稱,例如:sda, cdrom。 |
DEVPATH | 設備的 devpath 路徑。 |
SUBSYSTEM | 設備的子系統名稱,例如:sda 的子系統爲 block。 |
BUS | 設備在 devpath 裏的總線名稱,例如:usb |
DRIVER | 設備在 devpath 裏的設備驅動名稱,例如:ide-cdrom。 |
ID | 設備在 devpath 裏的識別號。 |
SYSFS{filename} | SYSFS{filename}: 設備的 devpath 路徑下,設備的屬性文件“filename”裏的內容。 |
ENV{key} | 環境變量。在一條規則中,可以設定最多五條環境變量的 匹配鍵。 |
PROGRAM | 調用外部命令。 |
RESULT | 外部命令 PROGRAM 的返回結果 |
NAME | 根據這個規則創建的設備文件的文件名。注意:僅僅第一行的NAME描述是有效的,後面的均忽略 |
SYMLINK | 根據規則創建的字符連接名 |
GROUP | 設備文件所在的組。 |
OWNER | 設備文件的屬組 |
MODE | 設備文件的權限,採用8進制 |
ATTRS{文件} | 匹配設備及其所有父設備在sysfs中的屬性值。 如果指定了多個 ATTRS 匹配, 那麼必須在同一個設備上全部匹配成功,纔算最終匹配成功 |
libudev說明
前面大致瞭解了udev相關的功能和原理,下面將描述如何通過libudev來實現相關功能。
libudev提供了在本地系統上檢測、枚舉設備的各種API函數。
Libudev使用過程如下
(參考: http://blog.csdn.net/coroutines/article/details/38067805)
1. 初始化
首先調用udev_new,創建一個udev library context。udev library context採用引用記數機制,創建的context默認引用記數爲1,使用udev_ref和udev_unref增加或減少引用記數,如果引用記數爲0,則釋放內部資源。
2. 枚舉設備
使用udev_enumrate_new創建一個枚舉器,用於掃描系統已接設備。使用udev_enumrate_ref和udev_enumrate_unref增加或減少引用記數。
使用udev_enumrate_add_match/nomatch_xxx系列函數增加枚舉的過濾器,過濾關鍵字以字符表示,如"block"設備。
使用udev_enumrate_scan_xxx系列函數掃描/sys目錄下,所有與過濾器匹配的設備。掃描完成後的數據結構是一個鏈表,使用udev_enumerate_get_list_entry獲取鏈表的首個結點,使用udev_list_entry_foreach遍歷整個鏈表。
3. 監控設備插拔 udev的設備插拔基於netlink實現。
使用udev_monitor_new_from_netlink創建一個新的monitor,函數的第二個參數是事件源的名稱,可選"kernel"或"udev"。基於"kernel"的事件通知要早於"udev",但相關的設備結點未必創建完成,所以一般應用的設計要基於"udev"進行監控。
使用udev_monitor_filter_add_match_subsystem_devtype增加一個基於設備類型的udev事件過濾器,例如: "block"設備。
使用udev_monitor_enable_receiving啓動監控過程。監控可以使用udev_monitor_get_fd獲取一個文件描述符,基於返回的fd可以執行poll操作,簡化程序設計。
插拔事件到達後,可以使用udev_monitor_receive_device獲取產生事件的設備映射。調用udev_device_get_action可以獲得一個字符串:"add"或者"remove",以及"change", "online", "offline"等,但後三個未知什麼情況下會產生。
4、獲取設備信息
使用udev_list_entry_get_name可以得到一個設備結點的sys路徑,基於這個路徑使用udev_device_new_from_syspath可以創建一個udev設備的映射,用於獲取設備屬性。獲取設備屬性使用udev_device_get_properties_list_entry,返回一個存儲了設備所有屬性信息的鏈表,使用udev_list_entry_foreach遍歷鏈表,使用udev_list_entry_get_name和udev_list_entry_get_value獲取屬性的名稱和值。
liudev識別移動設備
首先得知道什麼是usb設備的VID和PID。
據USB規範的規定,所有的USB設備都有供應商ID(VID)和產品識別碼(PID),主機通過不同的VID和PID來區別不同的設備,VID和PID都是兩個字節長,其中,供應商ID(VID)由供應商向USB執行論壇申請,每個供應商的VID是唯一的,PID由供應商自行決定,理論上來說,不同的產品、相同產品的不同型號、相同型號的不同設計的產品最好採用不同的PID,以便區別相同廠家的不同設備。
上面提到,分別調用udev_new,udev_monitor_new_from_netlink,
udev_monitor_filter_add_match_subsystem_devtype,dev_monitor_enable_receiving,dev_monitor_get_fd,udev_monitor_receive_device函數來監控插入的設備。如下圖所示
然後通過udev_device_get_sysattr_value(dev,"idVendor")判斷VID是否是該設備的VID,通過udev_device_get_sysattr_value(dev,"idProduct")來判斷PID是否是該設備的PID,如果都符合,那麼就能識別出這個設備,然後進行相應的操作。