《Linux裝置模型 (2)》和《Linux裝置模型 (3)》主要通過一些簡單的實作介紹了kobject、kset、kobj_type、attribute等資料結構的用法,但這些實作並沒有涉及到實際環境下的裝置模型和sysfs。本文將以/sys下的module子目錄為例,看看核心是如何構建sysfs這棵大樹的。
(註:本文的分析基於2.6.36核心)
module的建立
當module被insmod到核心空間時,/sys/module目錄下會相應建立一個和模組同名的目錄。我們以usb_storage為例,在執行完sudo modprobe usb_storage之後,sysfs裡會產生一個名為usb_storage的目錄,其目錄結構是:
在Linux 2.6核心裡,module的插入是由使用者程式insmod(modprobe最終也是調用insmod)發起的,但大部分工作還是由核心完成。我們可以用strace來觀察一下insmod的流程。
stat64("/sys/module/usb_storage", 0xbfb9a654) = -1 ENOENT (No such file or directory)
open("/lib/modules/2.6.35-24-generic/kernel/drivers/usb/storage/usb-storage.ko", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=94776, ...}) = 0
mmap2(NULL, 94776, PROT_READ|PROT_WRITE, MAP_PRIVATE, 3, 0) = 0xb776f000
close(3) = 0
init_module(0xb776f000, 94776, "") = 0
munmap(0xb776f000, 94776) = 0
exit_group(0) = ?
從這個流程我們可以看到,insmod在執行時,首先會把module的內容映射到記憶體裡,然後呼叫系統調用init_module來實現真正的工作。
如所示,這是系統調用init_module的執行路徑。因為本文只是討論裝置模型和sysfs,流程圖裡只涉及到相關的內容。
1、mod_sysfs_init首先會查詢當前模組(本例中是usb_storage)在sysfs中是否已經存在,如果沒有,則調用kobject_init_and_add建立之;
2、調用kobject_create_and_add建立holders目錄。holders目錄用於存放指向其他module的連結,這裡的其他module都是依賴於當前module的。以usb_storage為例,如果我們需要使用ums_XX模組(比如ums_karma或者ums_freecom等),可以調用sudo modprobe ums_XX來完成載入,因為ums_XX依賴於usb_storage,所以在usb_storage/holders目錄下就會建立指向ums_XX的連結,同時refcnt也會加1;
3、module_param_sysfs_setup用來建立parameters目錄,這個目錄裡的檔案對應著當前module的所有參數。在Linux核心裡,module的二進位ko檔案中的__param section用來儲存當前module的參數,load_module會把這些參數讀取到記憶體結構裡,module_param_sysfs_setup再根據相應的結構來建立paramters目錄及其參數檔案;
4、module_add_modinfo_attrs用來建立當前module目錄下的4個檔案:version、srcversion、refcnt和initstate,其中version和srcversion的資訊儲存在二進位ko檔案的.modinfo section裡。對於usb_storage模組來說,並沒有指定version,所以不存在version這個檔案。順便羅嗦一句,在Linux核心裡,可以用宏MODULE_VERSION定義版本號碼,比如MODULE_VERSION("v1.00")定義版本號碼為v1.00,這裡的版本資訊完全是字串,並無特定格式。srcversion可以由MODULE_INFO(srcversion, xxx)來定義,但一般情況下由modpost預設產生就可以了。refcnt反映當前module的引用計數。initstate反映module的三種狀態:live、coming和going;
5、add_usage_links和holders是密切相關的,但這個函數並不是操作當前module的holders目錄。以ums_XX為例,在ums_XX的載入過程中,其add_usage_links會把自己作為連結加到usb_storage的holders目錄下;
6、sections目錄對應當前module的二進位ko檔案裡的section資訊,這是通過add_sect_attrs實現的。需要提醒一下的是,section的命名通常以“.”開頭,而以“.”開頭的檔案在Linux裡被認為是隱藏檔案,所以如果要察看的話要在ls命令後加"-a"參數,下面談到的notes同樣需要這樣處理;
7、add_notes_attrs用來建立notes目錄。ELF檔案格式定義了一種名為note的元素,主要用於給二進位檔案添加一些標示資訊。通常,我們可以用readelf來察看ELF檔案是否包含note section。比如usb_storage,我們可以使用命令“readelf usb-storage.ko -n”察看,其輸出如下:
Notes at offset 0x00000034 with length 0x00000024:
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
相應的,在/sys/module/usb_storage/notes目錄下建立的notes檔案就是.note.gnu.build-id。
上面的7條流程裡,並無建立drivers的地方,那/sys/module/usb_storage下的drivers目錄是如何建立的呢?答案在:
在usb_storage的module_init中,會調用usb_register來註冊usb_driver結構,中所示,最終會調用module_add_driver來建立drivers目錄。
module的撤銷
module的撤銷過程和前文中的建立過程一一對應,在這裡就不詳細敘述了,請看。
關於sysfs中的其他子目錄的建立和撤銷,大家也可以很容易的在Linux核心中找到對應的代碼,本文不再一一贅述。