4 一樣的精靈,不一樣的API(1)

2021-06-18 09:28:36 字數 3212 閱讀 2178

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

= ;  

這裡面最重要的乙個函式就是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  /* 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   

26  

27 static inline long ptr_err(const void *ptr)  

28   

31  

32 static inline long is_err(const void *ptr)  

33   

36  

37 #endif  

4 一樣的精靈,不一樣的API(2)

4 一樣的精靈,不一樣的api 2 關於核心空間,我只想說,所有的驅動程式都是執行在核心空間的,核心空間雖然很大,但總是有限的。而在這有限的空間中,其最後乙個page是專門保留的,也就是說,一般人不可能用到核心空間最後乙個page的指標。換句話說,你在寫裝置驅動程式的過程中,涉及的任何乙個指標,必然...

不一樣又不一樣的 木板接水

空地上豎立著n個從左到右排列的木板,它們可以把水擋住,但溢位最邊上木板的水將會流到空地上。已知木板間距都是單位1,現給定每個木板的高度,請求出總共能接住的水量?說明一點,這裡只考慮間距 寬度 和高度,不考慮第三個維度,因此水量是平方單位。木板高度分別是2,1,3,那麼我們可以接住2 2 4平方單位的...

一樣的月亮,不一樣的心情

轉眼間,又是一年的中秋佳節,這都是乙個懷鄉思親的節日,自從讀書和工作後,就很難有機會和父母 兄弟姐妹一起過了,在這個只掛燈籠但毫無節日氛圍的城市裡,你可以看到許許多多偽裝的慶祝和喜悅,一樣的月亮一樣的月光一樣的月餅,但心情卻是千差萬別,各顯千秋,最可憐的就是小朋友們了,他們失去了各種自然的 純樸的 ...