如何修改设备驱动的加载顺序

来源:互联网 发布:pos机显示网络连接失败 编辑:程序博客网 时间:2024/05/22 06:40

内核启动的时候,各个驱动初始化的工作在文件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对应的是
#define module_init(x) __initcall(x);

#define __initcall(fn) device_initcall(fn)
所以,驱动对应的加载的优先级为6

在上面的不同的优先级中,
数字越小,优先级越高。
同一等级的优先级的驱动,加载顺序是链接过程决定的,由driver/Makefile确定的,改变这个makefile的内容手动设置谁先谁后。
不同等级的驱动加载的顺序是先优先级高,后优先级低。
我们可以看到,我们经常写的设备驱动程序中常用的module_init其实就是对应了优先级6:
#define __initcall(fn) device_initcall(fn)

#define module_init(x)        __initcall(x);

同一个等级的情况下注册顺序跟代码链接的顺序有关系,修改driver/Makefile的内容。
加载顺序可以从system.map中看到设备的注册函数的地址。
原创粉丝点击