1. 程式人生 > >Linux學習筆記之內核啟動流程與模塊機制

Linux學習筆記之內核啟動流程與模塊機制

oid img 相關 call rootfs _exit alt 執行 分模塊

本文旨在簡單的介紹一下Linux的啟動流程與模塊機制:

Linux啟動的C入口位於/Linux.2.6.22.6/init/main.c::start_kernel()

下圖簡要的描述了一下內核初始化的流程:

技術分享

本文我們分析一下do_initcalls ()函數,他負責大部分模塊的初始化(比如U盤驅動就是在這裏被初始化的)。

 1 static void __init do_initcalls(void)
 2 {
 3     initcall_t *call;
 4     int count = preempt_count();
 5 
 6     for (call = __initcall_start
; call < __initcall_end; call++) { 7 ktime_t t0, t1, delta; 8 char *msg = NULL; 9 char msgbuf[40]; 10 int result; 11 .... 12 } 13 }

它會循環執行一段區域裏面的內容,那麽這段區域裏面是什麽呢?

先把相關的定義貼出來:

1         __initcall_start = .;
2             INITCALLS
3         __initcall_end = .;
 1 #define INITCALLS                             2       *(.initcall0.init)                         3       *(.initcall0s.init)                         4       *(.initcall1.init)                         5       *(.initcall1s.init)                         6       *(.initcall2.init)                        
7 *(.initcall2s.init) 8 *(.initcall3.init) 9 *(.initcall3s.init) 10 *(.initcall4.init) 11 *(.initcall4s.init) 12 *(.initcall5.init) 13 *(.initcall5s.init) 14 *(.initcallrootfs.init) 15 *(.initcall6.init) 16 *(.initcall6s.init) 17 *(.initcall7.init) 18 *(.initcall7s.init)
1 #define __define_initcall(level,fn,id) 2     static initcall_t __initcall_##fn##id __attribute_used__ 3     __attribute__((__section__(".initcall" level ".init"))) = fn

這裏涉及的是鏈接器的知識,鏈接器在鏈接時會把文件分段,而do_initcalls ()裏面的for循環就是循環的某段區域(這裏指initcall段),我們把每個模塊的入口函數放到這個區域裏,這樣模塊在內核初始化的過程中就會跑到。這裏還涉及另一個重要的知識點就是:GCC支持用戶通過__attribute__來自定義段(比如通過__init修飾的函數只在初始化過程中執行一次,因為這段區域在初始化之後就被釋放掉了)。鏈接器的相關知識是理解這裏的重點,大家可以找點這方面的資料看看。

下面我們以U盤驅動程序為例,看看他是如何被加載的:

/Linux2.6.22.6/drivers/usb/storage/usb.c::

1 module_init(usb_stor_init);
2 module_exit(usb_stor_exit);

module_init()是一個宏,這裏表示指定usb_stor_init()為U盤驅動程序的入口點。

#define module_init(x)    __initcall(x);
#define __initcall(fn)    device_initcall(fn)
#define device_initcall(fn)        __define_initcall("6",fn,6)

到這裏,大概可以知道,程序在鏈接時,usb_stor_init()會被放到*(.initcall6.init)可以訪問到的地方,這樣在do_initcalls ()的for循環裏面,usb_stor_init()就會被執行。

到此,也可以大概清楚Linux的模塊機制是什麽樣子的了。

 1 #define pure_initcall(fn)                __define_initcall("0",fn,1)
 2 
 3 #define core_initcall(fn)                __define_initcall("1",fn,1)
 4 #define core_initcall_sync(fn)          __define_initcall("1s",fn,1s)
 5 #define postcore_initcall(fn)            __define_initcall("2",fn,2)
 6 #define postcore_initcall_sync(fn)       __define_initcall("2s",fn,2s)
 7 #define arch_initcall(fn)                __define_initcall("3",fn,3)
 8 #define arch_initcall_sync(fn)          __define_initcall("3s",fn,3s)
 9 #define subsys_initcall(fn)              __define_initcall("4",fn,4)
10 #define subsys_initcall_sync(fn)         __define_initcall("4s",fn,4s)
11 #define fs_initcall(fn)                    __define_initcall("5",fn,5)
12 #define fs_initcall_sync(fn)            __define_initcall("5s",fn,5s)
13 #define rootfs_initcall(fn)              __define_initcall("rootfs",fn,rootfs)
14 #define device_initcall(fn)              __define_initcall("6",fn,6)
15 #define device_initcall_sync(fn)        __define_initcall("6s",fn,6s)
16 #define late_initcall(fn)               __define_initcall("7",fn,7)
17 #define late_initcall_sync(fn)          __define_initcall("7s",fn,7s)

上面的定義表示模塊也是分優先級的,這也很好理解,還以U盤為例,內核必須先完成USB控制器以及USB總線驅動的初始化,然後U盤的驅動才能初始化成功,所以USB_Core的模塊入口定義是下面這樣的:

/Linux2.6.22.6/drivers/usb/core/usb.c::

subsys_initcall(usb_init);
module_exit(usb_exit);

Linux學習筆記之內核啟動流程與模塊機制