1. 程式人生 > >Linux那些事兒 之 我是PCI(4)初始化(一)

Linux那些事兒 之 我是PCI(4)初始化(一)

解析完了PCI的那些核心引數,再翻過多少座山跨過多少條河,核心就會遇到init/main.c裡一個名叫do_initcalls的函式。do_initcalls對核心來說只不過是漫長冒險旅程中的一個驛站,對PCI這個故事來說卻是命運轉輪的開始,核心在它裡邊完成了對.initcall.init節裡各種xxx_initcall函式的執行,PCI的那些自然也包括在內。你不用像新東方老羅“我走來走去,為中國的命運苦苦思索。”那樣走來走去為PCI的命運思索,因為決定PCI命運的那些xxx_initcall早列在之前的那張表裡了,也不用你再去驀然回首,這裡會再貼一遍。

檔案 函式 入口 記憶體位置
arch/i386/pci/acpi.c pci_acpi_init subsys_initcall .initcall4.init
arch/i386/pci/common.c pcibios_init subsys_initcall .initcall4.init
arch/i386/pci/i386.c pcibios_assign_resources
fs_initcall .initcall5.init
arch/i386/pci/legacy.c pci_legacy_init subsys_initcall .initcall4.init
drivers/pci/pci-acpi.c acpi_pci_init arch_initcall .initcall3.init
drivers/pci/pci- driver.c pci_driver_init postcore_initcall .initcall2.init
drivers/pci/pci- sysfs.c pci_sysfs_init late_initcall .initcall7.init
drivers/pci/pci.c pci_init device_initcall .initcall6.init
drivers/pci/probe.c pcibus_class_init postcore_initcall .initcall2.init
drivers/pci/proc.c pci_proc_init __initcall .initcall6.init
arch/i386/pci/init.c pci_access_init arch_initcall .initcall3.init
咱們從講USB Core時就已經知道對這些xxx_initcall函式的呼叫是必須按照一定順序的,先呼叫.initcall1.init中的再呼叫.initcall2.init中的,很明顯,表裡列出來的應該最先被呼叫的是.initcall2.init子節中的兩個函式pcibus_class_init和pci_driver_init。現在問題出現了,對於處於同一子節中的那些函式,比如pcibus_class_init和pci_driver_init這兩個函式來說又是哪個會最先被呼叫?當然,你可以說處在前邊兒地址的會最先被呼叫,這是大實話,因為do_initcalls函式的實現就是在.initcall.init所處的地址上來回的for迴圈。可你怎麼知道同一子節的函式哪個在前邊兒哪個在後邊兒? 別的不多說,先看看gcc的Using the GNU Compiler Collection中的一段話: the linker searches and processes libraries and object files in the order they are specified. Thus, ‘foo.o -lz bar.o’ searches library ‘z’ after file ‘foo.o’ but before ‘bar.o’. 看完這段話,希望會聽到你說:我悟道了!更希望會看到你翻出來drivers/pci/Makefile檔案,瞅到下邊兒這兩行 5 obj-y           += access.o bus.o probe.o remove.o pci.o quirks.o / 6                         pci-driver.o search.o pci-sysfs.o rom.o setup-res.o probe.o在pci-driver.o的前面,那麼probe.c裡的pcibus_class_init函式也會在pci- driver.c裡的pci_driver_init函式之前被呼叫。再給你看一句話,Documents/kbuild/makefile.txt的3.2中的:       The order of files in $(obj-y) is significant. 既然pcibus_class_init會首先被呼叫,那咱們就先窺視一下它的廬山真面目 class_register是裝置模型中一個很基礎的函式,在這裡它的目的就是註冊一個名叫“pci_bus”的class,關於class,你應該不會感到陌生了,usb那裡就已經註冊過一個usb_host,不過不同的是那時使用的是class_create,而現在使用的是class_register,咱不陪寫程式碼的哥們兒玩這些文字遊戲了,不管它是create還是register,咱只看它們能夠帶來啥後果,當然所謂的後果要體現在sysfs上,所以去look一下/sys/class目錄

atmdmagraphicshwmoni2c-adapterinput

memmiscnetpci_busscsi_devicescsi_disk

scsi_hostsoundspi_hostspi_masterspi_transporttty

usb_deviceusb_endpointusb_hostvcvtconsole

想當初,usb子系統初始化的時候,呼叫了一次class_create(THIS_MODULE, "usb_host"),然後上邊兒就多了一個usb_host目錄,那麼現在呼叫這個class_register,上邊兒又會多出什麼?這個大家一眼就能看出來,即使一眼看不出來兩眼也能看出來了,賺錢買豬肉的本事沒有,尋找這種敏感地帶的本事還都是有的,憑空多出來的就是那個pci_bus。從這點兒看,create還是register對咱們來說都差不多,都是在/sys/class下邊兒建立了一個類,usb_host類的目錄裡是各個具體的主機控制器,pci_bus類的目錄裡對應的就是各個pci匯流排了。本來難得糊塗一下明白這些就成了,不過如果真想稍微不那麼糊塗一點兒,可以去掃兩眼class_create的定義,你就會發現它裡面最終也會呼叫一個class_register,這兩個的差別就是class_create要更傻瓜一些,你指定個類的名稱就可以呼叫它了,它裡面會幫你建立一個struct class結構體,而class_register則更費事一些,你需要自己親自動手建立一個struct class結構體。如果你覺得自己挺特殊,需要指定自己的release函式等,那就必須得使用class_register了,PCI就屬於這種情況,至於它怎麼個特殊,就是後話了。

pcibus_class_init之後,接著就應該是pci_driver_init

還記得linux裝置模型裡存在於匯流排、裝置、驅動之間的那個著名的三角關係麼?如果不記得,那就先聽俺講個小故事:

     話說多少年以前有個人非常的健忘,他老婆很無奈,就對他說:“聽說南村的誰誰誰專治女性不孕男性健忘,你還是去找他醫一下吧!” 好男人準則第一條就是要聽老婆的話,於是這個人就背上弓箭,騎上馬出發了。人不是都有三急麼,半道兒上他想大便,就把馬拴在一棵大樹上,躲在樹後,順手把箭插在地上。方便過後,正在順爽,順爽,一順再順,順出新自我,忽然看見了地上的箭,驚出了一身冷汗,“好險,不知誰射來的箭,差點要了我的小命!”緊趕的往外跑,一腳踩在大便上,不禁連皺眉頭,大罵不已:“誰這麼缺德,不講公共衛生,在這裡隨地大小便……”。等到看到拴在樹上的馬,又高興起來,心想:雖然吃了點兒苦頭,撿到一匹馬著實得美,就像金帝美滋滋巧克力,全新的色彩,全新的味道。於是他騎上馬暈暈乎乎不知所之,沿著原路折了回去。一邊想著我這是在哪兒呢,一邊瞧見了一座房子。“咦,這房子好生面熟?……”這個時候,他老婆正從屋裡看見他糊里糊塗的樣子,氣不打一處來,出門兒來責備他。只見他不卑不亢的作了一個揖,說:“這位大嫂,你我素昧平生,何苦出言不遜?”

這個故事的教育意義就在於告訴我們健忘是一種病態,善忘是一種境界,做人不能健忘到如此地步。那個三角關係中的匯流排落實在USB就是usb_bus_type,落實在PCI就是上面的pci_bus_type,pci_driver_init函式的目的就是註冊PCI匯流排,只有匯流排存在了,才會有裝置的那條連結串列和驅動的那條連結串列,才會有裝置和驅動之間的match。