提交者:
addylee
日期:2008/10/8 22:50
閱讀:636
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 = ,上面的資訊說明,凡是verdon id為0x10ec, device id為0x8139, 0x8138的pci裝置(subvendor id和subdeviceid為pci_any_id,表示不限制。),都可以使用這個驅動程式(8139too)。,......
}module_device_table (pci, rtl8139_pci_tbl);
alias pci:v000010ecd00008138sv*sd*bc*sc*i* 8139toov後面的000010ec說明其vendor id為10ec,d後面的00008138說明device id為8139,而sv,和sd為subvendor id和subdevice id,後面的星號表示任意匹配。alias pci:v000010ecd00008139sv*sd*bc*sc*i* 8139too
......
另外在/lib/modules/uname-r /modules.dep檔案中還儲存這模組之間的依賴關係,其內容如下:
(這裡省去了路徑資訊。)options,這裡面的各種匯流排,你只能夠選擇y或n,而不能選擇m.),並且為每乙個裝置建立乙個裝置物件。每乙個匯流排物件有乙個kset對 象,每乙個裝置物件嵌入了乙個kobject物件,kobject連線在kset物件上,這樣匯流排和匯流排之間,匯流排和裝置裝置之間就組織成一顆樹狀結構。 當匯流排驅動程式為掃瞄到的裝置建立裝置物件時,會初始化kobject物件,並把它連線到裝置樹中,同時會呼叫kobject_uevent()把這個 (新增新裝置的)事件,以及相關資訊(包括裝置的vendorid,deviceid等資訊。)通過netlink傳送到使用者態中。在使用者態的udevd 檢測到這個事件,就可以根據這些資訊,開啟/lib/modules/uname-r /modules.alias檔案,根據8139too.ko:mii.ko
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我們得到udevd的程序id為25063,現在結束這個程序:root 25063 ...... /sbin/udevd --daemon
# kill -9 25063然後跟蹤udevd,在shell中執行:
# strace -f /sbin/udevd --daemon這時,我們看到udevd的輸出如下:
......我們發現udevd在這裡被阻塞在select()函式中。close(8) = 0
munmap(0xb7f8c000, 4096) = 0
select(7, [3 4 5 6], null, null, null
select函式原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);select函式的作用是:如果readfds中的任何乙個檔案有資料可讀,或者witefds中的任何乙個檔案可以寫入,或者exceptfds 中的任何乙個檔案出現異常時,就返回。否則阻塞當前程序,直到上訴條件滿足,或者因阻塞時間超過了timeout指定的時間,當前程序被喚 醒,select返回。第乙個引數:nfds表示最大的檔案描述符號,這裡為7(明明是6 ?)。
第二個引數:readfds為讀檔案描述符集合,這裡為3,4,5,6.
第三個引數:writefds為寫檔案描述符集合,這裡為null。
第四個引數:exceptfds為異常檔案描述符集合,這裡為null。
第五個引數:timeout指定超時時間,這裡為null。
所以,在這裡udevd等待3,4,5,6這幾個檔案有資料可讀,才會被喚醒。現在,到shell中執行:
# ps aux | grep udevdudevd的程序id為27617,現在我們來看看select等待的幾個檔案:root 27615 ...... strace -o /tmp/udevd.debug -f /sbin/udevd --daemon
root 27617 ...... /sbin/udevd --daemon
# cd /proc/27615/fd由於不方便在執行中插入一塊8139的網絡卡,因此現在我們以乙個u盤來做試驗,當你插入乙個u盤後,你將會看到strace的輸出,從它的輸出可以 看到udevd在select返回後,呼叫了modprobe載入驅動模組,並呼叫了sys_mknod,在dev目錄下建立了相應的節點。# 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]
execve("/sbin/modprobe", ["/sbin/modprobe", "-q", "usb:v05acp1301d0100dc00dsc00dp00"...]這裡modprobe的引數"usb:v05ac..."對應modules.alias中的某個模組。......
mknod("/dev/sdb", s_ifblk|0660, makedev(8, 16)) = 0
......
可以通過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核心模組自動載入機制
思 考 如 果想讓核心啟動過程中自動載入某個模組該怎麼做呢?最容易想到的方法就是到 etc init.d 中新增乙個啟動指令碼,然後在 etc rcn.d 目 錄下建立乙個符號鏈結,這個鏈結的名字以s開頭,這核心啟動時,就會自動執行這個指令碼了,這樣就可以在指令碼中使用modprobe來實現自動載入...
Linux核心模組自動載入機制
標題 思考 如 果想讓核心啟動過程中自動載入某個模組該怎麼做呢?最容易想到的方法就是到 etc init.d 中新增乙個啟動指令碼,然後在 etc rcn.d 目 錄下建立乙個符號鏈結,這個鏈結的名字以s開頭,這核心啟動時,就會自動執行這個指令碼了,這樣就可以在指令碼中使用modprobe來實現自動...
Linux核心模組自動載入機制
標題 思考如果想讓核心啟動過程中自動載入某個模組該怎麼做呢?最容易想到的方法就是到 etc init.d 中新增乙個啟動指令碼,然後在 etc rcn.d 目錄下建立乙個符號鏈結,這個鏈結的名字以s開頭,這核心啟動時,就會自動執行這個指令碼了,這樣就可以在指令碼中使用modprobe來實現自動載入。...