您的位置:首页 > 运维架构 > Linux

linux内核中的xx_initcall和module_init实现机制(linux3.1.0)

2014-11-29 17:53 417 查看
在内核代码中,arch_initcall 、device_initcall、module_init等经常遇到,本文分析其实现机制。

1 include/linux/init.h 下的相关定义

#ifndef MODULE
#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)
还有一个我们常遇到的module_init()

#define module_init(x)	__initcall(x);
#define __initcall(fn) device_initcall(fn)
所以module_init(x)和device_initcall(fn)是等价的

__define_initcall(level,fn,id)
level从0到7表示优先级,也就是pure_initcall() 最先被调用,late_initcall()最后被调用。调用的先后顺序

2 分析 __define_initcall宏定义

我们知道,以上所有的定义最后都转化为了__define_initcall(level,fn,id),那么就来分析这个宏。

#define__define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn

typedef int (*initcall_t)(void);


从一上定义可知:

1 定义一个initcall_t类型函数指针

2 函数指针初值为fn

3 函数指针段属性为(".initcall" level ".init")

以arch_initcall(customize_machine) 为例,可以转化为:

__define_initcall("3",customize_machine,3)

static initcall_t __initcall_customize_machine3  __used \
__attribute__((__section__(".initcall3.init"))) = customize_machine


1 定义一个函数指针 __initcall_customize_machine3

2 函数指针初始化为customize_machine

3 这个函数指针的属性是.initcall3.init

3 __section__(".initcall" level ".init")段属性定义

/include/asm-generic/vmlinux.lds.h中定义段属性

#define INITCALLS							\
*(.initcallearly.init)						\
VMLINUX_SYMBOL(__early_initcall_end) = .;			\
*(.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)

#define INIT_CALLS							\
VMLINUX_SYMBOL(__initcall_start) = .;			\
INITCALLS						\
VMLINUX_SYMBOL(__initcall_end) = .;


4 调用


其实,在linux内核中,有一个技巧是经常用到的,就是把某些具有类似特性的指针、函数、结构体等

放链接的时候放到同一个段,然后调用的时候到这个段中去遍历,或者去寻找匹配项。这里就是如此。

static void __init do_initcalls(void)
{
initcall_t *fn;

for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
}


__early_initcall_end 在__initcall_start之后,在*(.initcall0.init)之前。

do_initcalls(void)就是到这个段中去遍历fn,然后调用do_one_initcall(*fn),do_one_initcall(*fn)调用fn。

5 调用过程

现在已经知道如何被调用的了,来看看何时被调用。整个调用过程:

/init/main.c start_kernel ->rest_init()->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND)

->kernel_init->do_basic_setup()->do_initcalls()->do_one_initcall(initcall_t fn)->fn()

以上所有函数均在/init/main.c中

看到这里应该已经明白了xx_initcall()的实现机制。但是有个前提,是在静态编译内核

的时候是这样的。如果你看的够仔细的话,应该注意到在第一部分[b]include/linux/init.h 下的[/b]

[b]相关定义第一行是#ifndef MODULE。[/b]

所谓静态编译,就是在make menuconfig时候,选择 *,也就是yes,模块直接编译进了

内核。

动态编译

当你希望这个模块可以用insmod动态加载的时候,可以选择 M ,也就是只编译成模块,

不编译进内核,此时就定义了MODULE。MODULE参数的传递是编译时通过gcc -DMODULE

传递的。在编译一个自己写的驱动模块的时候,如果不把它融合到内核的目录里,那我们

自己写的Makefile里通常会在make 的后面加上modules,也是达到这个目的。

当动态编译一个模块的时候,定义如下:

#define early_initcall(fn)		module_init(fn)
#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)
#define security_initcall(fn)		module_init(fn)
所有这些 device_initcall(fn)等全部等效于module_init(fn)。其实也容易理解,既然你可以不

编译进内核,说明并不是那么的重要,那就不管你说以往是什么头衔,都一视同仁。那就只分析

module_init(fn)就可以了。



#define module_init(initfn)					\
static inline initcall_t __inittest(void)		\
{ return initfn; }					\
int init_module(void) __attribute__((alias(#initfn)));
首先我们可以发现发现module_init有两个含义:

1、验证加载函数的格式

static inline initcall_t __inittest(void)	\

{ return initfn; }
这个函数的作用是验证我们穿过来的加载函数格式是否正确,linux内核规定加载函数的的原型是:

typedef int (*initcall_t)(void);

所以我们写加载函数的时候必须是返回值为int参数为void的函数,这个在内核里要求比较严格,所以我们写

加载函数的时候必须按照这个约定。

2、定义别名

int init_module(void) __attribute__((alias(#initfn)));
这段代码的作用是给我们的加载函数定义一个别名,别名就是我们前面提到的init_module,这样insmod

就能够执行我们的加载函数了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: