Chinaunix首页 | 论坛 | 博客
  • 博客访问: 408571
  • 博文数量: 120
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 741
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-27 18:15
文章分类

全部博文(120)

文章存档

2016年(13)

2015年(41)

2014年(66)

我的朋友

分类: LINUX

2014-08-01 17:27:11

     Linux系统启动过程很复杂,因为它既需要支持模块静态加载机制也要支持动态加载机制。模块动态加载机制给系统提供了极大的灵活性,驱动程序既可支持静态编译进内核,也可以支持动态加载机制。Linux系统中对设备和子系统的初始化在最后进行,主要过程可以用下图表示。




1

进入子系统初始化时,在内核init进程中进行设备初始化,最为复杂、诡异的机制莫过于do_initcalls()函数调用,该函数完成了所有需要静态加载模块的初始化,需要进行静态加载的内核模块,需要使用一些特定的宏进行处理,下面详细来说明一些linux内核中initcalls机制。

先来看看do_initcalls()函数原型:


2

核心部分是639~671之间,该部分是一个函数指针调用,遍历_initcall_start~_initcall_end范围,逐个调用函数指针。

_initcall_start~_initcall_end之间存放的是什么呢,可以以下面一幅示意图来说明。


3

图左边是地址指针,右边是相关宏,使用相关宏处理函数指针,可以将被处理的函数指针放在特定的指针范围内。例如,网络设备层初始化函数是net_dev_init(),定义在net/core/dev.c中,在该函数下方有条宏处理subsys_initcall(net_dev_init),该宏完成将net_dev_init函数指针放在上图中.initcall4.init段中,在do_initcalls()函数调用时,其处于_initcall_start~_initcall_end直接,所以net_dev_init()就这样被调用了。

这种机制真是比较巧妙,也比较难以理解,设计初衷就是为了实现一个通用的启动流程,使移植或扩展时,只需要对需要启动加载的模块进行宏处理即可。

下面来详细了解这种机制的实现方法。

先说一说gcc对手动定位代码段位置的支持,_attribute_gcc的关键字,指示编译器给符号设置特定属性。编译完成后输入到链接器的是各个带有符号表的文件,链接器对各个文件中符号进行重定位,_attribute_在该阶段进行处理,将指定符号放在链接生成文件段中特定位置,不单只指代码段,也包括数据段,如系统初始化中经常见到的_initdata,即将指定符号放到数据段特定位置。

当然,具体这些段是如何生成的,也是有文件进行配置,即在链接配置文件arch/xxx/vmlinux.ds.S.,如下


4

2.6.16内核中INITCALLS已直接被替换为


*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)


这和图3中的结构是对应的。接下来看看内核提供了哪些宏定义用来处理特定函数指针和数据。在include/linux/init.h文件中,包括各种常见的包装。


#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)

可以看出,内核为满足不同初始化等级,设计了1~77个等级,不同等级初始化代码用对应的宏进行处理,读者可以对照上表进行理解一下。还有其它一些宏,用于各种任务需求,如模块加载宏module_init()module_exit(),其处理又略有不同,读者可以自己理解一下。

总的来说,initcalls机制提供给内核开发者或驱动开发者一个借口,方便将自己的函数添加到内核初始化列表中,在内核初始化最后阶段进行处理。

阅读(9233) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~