udev 使用方法


原文地址 http://blog.163.com/againinput4@yeah/blog/static/122764271200962305339483/

 

最近有在研究SD卡設備節點自動創建及掛載,發現TI的達芬奇平台使用的是udev而非mdev,所以花了點時間看了看udev,查到了《udev輕松上路》這篇文章,看了下《Writing udev rules》,那篇文章寫的不錯,建議有需要的朋友一定要好好看看,另外,在網上有篇關於SD卡和U盤自動創建節點自動掛載的文章,分別通過udev和mdev實現,也可以作為參考,真正理解還需要看看老外的文章,理解下里面的udev書寫規則,真正做到標本兼治!

通過上述文章,加上自己的理解,有了這篇文章!

第一、什么是udev?

這篇文章UDEV Primer給我們娓娓道來,花點時間預習一下是值得的。當然,不知道udev是什么也沒關系,
把它當個助記符好了,有了下面的上路指南,可以節省很多時間。我們只需要樹立一個信念:udev很簡單!
嵌入式的udev應用尤其簡單。

第二、為什么udev要取代devfs?

這是生產關系適應生產力的需要,udev好,devfs壞,用好的不用壞的。

udev是硬件平台無關的,屬於user space的進程,它脫離驅動層的關聯而建立在操作系統之上,基於這種設
計實現,我們可以隨時修改及刪除/dev下的設備文件名稱和指向,隨心所欲地按照我們的願望安排和管理設
備文件系統,而完成如此靈活的功能只需要簡單地修改udev的配置文件即可,無需重新啟動操作系統。udev
已經使得我們對設備的管理如探囊取物般輕松自如。

第三、如何得到udev?

udev的主頁在這里:http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
我們按照下面的步驟來生成udev的工具程序,以arm-linux為例:
1、wget http://www.us.kernel.org/pub/linux/utils/kernel/hotplug/udev-100.tar.bz2
2、tar xjf udev-100.tar.bz2
3、cd udev-100 編輯Makefile,查找CROSS_COMPILE,修改CROSS_COMPILE ?= arm-linux-
4、make

沒有什么意外的話當前目錄下生成udev,udevcontrol,udevd,udevinfo,udevmonitor,udevsettle,udevstart,
udevtest,udevtrigger九個工具程序,在嵌入式系統里,我們只需要udevd和udevstart就能使udev工作得很好,
其他工具則幫助我們完成udev的信息察看、事件捕捉或者更高級的操作。

另外一個方法是直接使用debian提供的已編譯好的二進制包,美中不足的是版本老了一些。
1、wget http://ftp.us.debian.org/debian/pool/main/u/udev/udev_0.056-3_arm.deb
2、ar -xf udev_0.056-3_arm.deb
3、tar xzf data.tar.gz

在sbin目錄里就有我們需要的udevd和udevstart工具程序。

建議大家采用第一種方式生成的udevd和udevstart。為什么要用最新udev呢?新的強,舊的弱,用強的不用弱的。

第四、如何配置udev?

首先,udev需要內核sysfs和tmpfs的支持,sysfs為udev提供設備入口和uevent通道,tmpfs為udev設備文件提
供存放空間,也就是說,在上電之前系統上是沒有足夠的設備文件可用的,我們需要一些技巧讓kernel先引導
起來。

由於在kernel啟動未完成以前我們的設備文件不可用,如果使用mtd設備作為rootfs的掛載點,這個時候/dev/mtdblock
是不存在的,我們無法讓kernel找到rootfs,kernel只好停在那里驚慌。
這個問題我們可以通過給kernel傳遞設備號的方式來解決,在linux系統中,mtdblock的主設備號是31,part號
從0開始,那么以前的/dev/mtdblock/3就等同於31:03,以次類推,所以我們只需要修改bootloader傳給kernel
的cmd line參數,使root=31:03,就可以讓kernel在udevd未起來之前成功的找到rootfs。
O.K.下一個問題。

其次,需要做的工作就是重新生成rootfs,把udevd和udevstart復制到/sbin目錄。然后我們需要在/etc/下為udev
建立設備規則,這可以說是udev最為復雜的一步。這篇文章提供了最完整的指導:Writing udev rules (http://reactivated.net/writing_udev_rules.html)
文中描述的復雜規則我們可以暫時不用去理會,上路指南將帶領我們輕松穿過這片迷霧。這里提供一個由簡入
繁的方法,對於嵌入式系統,這樣做可以一勞永逸。

1、在前面用到的udev-100目錄里,有一個etc目錄,里面放着的udev目錄包含了udev設備規則的詳細樣例文
本。為了簡單而又簡潔,我們只需要用到etc/udev/udev.conf這個文件,在我們的rootfs/etc下建立一個udev目
錄,把它復制過去,這個文件很簡單,除了注釋只有一行,是用來配置日志信息的,嵌入式系統也許用不上
日志,但是udevd需要檢查這個文件。

2、在rootfs/etc/udev下建立一個rules.d目錄,生成一個空的配置文件touch etc/udev/rules.d/udev.conf。然后
我們來編輯這個文件並向它寫入以下配置項:

###############################################
# vc devices
KERNEL=="tty[0-9]*", NAME="vc/%n"

# block devices
KERNEL=="loop[0-9]*", NAME="loop/%n"

# mtd devices
KERNEL=="mtd[0-9]*", NAME="mtd/%n"
KERNEL=="mtdblock*", NAME="mtdblock/%n"

# input devices
KERNEL=="mice" NAME="input/%k"
KERNEL=="mouse[0-9]*", NAME="input/%k"
KERNEL=="ts[0-9]*", NAME="input/%k"
KERNEL=="event[0-9]*", NAME="input/%k"

# misc devices
KERNEL=="apm_bios", NAME="misc/%k"
KERNEL=="rtc", NAME="misc/%k"
################################################

保存它,我們的設備文件系統基本上就可以了,udevd和udevstart會自動分析這個文件。

這里重點說下%n,先來看2個例子:

# cat /etc/udev/rules.d/honeywell.rules
#Honeywell
#SD card
#KERNEL=="mmcblk[0-9]p*", NAME="SD/%n", SYMLINK+="SdPart%n", RUN+="/sbin/SdCard", OPTIONS+="last_rule"
KERNEL=="mmcblk*", NAME="SD/%n",SYMLINK+="SdPart%n",RUN+="/sbin/SdCard"
#
插入SD卡:

# ls /dev/SD/ -l
brw-rw----    1 root     root     254,   0 Jan  1 00:41 0
brw-rw----    1 root     root     254,   1 Jan  1 00:41 1
# ls /dev/SdPart* -l
lrwxrwxrwx    1 root     root            4 Jan  1 00:41 /dev/SdPart0 -> SD/0
lrwxrwxrwx    1 root     root            4 Jan  1 00:41 /dev/SdPart1 -> SD/1
#
#
修改規則后:

# cat /etc/udev/rules.d/honeywell.rules
#Honeywell
#SD card
#KERNEL=="mmcblk[0-9]p*", NAME="SD/%n", SYMLINK+="SdPart%n", RUN+="/sbin/SdCard", OPTIONS+="last_rule"
KERNEL=="mmcblk0p*", NAME="SD/%n",SYMLINK+="SdPart%n",RUN+="/sbin/SdCard"
#
插入SD卡(需要先umount)

#
# ls -l /dev/SD/
brw-rw----    1 root     root     254,   1 Jan  1 00:49 1
#
# ls -l /dev/SdPart*
lrwxrwxrwx    1 root     root            4 Jan  1 00:49 /dev/SdPart1 -> SD/1
#

修改部分內容后如下:

# cat /etc/udev/rules.d/honeywell.rules
#Honeywell
#SD card
#KERNEL=="mmcblk[0-9]p*", NAME="SD/%n", SYMLINK+="SdPart%n", RUN+="/sbin/SdCard", OPTIONS+="last_rule"
KERNEL=="mmcblk?", NAME="SD/%n",SYMLINK+="SdPart%n",RUN+="/sbin/SdCard"
#

插入SD卡:
#
# ls -l /dev/SD/
brw-rw----    1 root     root     254,   0 Jan  1 00:54 0
# ls -l /dev/Sd*
lrwxrwxrwx    1 root     root            4 Jan  1 00:54 /dev/SdPart0 -> SD/0
#

結論:

%n是KERNEL中的數字號碼,

    在 KERNEL=="mmcblk*"中,KERNEL分別匹配mmcblk0(SD卡)和mmcblk0p1(SD卡1分區),所以%n分別對應數字0 1,也就創建了/dev/SdPart0 /dev/SdPart1兩個軟鏈接,
    當改為KERNEL=="mmcblk0p*"時,KERNEL只能匹配到mmcblk0p1,所以只會創建/dev/SdPart1軟連接。
    當改為KERNEL=="mmcblk?"時,KERNEL只匹配一次,優先匹配為到mmcblk0,所以只會創建/dev/SdPart0軟連接


3、為了使udevd在kernel起來后能夠自動運行,我們在rootfs/etc/init.d/rcS中增加以下幾行:

##################################
/bin/mount -t tmpfs tmpfs /dev

echo "Starting udevd..."
/sbin/udevd --daemon
/sbin/udevstart
##################################

4、重新生成rootfs,燒寫到flash指定的rootfs part中。

5、如果需要動態改變設備規則,可以把etc/udev放到jffs或yaffs part,以備修改,根據需求而定,可以隨時擴
充udev.conf中的配置項。

 

 

6、我們知道,用戶空間的程序與設備通信的方法,主要有以下幾種方式,
  1. 通過ioperm獲取操作IO端口的權限,然后用inb/inw/ inl/ outb/outw/outl等函數,避開設備驅動程序,直接去操作IO端口。(沒有用過)
  2. 用ioctl函數去操作/dev目錄下對應的設備,這是設備驅動程序提供的接口。像鍵盤、鼠標和觸摸屏等輸入設備一般都是這樣做的。
  3. 用write/read/mmap去操作/dev目錄下對應的設備,這也是設備驅動程序提供的接口。像framebuffer等都是這樣做的。
  
  上面的方法在大多數情況下,都可以正常工作,但是對於熱插撥(hotplug)的設備,比如像U盤,就有點困難了,因為你不知道:什么時候設備插上了,什么時候設備拔掉了。這就是所謂的hotplug問題了。
  
  處理hotplug傳統的方法是,在內核中執行一個稱為hotplug的程序,相關參數通過環境變量傳遞過來,再由hotplug通知其它關注 hotplug事件的應用程序。這樣做不但效率低下,而且感覺也不那么優雅。新的方法是采用NETLINK實現的,這是一種特殊類型的socket,專門用於內核空間與用戶空間的異步通信。下面的這個簡單的例子,可以監聽來自內核hotplug的事件。

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <sys/un.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <linux/types.h> #include <linux/netlink.h> #include <errno.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h>  

#define UEVENT_BUFFER_SIZE 2048  

static int init_hotplug_sock() { const int buffersize = 1024; int ret; struct sockaddr_nl snl; bzero(&snl, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; snl.nl_pid = getpid(); snl.nl_groups = 1; int s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (s == -1) { perror("socket"); return -1; } setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize)); ret = bind(s, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)); if (ret < 0) { perror("bind"); close(s); return -1; } return s; } int main(int argc, char* argv[]) { int hotplug_sock = init_hotplug_sock(); while(1) { /* Netlink message buffer */  
  char buf[UEVENT_BUFFER_SIZE * 2] = {0}; recv(hotplug_sock, &buf, sizeof(buf), 0); printf("%s\n", buf); /* USB 設備的插拔會出現字符信息,通過比較不同的信息確定特定設備的插拔,在這添加比較代碼 */ } return 0; }

 udev的主體部分在udevd.c文件中,它主要監控來自4個文件描述符的事件/消息,並做出處理:
  1. 來自客戶端的控制消息。這通常由udevcontrol命令通過地址為/org/kernel/udev/udevd的本地socket,向udevd發送的控制消息。其中消息類型有:
  l UDEVD_CTRL_STOP_EXEC_QUEUE 停止處理消息隊列。
  l UDEVD_CTRL_START_EXEC_QUEUE 開始處理消息隊列。
  l UDEVD_CTRL_SET_LOG_LEVEL 設置LOG的級別。
  l UDEVD_CTRL_SET_MAX_CHILDS 設置最大子進程數限制。好像沒有用。
  l UDEVD_CTRL_SET_MAX_CHILDS_RUNNING 設置最大運行子進程數限制(遍歷proc目錄下所有進程,根據session的值判斷)。
  l UDEVD_CTRL_RELOAD_RULES 重新加載配置文件。
  2. 來自內核的hotplug事件。如果有事件來源於hotplug,它讀取該事件,創建一個udevd_uevent_msg對象,記錄當前的消息序列號,設置消息的狀態為EVENT_QUEUED,然后並放入running_list和exec_list兩個隊列中,稍后再進行處理。
  3. 來自signal handler中的事件。signal handler是異步執行的,即使有signal產生,主進程的select並不會喚醒,為了喚醒主進程的select,它建立了一個管道,在 signal handler中,向該管道寫入長度為1個子節的數據,這樣就可以喚醒主進程的select了。
  4. 來自配置文件變化的事件。udev通過文件系統inotify功能,監控其配置文件目錄/etc/udev/rules.d,一旦該目錄中文件有變化,它就重新加載配置文件。
  
  其中最主要的事件,當然是來自內核的hotplug事件,如何處理這些事件是udev的關鍵。udev本身並不知道如何處理這些事件,也沒有必要知道,因為它只實現機制,而不實現策略。事件的處理是由配置文件決定的,這些配置文件即所謂的rule。
  
  關於rule的編寫方法可以參考《writing_udev_rules》,udev_rules.c實現了對規則的解析。
  
  在規則中,可以讓外部應用程序處理某個事件,這有兩種方式,一種是直接執行命令,通常是讓modprobe去加載驅動程序,或者讓mount去加載分區。另外一種是通過本地socket發送消息給某個應用程序。
  
  在udevd.c:udev_event_process函數中,我們可以看到,如果RUN參數以”socket:”開頭則認為是發到socket,否則認為是執行指定的程序。
  
  下面的規則是執行指定程序:
  60-pcmcia.rules: RUN+="/sbin/modprobe pcmcia"
  
  下面的規則是通過socket發送消息:
  90-hal.rules:RUN+="socket:/org/freedesktop/hal/udev_event"



mission completed!

-----------------------------------------

在2.6內核里面,如果配置了sysfs的話,kernel啟動的時候會自動在sysfs空間查找root設備。所以,型如root=/dev/mtdblock3等的參數還是可以工作的。至少在我這里可以。

-----------------------------------------

是這樣的,謝謝指正!

更正如下:
========================
由於在kernel啟動未完成以前我們的設備文件不可用,如果使用mtd設備作為rootfs的掛載點,這個時候/dev/mtdblock 這個設備目錄是不存在的,我們無法讓kernel通過/dev/mtdblock/X這樣的設備找到rootfs,kernel只好停在那里驚慌。
這個問題我們可以通過給kernel傳遞設備號的方式來解決,在linux系統中,mtdblock的主設備號是31,part號 從0開始,那么以前的/dev/mtdblock/3就等同於31:03,以次類推,所以我們只需要修改bootloader傳給kernel 的cmd line參數,使root=31:03,就可以讓kernel在udevd未起來之前成功的找到rootfs。

另外一種方法就是給kernel傳遞未經歸類的設備文件名,在udev未創建之前,所有的設備實際上已經通過sysfs 建立,mtdblockX的位置相對於/sys/block/mtdblockX/dev,這個文件里存放着mtdblockX的設備號,形式與上 一種方式相同。這時由於沒有相應的udev規則,所有的設備都被隱含地映射到/dev目錄下,mtdblockX對應於 /dev/mtdbockX,這樣我們給kernel傳遞root=/dev/mtdblock3,kernel發現/dev沒有被建立,就自動從映射表里 查找對應關系,最后取出/sys/block/mtdblockX/dev里的設備號,完成rootfs的掛載。
========================

-----------------------------------------

>2、在rootfs/etc/udev下建立一個rules.d目錄,生成一個空的配置文件touch etc/udev/rules.d/udev.conf。 這里是不是有些問題,我本機上的udev.conf文件是在/etc/udev/下的。
-----------------------------------------

在udev_version.h中有以下兩個定義:
#define UDEV_CONFIG_FILE "/etc/udev/udev.conf"
#define UDEV_RULES_FILE "/etc/udev/rules.d"

我想之所以采用rules.d的目錄形式,其目的和rcX.d相同,可以在規則十分復雜的情況下用數字鏈接的方式控制
啟動順序。由於版本的不斷升級,代碼以及結構也越來越漂亮。

我們按照這種默認的方式來編寫和存放規則文件,不失為一個好習慣。

-----------------------------------------

我的/etc/udev/下有rules.d目錄和udev.conf文件
我的意思是udev.conf文件是否應該在/etc/udev目錄而非/etc/udev/rules.d目錄下

-----------------------------------------

是這樣的,謝謝指正!

更正如下:
========================
2、在rootfs/etc/udev下建立一個rules.d目錄,生成一個空的配置文件touch etc/udev/rules.d/udev.rules。
========================

兄弟確實是火眼金睛啊,這里有個筆誤。

-----------------------------------------

pc的應用相對復雜些。

通過查看pc上的腳本/etc/init.d/udev我們發現:

udevtrigger同樣可以完成udevstart所做的事情,而且它能夠兼容以前MAKEDEV創建/dev的方式,配合MAKEDEV
把漏掉的設備自動補上。

udevsettle用來檢查/dev是不是已經按照我們的規則在指定時間內創建完成,不管結果如何,只是向系統寫入
一個報告,並不能夠解決所遇到的問題。

udevstart是強制性的,udevtrigger則先做判斷。

嵌入式系統上遠沒有這么復雜,所以我推薦在用udevstart來一次完成/dev的創建工作,因為現在的udevtrigger
還存在一些問題,處理uevent有時會重復創建同一個設備,創建失敗的提示看起來很不美觀。

-----------------------------------------

我看到這樣的一段說明:
Every time the kernel notices an update in the device structure, it calls the /sbin/hotplug program. Hotplug runs the applications linked in the /etc/hotplug.d/default directory where you will also find a symlink to the udev application. Hotplug directs the information given by the kernel to the udev application which performs the necessary actions on the /dev structure (creating or deleting device files).

Q1: 在嵌入式系統中只需要udevd和udevstart就夠了嗎??? 不需要udev嗎?

Q2: 不需要在/etc/hotplug.d/default 目錄下建立到udev的符號鏈接嗎? 如果不建立,hotplug怎樣通知udev了????

-----------------------------------------

這段說明已經是老皇歷了。
在古代的時候是需要hotplug的,現在這種framework已經被拋棄。
取而代之的是udevd通過netlink接口接收內核發來的消息。

-----------------------------------------

【相關資源】

Udev最新source code源碼下載:

http://www.us.kernel.org/pub/linux/utils/kernel/hotplug/

【相關文章】

1.Udev
2.The README file in the udev source tree.
3.A OLS 2003 paper about how udev was originally designed, and why it was created.
4.The OLS 2003 presentation that went along with the paper.
5.An article about udev and how it works was published in Linux Journal in 2004.
6.Writing udev rules
7.A general udev primer
8.The last word on how udev and devfs compare.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM