茫茫網海中的冷日 - 對這文章發表回應
茫茫網海中的冷日
         
茫茫網海中的冷日
發生過的事,不可能遺忘,只是想不起來而已!
 恭喜您是本站第 1676313 位訪客!  登入  | 註冊
主選單

Google 自訂搜尋

Goole 廣告

隨機相片
PI20101106_00006.jpg

授權條款

使用者登入
使用者名稱:

密碼:


忘了密碼?

現在就註冊!

對這文章發表回應

發表限制: 非會員 可以發表

發表者: 冷日 發表時間: 2012/7/10 13:54:09

udev實現原理

             

轉載時請註明出處和作者聯繫方式: http://blog.csdn.net/absurd

作者聯繫方式: 李先靜<xianjimli at hotmail dot com>

更新時間: 2007-4-29

 

相對於
linux 來說, udev 還是一個新事物。然而,儘管它 03 年才出現,儘管它很低調 ( J ) ,但它無疑已經成為 linux 下不可或缺的組件了。 udev 是什麼?它是如何實現的?最近研究 Linux 設備管理時,花了一些時間去研究 udev 的實現。

 


udev
是什麼? u 是指 user space dev 是指 device udev 是用戶空間的設備驅動程序嗎?最初我也這樣認為,調試內核空間的程序要比調試用戶空間的程序複雜得多,內核空間的程序的 BUG 所引起的後果也嚴重得多, device driver 是內核空間中所佔比較最大的代碼,如果把這些 device driver 中硬件無關的代碼,從內核空間移動到用戶空間,自然是一個不錯的想法。

 


但我的想法並不正確, udev 的文檔是這樣說的,

1.          dynamic replacement for /dev 。作為 devfs 的替代者,傳統的 devfs 不能動態分配 major minor 的值,而 major minor 非常有限,很快就會用完了。 udev 能夠像
DHCP 動態分配 IP 地址一樣去動態分配 major minor

 

2.          device naming 。提供設備命名持久化的機制。傳統設備命名方式不具直觀性,像 /dev/hda1 這樣的名字肯定沒有 boot_disk 這樣的名字直觀。 udev 能夠像
DNS 解析域名一樣去給設備指定一個有意義的名稱。

 

3.          API to access info about current system devices 。提供了一組易用的 API 去操作 sysfs ,避免重複實現同樣的代碼,這沒有什麼好說的。

 

我們知道,用戶空間的程序與設備通信的方法,主要有以下幾種方式,

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>

 

static int init_hotplug_sock ( void )

{

    struct sockaddr_nl snl ;


    const int buffersize = 16 * 1024 * 1024;

    int retval ;

 

    memset (& snl , 0x00, sizeof ( struct sockaddr_nl));


   
snl .nl_family = AF_NETLINK;

    snl .nl_pid = getpid ();

    snl .nl_groups = 1;

 

    int hotplug_sock = socket (PF_NETLINK, SOCK_DGRAM , NETLINK_KOBJECT_UEVENT);

    if ( hotplug_sock == -1) {

        printf ( "error getting socket: %s" , strerror ( errno ));

        return -1;

   }


 

    /* set receive buffersize */

    setsockopt ( hotplug_sock , SOL_SOCKET , SO_RCVBUFFORCE, & buffersize , sizeof ( buffersize ));

 

    retval =
bind ( hotplug_sock , ( struct sockaddr *) & snl , sizeof ( struct sockaddr_nl));

    if ( retval < 0) {

        printf ( "bind failed: %s" , strerror
( errno ));

        close ( hotplug_sock );

        hotplug_sock = -1;

        return -1;

   }

 


   
return hotplug_sock ;

}

 

#define UEVENT_BUFFER_SIZE      2048

 

int main ( int argc , char * argv [])

{

         int hotplug_sock       = init_hotplug_sock ();

        

         while (1)

        {


                  
char buf [ UEVENT_BUFFER_SIZE *2] = {0};

                   recv ( hotplug_sock , & buf , sizeof ( buf ), 0);  


                  
printf ( "%s/n" , buf );

        }

 

         return 0;

}

 

編譯:


gcc -g hotplug.c -o hotplug_monitor

 

運行後插 / U 盤,可以看到:

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0

add@/class/scsi_host/host2


add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83

add@/class/usb_device/usbdev2.2

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0

add@/class/scsi_disk/2:0:0:0

add@/block/sda

add@/block/sda/sda1


add@/class/scsi_device/2:0:0:0

add@/class/scsi_generic/sg0

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83

remove@/class/scsi_generic/sg0

remove@/class/scsi_device/2:0:0:0

remove@/class/scsi_disk/2:0:0:0


remove@/block/sda/sda1

remove@/block/sda

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0

remove@/class/scsi_host/host2

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0

remove@/class/usb_device/usbdev2.2

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1


 

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"

 

hal 正是我們下一步要關心的,接下來我會分析 HAL 的實現原理。

 

~~end~~

 


原文出處:udev的实现原理 - Linux mobile development - 博客频道 - CSDN.NET
內容圖示
url email imgsrc image code quote
樣本
bold italic underline linethrough   












 [詳情...]
validation picture

注意事項:
預覽不需輸入認證碼,僅真正發送文章時才會檢查驗證碼。
認證碼有效期10分鐘,若輸入資料超過10分鐘,請您備份內容後,重新整理本頁並貼回您的內容,再輸入驗證碼送出。

選項

Powered by XOOPS 2.0 © 2001-2008 The XOOPS Project|