4.一樣的精靈,不一樣的API(1)
usb_register()這個函數是用來向USB核心層,即USB Core,註冊一個USB設備驅動的,而這裏我們註冊的是Hub的驅動程序所對應的struct usb_driver結構體變量。定義於drivers/usb/ core/hub.c中:
- 2841 static struct usb_driver hub_driver = {
- 2842 .name = "hub",
- 2843 .probe = hub_probe,
- 2844 .disconnect = hub_disconnect,
- 2845 .suspend = hub_suspend,
- 2846 .resume = hub_resume,
- 2847 .pre_reset = hub_pre_reset,
- 2848 .post_reset = hub_post_reset,
- 2849 .ioctl = hub_ioctl,
- 2850 .id_table = hub_id_table,
- 2851 .supports_autosuspend = 1,
- 2852 };
這裏面最重要的一個函數就是hub_probe,很多事情都在這期間發生了。每個USB設備(或者說所有設備)的驅動都會有一個probe函數,比如U盤的probe函數就是storage_probe(),不過storage_probe()被調用需要有兩個前提:第一個前提是usb-storage被加載了,第二個前提是U盤等設備插入了被檢測到了。
而Hub,說它特別,我可絕不是"忽悠"你。Hub本身就有兩種:一種是普通的Hub,一種是Root Hub。對於普通Hub,它完全可能也和U盤一樣,在某個時刻被插入,然後在這種情況下hub_probe被調用,但是對於Root Hub就不需要這麼多廢話了,Root Hub肯定是有的,只要你有USB主機控制器,就一定會有Root Hub,所以hub_probe()基本上很自然地就被調用了,不用說非得等待某個插入事件的發生,沒這個必要。當然沒有USB主機控制器就沒有USB設備能工作。那麼USB Core這整個模塊你就沒有必要分析了。所以,只要你有USB主機控制器,那麼在USB主機控制器的驅動程序初始化的過程中,它就會調用hub_probe()來探測Root Hub,不管你的主機控制器是OHCI、UHCI還是EHCI的接口。
如果register一切順利的話,那麼返回值爲0。如果返回值爲負數,就說明出錯了。現在假設這一步沒有出錯。
usb_hub_init()的2862行,這行代碼其實是很有技術含量的,不過對於寫驅動的人來說,其作用就和當年的kernel_thread()相當。不過kernel_thread()返回值是一個int型的,而kthread_run()返回的卻是struct task_struct結構體指針。這裏等號左邊的khubd_task是我們自己定義的一個struct task_struct指針:
- 88 static struct task_struct *khubd_task;
struct task_struct不用多說,記錄進程的數據結構。每一個進程都用一個struct task_struct結構體變量來表示。所以這裏所做的就是記錄下創建好的內核進程,以便日後要卸載模塊時可以用另一個函數來結束這個內核進程(你也可以叫內核線程),到時我們會調用kthread_stop(khubd_task)函數來結束這個內核線程,這個函數的調用我們將會在usb_hub_cleanup()函數中看到。而usb_hub_cleanup()正是Hub裏面和usb_hub_init()相對應的函數。
2863行,判斷khubd_task,IS_ERR是一個宏,用來判斷指針的。當你創建了一個進程,你當然想知道這個進程創建成功了沒有。
以前我們注意到每次申請內存時都會做一次判斷,你說創建進程是不是也要申請內存?不申請內存誰來記錄struct task_struct?很顯然,要進行判斷。以前我們判斷的是指針是否爲空。以後接觸代碼多了你會發現,其實Linux內核中有很多種內存申請的方式,而這些方式所返回的內存地址也是不一樣的,所以並不是每一次我們都只要判斷指針是否爲空就可以了。事實上,每一次調用kthread_run()之後,我們都會用一個IS_ERR()來判斷指針是否有效。IS_ERR()爲1就表示指針有錯,或者準確一點說叫做指針無效。
什麼叫指針無效?後面會專門解釋,讓我們繼續往下看,只需要記得,如果你不希望發生缺頁異常這樣的錯誤的話,每次調用完kthread_run()之後要用IS_ERR()來檢測返回的指針。如果IS_ERR()返回值是0,那麼說明沒有問題,於是返回值爲 0,也就是說usb_hub_init()就這麼結束了。反之,就會執行usb_deregister(),因爲內核線程沒有成功創建,hub就沒法驅動起來了。
最後函數在2870行,返回值爲-1。回到usb_init()函數中我們會知道,接下來usb_hub_ cleanup()就會被調用。usb_hub_cleanup()同樣定義於drivers/usb/core/hub.c中:
- 2873 void usb_hub_cleanup(void)
- 2874 {
- 2875 kthread_stop(khubd_task);
- 2876
- 2877 /*
- 2878 * Hub resources are freed for us by usb_deregister. It calls
- 2879 * usb_driver_purge on every device which in turn calls that
- 2880 * devices disconnect function if it is using this driver.
- 2881 * The hub_disconnect function takes care of releasing the
- 2882 * individual hub resources. -greg
- 2883 */
- 2884 usb_deregister(&hub_driver);
- 2885 } /* usb_hub_cleanup() */
這個函數我想沒有任何必要解釋了吧。kthread_stop()和剛纔的kthread_run()對應,usb_deregister()和usb_register()對應。
總之,如果創建子進程出了問題,那麼一切都免談。
反之,如果成功了,那麼kthread_run()的三個參數就是我們要關注的了:第一個是hub_thread(),子進程將從這裏開始執行。第二個是hub_thread(),傳遞的是NULL,第三個參數就是精靈進程的名字ps -el,如下所示:
- localhost:/usr/src/linux-2.6.22/drivers/usb/core # ps -el | grep khubd
- 1 S 0 1963 27 0 70 -5 - 0 hub_th ? 00:00:00 khubd
你就會發現有這麼一個精靈進程運行着。所以,下一步,讓我們進入hub_thread()來查看這個子進程吧。
以下是關於IS_ERR的介紹文字。如果你對內存管理沒有任何興趣,就不用往下看了。要想明白IS_ERR(),首先你得知道有一種空間叫做內核空間,不清楚也不要緊。結合IS_ERR()的代碼來看,來自include/linux/err.h:
- 16 #define MAX_ERRNO 4095
- 17
- 18 #ifndef __ASSEMBLY__
- 19
- 20 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
- 21
- 22 static inline void *ERR_PTR(long error)
- 23 {
- 24 return (void *) error;
- 25 }
- 26
- 27 static inline long PTR_ERR(const void *ptr)
- 28 {
- 29 return (long) ptr;
- 30 }
- 31
- 32 static inline long IS_ERR(const void *ptr)
- 33 {
- 34 return IS_ERR_VALUE((unsigned long)ptr);
- 35 }
- 36
- 37 #endif