1. 程式人生 > >基於Omapl138開發板linux3.3系統分析do_initcall()函式

基於Omapl138開發板linux3.3系統分析do_initcall()函式

參考了網上的很多內容,網上的分析基本上是基於linux2.6或者3.1的核心,對於這個函式而言,其實大同小異,但是幾乎沒有哪篇文章能一次性把我想要了解的東西全部呈現,所以自己嘗試整理如下:



do_initcalls()->

static void __init do_initcalls(void)

{

       	initcall_t *fn;
 	for (fn = __early_initcall_end; fn < __initcall_end; fn++)
 	do_one_initcall(*fn);

}


/linux-3.3/arm/kernel/vmlinux.lds中有下述定義:



__initcall_start = .;

*(.initcallearly.init) __early_initcall_end
= .;

*(.initcall0.init) 

*(.initcall0s.init) 

*(.initcall1.init) 

*(.initcall1s.init) 

*(.initcall2.init) 

*(.initcall2s.init) 

*(.initcall3.init) 

*(.initcall3s.init) 

*(.initcall4.init) 

*(.initcall4s.init) 

*(.initcall5.init) 

*(.initcall5s.init) 

*(.initcallrootfs.init) 

*(.initcall6.init) 

*(.initcall6s.init) 

*(.initcall7.init) 

*(.initcall7s.init) __initcall_end = .;


與之類似,

在include\asm-generic中有下述定義:



#define INITCALLS                                          \

       *(.initcallearly.init)                                    \

       VMLINUX_SYMBOL(__early_initcall_end)
= .;                   \

     *(.initcall0.init)                                   \

     *(.initcall0s.init)                                        \

     *(.initcall1.init)                                   \

     *(.initcall1s.init)                                        \

     *(.initcall2.init)                                   \

     *(.initcall2s.init)                                        \

     *(.initcall3.init)                                   \

     *(.initcall3s.init)                                        \

     *(.initcall4.init)                                   \

     *(.initcall4s.init)                                        \

     *(.initcall5.init)                                   \

     *(.initcall5s.init)                                        \

       *(.initcallrootfs.init)                                   \

     *(.initcall6.init)                                   \

     *(.initcall6s.init)                                        \

     *(.initcall7.init)                                   \

     *(.initcall7s.init)

#define INIT_CALLS                                                \

              VMLINUX_SYMBOL(__initcall_start)= .;                    \

              INITCALLS                                         \

              VMLINUX_SYMBOL(__initcall_end)= .;


由上面分析可以看出do_initcalls的作用就是遍歷一個指標陣列,陣列內容是對應level的函式指標, 所以這裡要乾的事情就是依次取出各個level的函式指標fn執行(*fn)回撥,
也就是實現了依次遍歷各個level的驅動程式。

接著來看do_one_initcall(*fn)這個函式做了什麼事情;

int __init_or module do_one_initcall(initcall_t fn)
{
	int count=preempt_count();
	int ret;
	if(initcall_debug)
	ret=do_one_initcall_debug(fn);
	else
	ret=fn();
	.......


省略的部分是發生錯誤後的處理函式。
我想要分析的主要是具體裝置的驅動初始化過程,所以關注的是module_init()的過程,這對應上面指標陣列的哪一段呢,可以從下面的分析中得到:



#define __initcall(fn) device_initcall(fn);

#define module_init(x) __initcall(x);

#define module_exit(x) __exitcall(x);



所以module_init(fn)與__initcall(fn)以及device_initcall(fn)這三者是等價的。



#define __define_initcall(level,fn,id) \

       static  initcall_t __initcall_##fn##id __used \

       __attribute__((__section__(".initcall"level ".init"))) = fn

->

#define __used            __attribute__((__unused__))


#define __used attribute((unused))

在gcc手冊中找到了有關的解釋:

unused:This attribute, attached to a function, means that the function is meant to be possibly unused. GCC will not produce a warning for this function.

表示該函式或變數可能不使用,這個屬性可以避免編譯器產生警告資訊



->

__attribute__((__section__(".initcall"level ".init")))=fn;

__attribute__((section("section_name")))

其作用是將作用的函式或資料放入指定名為"section_name"對應的段中,這句話的意思就是把fn放在“.initcall”
level”.init”段中,

且定義__define_initcall(level,fn,id)與__initcall_##fn##id的意義一致。
具體驅動在核心中載入順序可以從檔案Omapl138/linux3.3/System.map中得到:
在這裡用nandflash的驅動舉例:



module_init(nand_davinci_init);

module_exit(nand_davinci_exit);

展開上面的巨集:

module_init(nand_davinci_init)

->

__initcall(nand_davinci_init)

->

device_initcall(nand_davinci_init)

->

__define_initcall("6", nand_davinci_init,6)

->

static initcall_t __initcall_nand_davinvi_init6__used\

__attribue_((__section_(.”initcall6.init”)))=nand_davinci_init;


明天再具體分析其他驅動的載入流程