歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

Linux內核驅動程序初始化順序的調整

內核啟動的時候,各個驅動初始化的工作在文件init/main.c中的do_basic_setup()函數中做.

static void __init do_basic_setup(void)
{
/* drivers will send hotplug events */
init_workqueues();
usermodehelper_init();
driver_init();

#ifdef CONFIG_SYSCTL
sysctl_init();
#endif

/* Networking initialization needs a process context */
sock_init();

do_initcalls();
}

其中的driver_init()做一些核心的初始化,看看代碼就明白了.
相應的驅動程序的初始化在do_initcalls()中做.

static void __init do_initcalls(void)
{
initcall_t *call;
int count = preempt_count();

for (call = __initcall_start; call < __initcall_end; call++) {
char *msg;

if (initcall_debug) {
printk(KERN_DEBUG "Calling initcall 0x%p", *call);
print_fn_descriptor_symbol(": %s()", (unsigned long) *call);
printk("\n");
}

(*call)();

msg = NULL;
if (preempt_count() != count) {
msg = "preemption imbalance";
preempt_count() = count;
}
if (irqs_disabled()) {
msg = "disabled interrupts";
local_irq_enable();
}
if (msg) {
printk(KERN_WARNING "error in initcall at 0x%p: "
"returned with %s\n", *call, msg);
}
}

/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}

這個__initcall_start是在文件 arch/xxx/kernel/vmlinux.lds.S (其中的xxx 是你的體系結構的名稱,例如i386)
這個文件是內核ld的時候使用的.其中定義了各個sectioin,看看就明白了。
在這個文件中有個.initcall.init, 代碼如下:

__initcall_start = .;
.initcall.init : {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
 

這裡有7個初始化的優先級,內核會按照這個優先級的順序依次加載.
這些優先級是在文件include/linux/init.h 中定義的. 你注意一下宏 __define_initcall的實現就明白了.
相關代碼如下:


#define __define_initcall(level,fn) \
static initcall_t __initcall_##fn __attribute_used__ \
__attribute__((__section__(".initcall" level ".init"))) = fn

#define core_initcall(fn) __define_initcall("1",fn)
#define postcore_initcall(fn) __define_initcall("2",fn)
#define arch_initcall(fn) __define_initcall("3",fn)
#define subsys_initcall(fn) __define_initcall("4",fn)
#define fs_initcall(fn) __define_initcall("5",fn)
#define device_initcall(fn) __define_initcall("6",fn)
#define late_initcall(fn) __define_initcall("7",fn)


我們可以看到,我們經常寫的設備驅動程序中常用的module_init其實就是對應了優先級6:
#define __initcall(fn) device_initcall(fn)

#define module_init(x) __initcall(x);

____________

不過我怎麼覺的你的問題不是由於加載順序造成的?猜測而已.
我也寫過touchscreen 的驅動, 感覺touchscreen 用serio的多一些,也有用i2c的.
你應該在自己的驅動裡去封狀這些的結構,一個input_dev結構,一個是serio(或著是i2c_driver),然後去控制加載順序.
module_init 中應該是注冊i2c_driver, 然後在attach_adapter的時候再注冊input_dev.

 

 


今天在做一個驅動的時候要用到另一個驅動(I2C)提供的API,在內核初始化時碰到了一個依賴問題。

我的驅動在I2C初始化之前就運行起來了,而這時I2C提供的API還處於不可用狀態。查了很多資料,網上有人說所有使用module_init這個宏的驅動程序的起動順序都是不確定的(我沒有查到權威的資料)。

所有的__init函數在區段.initcall.init中還保存了一份函數指針,在初始化時內核會通過這些函數指針調用這些__init函數指針,並在整個初始化完成後,釋放整個init區段(包括.init.text,.initcall.init等)。

注意,這些函數在內核初始化過程中的調用順序只和這裡的函數指針的順序有關,和1)中所述的這些函數本身在.init.text區段中的順序無關。在2.4內核中,這些函數指針的順序也是和鏈接的順序有關的,是不確定的。在2.6內核中,initcall.init區段又分成7個子區段,分別是


de>.initcall1.init .initcall2.init .initcall3.init .initcall4.init .initcall5.init .initcall6.init .initcall7.initde>

當需要把函數fn放到.initcall1.init區段時,只要聲明


de>core_initcall(fn);de>

即可。

其他的各個區段的定義方法分別是:


de>core_initcall(fn) --->.initcall1.init postcore_initcall(fn) --->.initcall2.init arch_initcall(fn) --->.initcall3.init subsys_initcall(fn) --->.initcall4.init fs_initcall(fn) --->.initcall5.init device_initcall(fn) --->.initcall6.init late_initcall(fn) --->.initcall7.initde>

而與2.4兼容的initcall(fn)則等價於device_initcall(fn)。各個子區段之間的順序是確定的,即先調 用.initcall1.init中的函數指針,再調用.initcall2.init中的函數指針,等等。而在每個子區段中的函數指針的順序是和鏈接順 序相關的,是不確定的。

Copyright © Linux教程網 All Rights Reserved