udev實現原理
作者****:李先靜
相對於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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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) ;
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
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的值判斷)。
2. 來自核心的hotplug事件。如果有事件**於hotplug,它讀取該事件,建立乙個udevd_uevent_msg物件,記錄當前的訊息序列號,設定訊息的狀態為event_queued,然後並放入running_list和exec_list兩個佇列中,稍後再進行處理。
3. 來自signal handler中的事件。signal handler是非同步執行的,即使有signal產生,主程序的select並不會喚醒,為了喚醒主程序的select,它建立了乙個管道,在signal handler中,向該管道寫入長度為1個子節的資料,這樣就可以喚醒主程序的select了。
其中最主要的事件,當然是來自核心的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的實現原理
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!udev實現原理 作者 李先靜 相對於linux 來說,udev 還是乙個新事物。然而,儘管它03 年才出現,儘管它很低調 j 但它無疑已經成為linux 下不可或缺的元件了。udev 是什麼?它是如何實現的?最近研究linux 裝置管理時,花了一...
Udev 基本工作原理
udev 基本工作原理 udev的 http www.kernel.org pub linux utils kernel hotplug udev.html 此外,關於udev的rules規則的撰寫,網上也有很多文章,假如要獲得最準確的版本,能夠在src code的 樹里找到writing udev...
udev 事件處理
處理 hotplug 傳統的方法是,在核心中執行乙個稱為 hotplug 的程式,相關引數通過環境變數傳遞過來,再由 hotplug 通知其它關注 hotplug 事件的應用程式。這樣做不但效率低下,而且感覺也不那麼優雅。新的方法是採用 netlink 實現的,這是一種特殊型別的 socket 專門...