天天看點

[driver]linux核心動态加載子產品

問題:

把編譯好的子產品放到闆子/lib/modules對應檔案夾下,并且執行了depmod -a, 比如pl2303.ko, 那麼下一次插入pl2303的序列槽線,是否可以識别,也就是自動加載pl2303驅動?

--------------------------------------------------------------------------------------------------------------

一、安裝核心子產品

一般步驟:

  1. 在/usr/src/​​Linux​​/下運作make menuconfig把需要編譯成子產品的項打上(M),儲存并退出。
  2. 運作make modules,這一步将在/usr/src/linux/下生成*.o或*.ko檔案。
  3. 運作make modeules_install來安裝,這步會把生成的.o或ko檔案拷貝到/lib/modules/`uname -r`/下。

如果你隻要編譯某一個或幾個子產品,就可以用下面這個快速的方法:

  1. 找到編譯核心所需要的.config檔案。 在/usr/src/linux/arch目錄下有若幹編譯核心所用的配置。選擇我們想要的配置,将它複制到/usr/src/linux目錄下,改名為.config。 cp /usr/src/linux/arch/x86/xxconfig /usr/src/linux/.config
  2. 修改.config檔案,去掉不用的子產品,加上自己想要的子產品。 打開.config,有許多XXXX=m的項,這些都是要被編譯為子產品 的項,因為我們不希望編譯這些子產品,是以要把XXXX=m的項統統去掉。然後再加上我們想要的子產品,例如将# CONFIG_NTFS_FS is not set 改為CONFIG_NTFS_FS=m 當然,可以用你熟悉各種工具來做這件事。
  3. 編譯NTFS子產品。在/usr/src/linux目錄下運作指令make modules來編譯我們想要的子產品。
  4. 安裝子產品。 編譯後得到的.o檔案在/usr/src/linux/目錄下,手動将它複制到正确的目錄下。 例如cp /usr/src/linux/fs/ntfs/ntfs.o /lib/modules/2.2.16-22/fs/

千萬不能運作指令make modules_install,否則将帶來嚴重的後果,它會删除你系統中的所有子產品,隻安裝剛剛編譯的子產品(ntfs.o)。

二:安裝完成以後,我們就可以加載子產品了

和linux中加載子產品有關的幾個指令分别如下:

depmod, modprobe, lsmod

2.1.先來看看depmod指令:

depmod是一個 用來産生modules.dep和map檔案的程式。在modules.dep檔案中空白行和以'#'開頭的行将被忽略.depmod通過讀取/lib /modules/version目錄下的每一個子產品來建立一個記錄子產品相依性的清單。這個清單就是/lib/modules/version目錄下的 modules.dep。depmod也會在/lib/modules/version目錄下建立許多map檔案,例如 modules.dep,modules.isapnpmap,modules.pcimap,modules.alias這些檔案将會被hotplug 用到。

OPTIONS:

-a --all Probe all modules. This option is enabled by default if no

            file names are given in the command-line.

檢查所有的子產品,這個指令是預設的如果你沒有指定子產品名字的話。

-A --quick This option scans to see if any modules are newer than the

                 modules.dep file before any work is done%3

2.2.再來看看modprobe指令:

modprobe 指令是根據depmod -a的輸出/lib/modules/version/modules.dep來加載全部的所需要子產品。可以通過modprobe -l來顯示可以目前可以加載的子產品。modprobe 在挂載子產品是不用指定子產品檔案的路徑,也不用帶檔案的字尾.o 或.ko, 而insmod 需要的是子產品的所在目錄的絕對路徑,并且一定要帶有子產品檔案名字尾的(modulefile.o 或modulesfile.ko )。 insmod比較重要的用途是用來​​測試​​子產品的正确性,加載一般都是依靠modprobe。

用法:modprobe xxx.ko        #加載某個子產品

modprobe -r xxx.ko     #解除安裝某個子產品

2.3.lsmod:

lsmod 顯示目前加載的所有 子產品,相當于cat /proc/modules,假設你沒有設定開機加載某個子產品,比如ntfs,那麼開機後執行lsmod,清單裡不會有ntfs這個子產品的,這時你再執行 mount -t ntfs xxx後,執行lsmod後清單裡就會有ntfs這個子產品了。

還要注意的是lsmod顯示的是子產品名,而不是别名(alias)。

2.4.modinfo

顯示kernel子產品的對象檔案,以顯示該子產品的相關資訊

三、系統如何完成動态加載

在核心中有一個“Automatic kernel module loading"功能被編譯到了核心中。當使用者嘗試打開某類型的檔案時,核心會根據需要嘗試加載相應的子產品。我們來看看驅動程式自動加載是怎麼實作的:

  每一個裝置都有Verdon ID, Device ID, SubVendor ID等資訊。而每一個裝置驅動程式,必須說明自己能夠為哪些Verdon ID, Deviece

  ID, SubVendor ID的裝置提供服務。以PCI裝置為例,它是通過一個pci_device_id的​​資料結構​​來實作這個功能的。例如:RTL8139的pci_device_id定義為:

static struct pci_device_id rtl8139_pci_tbl[] = {
      {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
      {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
      ......
}      

在子產品安裝的時候,depmod會根據子產品中的rtl8139_pci_tbl的資訊,生成下面的資訊,儲存到/lib/modules/uname-r /modules.alias檔案中,其内容如下:

alias pci:v000010ECd00008138sv*sd*bc*sc*i* 8139too
alias pci:v000010ECd00008139sv*sd*bc*sc*i* 8139too
......      

另外在/lib/modules/uname-r /modules.dep檔案中還儲存這子產品之間的依賴關系,其内容如下:

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

8139too.ko:mii.ko

在核心啟動過程中,

  • 總線驅動程式會按總線協定進行總線枚舉,并且為每一個裝置建立一個裝置對象(總線驅動程式總是內建在核心之中,不能夠按子產品方式加載,你可以通過make menuconfig進入Bus 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

# kill -9 25063 然後跟蹤udevd,在shell中運作:

# strace -f /sbin/udevd --daemon

......
 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

  • 第一個參數: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中運作: 27615 ...... strace -o /tmp/udevd.debug -f /sbin/udevd --daemon root 27617  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的消息。

四、核心子產品加載的配置:

有時候需要一次性加載許多子產品,需要在一個地方統一配置modprobe的選項等,有一個比較重要的檔案: /etc/modprobe.conf , 在opensuse中,和它有關的還有modprobe.d/檔案夾下的許多檔案和 modprobe.conf.local 檔案,在 /etc/modprobe.conf 裡會include其它所有的檔案,一般建議在 modprobe.conf.local 中修改自己的配置。  /etc/modprobe.conf 其實就是用于 寫入子產品的加載指令或子產品的别名的定義等。 man modprobe.conf

alias

options

install modulename command...使用自己定義的指令去加載指定的子產品,如install fred /sbin/modprobe barney; /sbin/modprobe

              --ignore-install fred" 每次加載fred子產品的時候用的是 “/sbin/modprobe barney; /sbin/modprobe

              --ignore-install fred”指令

remove

include filename 引入其它檔案

五、核心子產品開機自動挂載:

繼續閱讀