linux核心中udevd的模組自動載入機制。

2021-05-25 05:17:39 字數 4777 閱讀 2363

www.osplay.org

摘要:本文討論了linux核心中udevd的模組自動載入機制。

標題

思 考

如果想讓核心啟動過程中自動載入某個模組 該怎麼做呢?最容易想到的方法就是到/etc/init.d/中新增乙個啟動指令碼,然後在/etc/rcn.d/目錄下建立乙個符號鏈結,這個鏈結的名字 以s開頭,這核心啟動時,就會自動執行這個指令碼了,這樣就可以在指令碼中使用modprobe來實現自動載入。但是我們發現,核心中載入了許多硬體裝置的驅 動,而搜尋/etc目錄,卻沒有發現任何指令碼負責載入這些硬體裝置驅動程式的模組。那麼這些模組又是如何被載入的呢?

id, subvendor id的裝置提供服務。以pci裝置為例,它是通過乙個pci_device_id的資料結構來實現這個功能的。例如:rtl8139的 pci_device_id定義為:

static struct pci_device_id rtl8139_pci_tbl = ,

,......

}module_device_table (pci, rtl8139_pci_tbl);

上面的資訊說明,凡是verdon id為0x10ec, device id為0x8139, 0x8138的pci裝置(subvendor id和subdeviceid為pci_any_id,表示不限制。),都可以使用這個驅動程式(8139too)。

alias pci:v000010ecd00008138sv*sd*bc*sc*i* 8139too

alias pci:v000010ecd00008139sv*sd*bc*sc*i* 8139too

......

v後面的000010ec說明其vendor id為10ec,d後面的00008138說明device id為8139,而sv,和sd為subvendor id和subdevice id,後面的星號表示任意匹配。

另外在/lib/modules/uname-r /modules.dep檔案中還儲存這模組之間的依賴關係,其內容如下:

(這裡省去了路徑資訊。)

8139too.ko:mii.ko

options,這裡面的各種匯流排,你只能夠選擇y或n,而不能選擇m.),並且為每乙個裝置建立乙個裝置物件。每乙個匯流排物件有乙個kset對 象,每乙個裝置物件嵌入了乙個kobject物件,kobject連線在kset物件上,這樣匯流排和匯流排之間,匯流排和裝置裝置之間就組織成一顆樹狀結構。 當匯流排驅動程式為掃瞄到的裝置建立裝置物件時,會初始化kobject物件,並把它連線到裝置樹中,同時會呼叫kobject_uevent()把這個 (新增新裝置的)事件,以及相關資訊(包括裝置的vendorid,deviceid等資訊。)通過netlink傳送到使用者態中。在使用者態的udevd 檢測到這個事件,就可以根據這些資訊,開啟/lib/modules/uname-r /modules.alias檔案,根據

alias pci:v000010ecd00008138sv*sd*bc*sc*i* 8139too

得知這個新掃瞄到的裝置驅動模組為8139too。於是modprobe就知道要載入8139too這個模組了,同時modprobe根據 modules.dep檔案發現,8139too依賴於mii.ko,如果mii.ko沒有載入,modprobe就先載入mii.ko,接著再載入 8139too.ko。在你的shell中,執行:

# ps aux | grep udevd

root 25063 ...... /sbin/udevd --daemon

我們得到udevd的程序id為25063,現在結束這個程序:

# kill -9 25063

然後跟蹤udevd,在shell中執行:
# strace -f /sbin/udevd --daemon

這時,我們看到udevd的輸出如下:
......

close(8) = 0

munmap(0xb7f8c000, 4096) = 0

select(7, [3 4 5 6], null, null, null

我們發現udevd在這裡被阻塞在select()函式中。

select函式原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

第乙個引數:nfds表示最大的檔案描述符號,這裡為7(明明是6 ?)。

第二個引數:readfds為讀檔案描述符集合,這裡為3,4,5,6.

第三個引數:writefds為寫檔案描述符集合,這裡為null。

第四個引數:exceptfds為異常檔案描述符集合,這裡為null。

第五個引數:timeout指定超時時間,這裡為null。

select函式的作用是:如果readfds中的任何乙個檔案有資料可讀,或者witefds中的任何乙個檔案可以寫入,或者exceptfds 中的任何乙個檔案出現異常時,就返回。否則阻塞當前程序,直到上訴條件滿足,或者因阻塞時間超過了timeout指定的時間,當前程序被喚 醒,select返回。

所以,在這裡udevd等待3,4,5,6這幾個檔案有資料可讀,才會被喚醒。現在,到shell中執行:

# ps aux | grep udevd

root 27615 ...... strace -o /tmp/udevd.debug -f /sbin/udevd --daemon

root 27617 ...... /sbin/udevd --daemon

udevd的程序id為27617,現在我們來看看select等待的幾個檔案:

# cd /proc/27615/fd

# ls -l

udevd的標準輸入,標準輸出,標準錯誤全部為/dev/null.

0 -> /dev/null

1 -> /dev/null

2 -> /dev/null

udevd在下面這幾個檔案上等待。

3 -> /inotify

4 -> socket:[331468]

5 -> socket:[331469]

6 -> pipe:[331470]

7 -> pipe:[331470]

由於不方便在執行中插入一塊8139的網絡卡,因此現在我們以乙個u盤來做試驗,當你插入乙個u盤後,你將會看到strace的輸出,從它的輸出可以 看到udevd在select返回後,呼叫了modprobe載入驅動模組,並呼叫了sys_mknod,在dev目錄下建立了相應的節點。

execve("/sbin/modprobe", ["/sbin/modprobe", "-q", "usb:v05acp1301d0100dc00dsc00dp00"...]

......

mknod("/dev/sdb", s_ifblk|0660, makedev(8, 16)) = 0

......

這裡modprobe的引數"usb:v05ac..."對應modules.alias中的某個模組。

可以通過udevmonitor來檢視核心通過netlink傳送給udevd的訊息,在shell中執行:

# udevmonitor --env 

然後再插入u盤,就會看到相關的傳送給udevd的訊息。

== 核心處理過程 ==:

int pci_bus_add_device(struct pci_dev *dev)

device_add()**如下:

int device_add(struct device *dev)

device_add()在準備好相關資料結構後,會呼叫kobject_uevent(),把這個訊息傳送到使用者空間的udevd。

int kobject_uevent(struct kobject *kobj, enum kobject_action action)

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext)

}......

/* 通過netlink傳送訊息,這樣使用者態的udevd程序就會從select()函式返回,並做相應的處理。 */

#if defined(config_net)

/* send netlink message */

if (uevent_sock)

netlink_cb(skb).dst_group = 1;

netlink_broadcast(uevent_sock, skb, 0, 1, gfp_kernel);}}

#endif

......

return retval;

}

現在我們知道/dev目錄下的裝置檔案是由udevd負責建立的,但是在核心啟動過程中,需要mount乙個根目錄,通常我們的根目錄是在硬碟上, 比如:/dev/sda1, 但是硬碟對應的驅動程式沒有載入前,/dev/sda1是不存在的, 如果沒有/dev/sda1,就不能通過mount /dev/sda1 /來掛載根目錄。另一方面udevd是乙個可執行檔案,如果連硬碟驅動程式到沒有載入,根目錄都不存在,udevd就不能執行。如果udevd不能執行, 那麼就不會自動載入磁碟驅動程式,也就不能自動建立/dev/sda1。這不是死鎖了嗎?那麼你的linux是怎麼啟動的呢?

Linux核心中的list for each

在linux核心原始碼中,經常要對鍊錶進行操作,其中乙個很重要的巨集是list for each entry 意思大體如下 假設只有兩個結點,則第乙個member代表head,list for each entry的作用就是迴圈遍歷每乙個pos中的member子項。巨集list for each e...

Linux核心中的Namespace

linux核心中的namespace提供了乙個輕量級的基於系統呼叫層面的虛擬化解決方案。相比傳統的使用 vmware,qemu,xen,kvm,hurd的虛擬 圖1所示 基於namespace的輕量級虛擬具有易使用,易管理,無需硬體虛擬化支援,低 成本等優點。圖 1.namespace又稱conta...

linux核心中的 config

經過make menuconfig 生成 config 決定哪些驅動編譯到核心,哪些驅動編譯為模組 那為什麼呢?舉個例子分析一下 eg 在.config 中有 config dm9000 y grep config dm9000 r 1.c檔案中 arch arm mach s5pv210 mach...