1.
在看linux核心代码的时候看到/init/main.c 里面的do_initcalls函数
static void(void){*call;for (call =; call
static void(fn){int=();,,;char[64];int;if () {("calling %s/n", fn);=();}= fn();if () {=();=(,);("initcall %s", fn);(" returned %d after %Ld msecs/n",,(unsigned long long).tv64 >> 20);}[0] = 0;if (&&!= -&&)(, "error code %d ",);if (() !=) {(, "preemption imbalance ", sizeof());() =;}if (()) {(, "disabled interrupts ", sizeof());();}if ([0]) {("initcall %s", fn);(" returned with %s/n",);}}
__initcall_start是在 arch目录中的相关CPU中的vmlinux.lds文件指定,如i386中位于arch/i386/vmlinux.lds中,至于在 __initcall_start和__initcall_end之间的是由函数声明__init指定
2.
察看/arch/i386/vmlinux.lds,发现一段代码
__initcall_start = .;
.initcall.init : { *(.initcall.init) }
__initcall_end = .;
跟我找的东西相关
使用info ld,察看相关资料,(最终发现太麻烦,到网上找了一个ld.pdf).发现有这么一
段介绍关于c++地联结构造器的使用,和这段用法相同
其含义时,是让__initcall_start指向代码节.initcall.init的节首,而__initcall_end
指向.initcall.init的节尾。
那么第一段代码从程序逻辑上得到了解释。
3。因为do_initcalls所作的是系统中有关于选择的驱动部分的初始化工作,那么具体是这些
函数指针数据怎样放到了.initcall.init节。
想起来了,还没有使用grep哈哈,在
grep -rn .initcall.init *
发现在include/linux/init.h:83:有这样一个定义
#define __init_call __attribute__ ((unused,__section__ (".initcall.init")))
娃哈哈哈
终于让我发现了
然后又发现了
#define __initcall(fn) /
static initcall_t __initcall_##fn __init_call = fn
4。问题是什么是__attribute__??,查找man gcc,找到关于__attribute__的定义
`section ("section-name")'
Normally, the compiler places the code it generates in the `text'
section. Sometimes, however, you need additional sections, or you
need certain particular functions to appear in special sections.
The `section' attribute specifies that a function lives in a
particular section. For example, the declaration:
extern void foobar (void) __attribute__ ((section ("bar")));
puts the function `foobar' in the `bar' section.
Some file formats do not support arbitrary sections so the
`section' attribute is not available on all platforms. If you
need to map the entire contents of a module to a particular
section, consider using the facilities of the linker instead.
他的意思就是使它建造一个在.initcall.init节的指向初始函数的指针
5。问题是##是什么意思?
查阅gcc的man page得知,她是用在可变参数使用宏定义的时候的
在这里也就是建立一个变量名称为所指向的函数的名称,并且前面加上
__initcall_.
6.然后看看成果
在/include/linux/init.c中有发现
#define module_init(x) __initcall(x);
看看很多驱动中都有类似
module_init(usb_init);
module_exit(usb_exit);
的代码,哈哈,这下明白了。
分析kernel的initcall函数
Author: Dongas
Data: 08-07-15
先来看看这些initcall函数的声明:
/* include/linux/init.h */
/* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*/
#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)
#define __initcall(fn) device_initcall(fn)
#define __exitcall(fn) /
static exitcall_t __exitcall_##fn __exit_call = fn
#define console_initcall(fn) /
static initcall_t __initcall_##fn /
__attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn
#define security_initcall(fn) /
static initcall_t __initcall_##fn /
__attribute_used__ __attribute__((__section__(".security_initcall.init"))) = fn
#define module_init(x) __initcall(x); ß从这里知道module_init的等级为6,相对靠后
#define module_exit(x) __exitcall(x);
可以发现这些*_initcall(fn)最终都是通过__define_initcall(level,fn)宏定义生成的。
__define_initcall宏定义如下:
#define __define_initcall(level,fn) /
static initcall_t __initcall_##fn __attribute_used__ /
__attribute__((__section__(".initcall" level ".init"))) = fn
这句话的意思为定义一个initcall_t型的初始化函数,函数存放在.initcall”level”.init section内。.initcall”level”.init section定义在vmlinux.lds内。
/* arch/arm/kernel/vmlinux.lds */
……
__initcall_start = .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
__initcall_end = .;
……
正好包括了上面init.h里定义的从core_initcall到late_initcall等7个level等级的.initcall”level”.init section. 因此通过不同的*_initcall声明的函数指针最终都会存放不同level等级的.initcall”level”.init section内。这些不同level的section按level等级高低依次存放。
下面我们再来看看,内核是什么时候调用存储在.initcall”level”.init section内的函数的。
内核是通过do_initcalls函数循环调用执行initcall.init section内的函数的,流程如下:
start_kernel -> rest_init -> kernel_thread -> init -> do_basic_setup -> do_initcalls
这里要分析两个函数: kernel_thread和do_initcalls,这两个函数都定义在init/main.c内
1) kernel_thre