全部博文(120)
分类: LINUX
2010-05-19 17:02:32
前言:前段时间做一个项目需要设计一个动态库,并希望在加载库的同时自动执行一些初始化动作,
于是联想到了linux内核众子系统的初始化,于是研究之,并在过这程中发现了初始化段的存在,利用初始化段实现了该功能。工作一年,笔记积累多了,慢慢
变得杂乱无章,于是开博,一方面整理笔记,梳理知识,另一方面和大家交流,共同进步。
keyword:subsys_initcall, init, init_call
在linux内核代码里,运用了subsys_initcall来
进行各种子系统的初始化,具体怎么初始化的呢?其实并不复杂。以2.6.29内核作为例子。在
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn)
__define_initcall("1",fn,1)
#define
core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define
postcore_initcall(fn) __define_initcall("2",fn,2)
#define
postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define
arch_initcall(fn) __define_initcall("3",fn,3)
#define
arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn)
__define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define
fs_initcall(fn) __define_initcall("5",fn,5)
#define
fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define
rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define
device_initcall(fn) __define_initcall("6",fn,6)
#define
device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define
late_initcall(fn) __define_initcall("7",fn,7)
#define
late_initcall_sync(fn) __define_initcall("7s",fn,7s)
而__define_initcall又
被定义为
#define
__define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall"
level
".init"))) = fn
所以 subsys_initcall(fn) == __initcall_fn4
它将被链接器放于section .initcall4.init
中。(attribute将会在另一篇文章中介绍)
执行过程:
start_kernel->rest_init
系统在启动后在rest_init中会创建init内核线程
init->do_basic_setup->do_initcalls
do_initcalls
中会把.initcall.init.中的函数依次执行一遍:
for (call = __initcall_start; call < __initcall_end;
call++) {
. .....
result = (*call)();
.
........
}
这个__initcall_start是在文件
.initcall.init
: AT(ADDR(.initcall.init) - LOAD_OFFSET) {
__initcall_start =
.;
INITCALLS
__initcall_end
= .;
}
INITCALLS被定义于
#define INITCALLS \
*(.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)
subsys_initcall的静态调用
方式应该讲清楚来龙去脉了,现在看看动态方式的初始化函数调用(模块方式)。在
#define core_initcall(fn) module_init(fn)
#define
postcore_initcall(fn) module_init(fn)
#define
arch_initcall(fn) module_init(fn)
#define
subsys_initcall(fn) module_init(fn)
#define
fs_initcall(fn) module_init(fn)
#define
device_initcall(fn) module_init(fn)
#define
late_initcall(fn) module_init(fn)
这是在定义MODULE的
情况下对subsys_initcall的
定义,就是说对于驱动模块,使用subsys_initcall等价于使用module_init
下面先看看module_init宏究竟做
了什么
#define module_init(initfn) \
static
inline initcall_t __inittest(void) \ /*定义此函数用来
检测传入函数的类型,并在编译时提供警告信息*/
{ return initfn; } \
int init_module(void)
__attribute__((alias(#initfn))); /*声明init_modlue为
initfn的别名,insmod只查找名字为init_module函数并调用*/
typedef int (*initcall_t)(void);
/*函数类型定义*/
在以模块方式编译一个模块的时候,会自动生成一个xxx.mod.c文件, 在该文件里面定义一个struct
module变量,并把init函数设置为上面的init_module()
而上面的这个init_module,被alias成模块的初始化函数(参考
也就是说,模块装载的时候
(insmod,modprobe),sys_init_module()系统调用会调用module_init指定的函数(对于编译成>模块的情
况)。
内核在启动时已经检测到了系统的硬件设备, 并把硬件设备信息通过sysfs内核虚拟文件系统导出。sysfs文件系统由系统初始化脚本挂载到/sys上。udev 扫描sysfs文件系统,根据硬件设备信息生成热插拔(hotplug)事件,udev再读取这些事件,生成对应的硬件设备文件。由于 没有实际的硬件插拔动作,所以这一过程被称为coldplug。
udev完成coldplug操作,需要下
面三个程序:
udevtrigger——扫描sysfs文件系统,生成相应的硬件设备hotplug事件。
udevd——作为
deamon,记录hotplug事件,然后排队后再发送给udev,避免事件冲突(race conditions)。
udevsettle
——查看udev事件队列,等队列内事件全部处理完毕才退出。
要规定事件怎样处理就要编写规则文件了.规
则文件是udev的灵魂,没有规则文件,udev无法自动加载硬件设备的驱动模块。它一般位于
这里给出一个简单的初始化段的使用例子,将 a.c编译成一个动态库,其中,函数a()和函数c()放在两个不同的初始化段里,函数b()默认放置;编译main.c,链接到由a.c编译成的动态 库,观察各函数的执行顺序。
# cat a.c
int a(void)
{
printf("a\n");
return 0;
}
__attribute__((__section__(".init_array.2")))
static fn init_a = a;
{
printf("c\n");
return 0;
}
__attribute__((__section__(".init_array.1"))) static fn
init_c = c;
int b()
{
printf("b\n");
return 0;
}
# cat main.c
#include
int b(); int main() { printf("main\n"); b(); } |
gcc -fPIC -g
-c a.c
gcc -shared -g -o liba.so a.o cp liba.so /lib/ -fr gcc main.c liba.so ldconfig ./a.out |
# gcc -fPIC -g -c a.c
# gcc -shared -g -o
liba.so a.o
# cp liba.so /lib/
# gcc main.c liba.so
# ldconfig
# ./a.out
a
c
main
b