驱动开发基础 -- module_init() 和 late_initcall() 的加载顺序

在模块加载的过程中,init头文件非常重要,它定义了module_init和xxx_initcall以及相应的clearup函数,还决定了模块的加载顺序级别以及模块编译进内核和动态加载时module_init所做的不同的事情。
而vmlinux.lds文件用来决定初始化所用的内存的分布,我们看看内核初始化的内存分布:

       .init.text : {
 
        _sinittext = .;
 
        *(.init.text)
 
        _einittext = .;
 
  }
 
  .init.data : {
 
    *(.init.data);
 
    __vtop_table_begin = .;
 
    *(.vtop_fixup);
 
    __vtop_table_end = .;
 
    __ptov_table_begin = .;
 
    *(.ptov_fixup);
 
    __ptov_table_end = .;
 
  }
 
  . = ALIGN(16);
 
  __setup_start = .;
 
  .init.setup : { *(.init.setup) }
 
  __setup_end = .;
 
  __initcall_start = .;
 
  .initcall.init : {
 
        *(.initcall1.init)
 
        *(.initcall2.init)
 
        *(.initcall3.init)
 
        *(.initcall4.init)
 
        *(.initcall5.init)
 
        *(.initcall6.init)
 
        *(.initcall7.init)
 
  }
 
  __initcall_end = .;

内存的分布可以用下图来说明:
加粗样式
其中__init用来标示的是初始化函数,在初始化后不会再调用,__initdata是初始化数据, __initparam是初始化参数,其他7个初始化宏就是初始化函数会用到的。所有标示为__init的函数都会在链接的时候都会放在.init.text中,放得顺序是不确定的,与编译、链接顺序有关,同时所有__init函数都会在.initcallx.init中放一个函数指针,初始化的时候按照.initcall1.init->.initcall7.init的顺序初始化,.initcallx.init有不同的别名,在init.h中定义:

              #define __define_initcall(level,fn) \
       static initcall_t __initcall_##fn __attribute_used__ \
       __attribute__((__section__(".initcall" level ".init"))) = fn
 
 
#define early_initcall(fn)                     __define_initcall(".early1",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 __initcall(fn) device_initcall(fn)     

 #define module_init(x)  __initcall(x);

可以看出,通常用的module__init()函数都使用了devince_initcall定义,即第6个加载顺序的initcall宏。

do_initcalls函数

那么这些初始化函数在什么地方执行呢?在main.c的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)();
 
           …………
 
       /* Make sure there is no pending stuff from the initcall sequence */
 
       flush_scheduled_work();
 
}

这个函数从__initcall_start开始到__initcall_end依次执行标示为__init的函数,从而完成初始化

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
r40_tinav2.1_最终验证通过_使用CB-S来验证OV5640有横条纹fpscamera+SPI2.0成功_20171114_1443没有外层目录.7z 开发板:CB-S 1、(可选修改/调试技巧:) 除了ov5640.c之外,其它的驱动都不编译,节省编译时间! W:\ov5640_spi20_r40t\lichee\linux-3.10\drivers\media\platform\sunxi-vfe\device\Makefile obj-m += ov5640.o #obj-m += ov2640.o #obj-m += ov7736.o #obj-m += s5k4ec.o #obj-m += s5k4ec_mipi.o #obj-m += gc2035.o #obj-m += gt2005.o #obj-m += gc0307.o #obj-m += gc0308.o #obj-m += gc0328.o #obj-m += gc0328c.o #obj-m += gc2145.o #obj-m += gc0329.o #obj-m += gc0311.o #obj-m += hi253.o #obj-m += sp2518.o #obj-m += sp2519.o #obj-m += sp0718.o #obj-m += sp0838.o #obj-m += ov16825.o #obj-m += ov5650.o #obj-m += ov5647.o #obj-m += ov5647_mipi.o #obj-m += t8et5.o #obj-m += s5k4e1.o #obj-m += s5k4e1_mipi.o #obj-m += sp2518.o #obj-m += sp0718.o #obj-m += gc5004.o #obj-m += gc5004_mipi.o #obj-m += ov5648.o #obj-m += ar0330.o #obj-m += ov5648.o #obj-m += sp5408.o #obj-m += ov12830.o #obj-m += ov8825.o #obj-m += ov8850.o #obj-m += gc2155.o #obj-m += ov8858.o #obj-m += ov13850.o #obj-m += imx214.o #obj-m += ov8858_4lane.o #obj-m += sp5409.o #obj-m += s5k5e2yx.o #obj-m += ov2710_mipi.o #obj-m += ov2686.o (这里是看ov5640的驱动的probe执行是否正确?设备ID是否读取成功!) W:\ov5640_spi20_r40t\lichee\linux-3.10\drivers\media\platform\sunxi-vfe\device\ov5640.c static int sensor_detect(struct v4l2_subdev *sd) { data_type rdval; printk("****wyb %s:%d/%s()! \n", __FILE__, __LINE__, __func__); LOG_ERR_RET(sensor_read(sd, 0x300a, &rdval;)) printk("****wyb %s:%d/%s()! 0x300a rdval=0xx\n", __FILE__, __LINE__, __func__, rdval); if(rdval != 0x56) return -ENODEV; LOG_ERR_RET(sensor_read(sd, 0x300b, &rdval;)) printk("****wyb %s:%d/%s()! 0x300b rdval=0xx\n", __FILE__, __LINE__, __func__, rdval); if(rdval != 0x40) return -ENODEV; return 0; } (在全志R16平台改过这个文件,让摄像头不要休眠,但是全志R40平台的tina v2.1系统下不需要修改!) W:\ov5640_spi20_r40t\lichee\linux-3.10\drivers\media\platform\sunxi-vfe\vfe.c 2、(可

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值