momo zone

调核人的blog

内核子系统之初始化宏

看了一下usb子系统的源码,先从subsys_initcall(usb_init) 开始。

这个也是让人一头雾水,用笨的方法慢慢展开吧
subsys_initcall(usb_init)
       ||
       ||  #define subsys_initcall(fn) __define_initcall("4",fn,4)
       ||
__define_initcall("4",usb_init,4)
       ||
       ||  #define __define_initcall(level,fn,id) static initcall_t __initcall_##fn##id __used __attribute__((__section__(".initcall" level".init"))) = fn //可变参数宏,允许替换空值
       ||
static initcall_t __initcall_usb_init4 __used __attribute__((__section__(".initcall"level".init")))=usb_init
       ||
       ||  typedef int (*initcall_t)(void) //定义‘函数指针’类型,被指向的函数不带参数,返回值类型为int。
       ||   #define __used __attribute__((__used__)) //对于gcc4,gcc3.3及以上版本而言
||
static initcall_t __initcall_usb_init4 __attribute__((__used__)) __attribute__((__section__(".initcall"level".init")))=usb_init

这里暂不讨论__attribute__具体是什么,其实到这里就能看出来subsys_initcall 到底怎么回事:
创建一个静态的函数指针,并使之指向usb_init。
在看一下usb_init()的定义:
static int __init usb_init(void)
       ||  
       ||  #define __init __section(.init.text) __cold notrace
       ||
static int __section(.init.text) __cold notrace usb_init(void)
       ||
       ||  #define __section(S) __attribute__((__section__(#S)))
       ||  #define __cold       __attribute__((__cold__))
       ||  #define notrace __attribute__((no_instrument_function))
       ||
static init __attribute__((__section__(.init.text))) __attribute__((__cold__)) __attribute__((no_instrument_function)) init(void)
这样就可以看得更清楚。
ok , 问题是这么一堆__attribute__ 是什么意思
 
这个要扯到gcc的一些扩展特性,不说太多,这里只需要留意一下__section__ 标记,  他是将该段代码(文中是函数)放入执行镜像的指定段中。
文中就是放入.init.text 段中。这样有个好处,就是这些负责初始化的函数执行之后可以立马被请出去,腾出来内存。细节可以在arch/x86/kernel/vmlinux.lds (编译一次内核后才有,用来指示ld 如何编排内核镜像的)中窥视一下:
这个文件虽然是天书(大部分是注释,非常详尽)  ,不过只要搜这个就行了initcall.init。
 __inicall_start = .;
.initcall.init : AT(ADDR(.initcall.init) – 0xC0000000) {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;
那个函数指针
看到这里应该能悟到了
__initcall_usb_init4 这个函数指针就是被放到了这里 *(.initcall4.init)
而真正的函数体是放在.init.text 这里面。内核初始化时会按照1-7的顺序执行这些函数指针所指的函数。这个机制就像/etc/init.d/ 里面的启动脚本。
Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: