欢迎转载,相互学习,但请注明出处,非常感谢!
http://blog.csdn.net/richard_liujh/article/details/45669207
- 刘金辉
写过linux驱动的程序猿都知道module_init() 这个函数。那么我们来了解一下module_init这个函数的具体功能和执行过程
在kernel源码目录中找到include\linux\init.h文件
-
"font-family:SimSun;font-size:14px;">/**
-
* module_init() - driver initialization entry point
-
* @x:
function
to be run at kernel boot time
or
module insertion
-
*
-
* module_init() will either be called during do_initcalls() (
if
-
* builtin)
or at
module insertion time (
if a
module). There can only
-
* be one per
module.
-
*/
-
#define module_init(x) __initcall(x);
这里面就有对module_init 的定义,我们发现
module_init(x)是一个宏定义,那么_initcall(x)又是什么呢?
#define __initcall(fn) device_initcall(fn)
感觉怪怪的,怎么这么多的宏??再解释这个之前,我们再来看看更多的宏定义吧
完整的宏定义如下:
__define_initcall:
-
/* 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.
-
*
-
* The `id
' arg to __define_initcall() is needed so that multiple initcalls
-
* can point at the same handler without causing duplicate-symbol build errors.
-
*/
-
-
#define __define_initcall(fn, id) \
-
static initcall_t __initcall_
##fn##id __used \
-
__attribute__((__section__(
".initcall"
#id ".init"))) = fn
initcalls:
-
"code"
class=
"cpp">/
-
* A
"pure" initcall has no dependencies
on anything
else,
and purely
-
* initializes variables that couldn
't be statically initialized.
-
*
-
* This only exists
for built-
in code,
not
for modules.
-
* Keep main.c:initcall_level_names[]
in sync.
-
*/
-
#define pure_initcall(fn) __define_initcall(fn, 0)
-
-
#define core_initcall(fn) __define_initcall(fn, 1)
-
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
-
#define postcore_initcall(fn) __define_initcall(fn, 2)
-
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
-
#define arch_initcall(fn) __define_initcall(fn, 3)
-
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
-
#define subsys_initcall(fn) __define_initcall(fn, 4)
-
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
-
#define fs_initcall(fn) __define_initcall(fn, 5)
-
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
-
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
-
#define device_initcall(fn) __define_initcall(fn, 6)
-
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
-
#define late_initcall(fn) __define_initcall(fn, 7)
-
#define late_initcall_sync(fn) __define_initcall(fn, 7s)
-
-
#define __initcall(fn) device_initcall(fn)
我们可以看到非常多的xxx_initcall宏函数定义,他们都是通过__define_initcall 实现的。在__define_initcall里面包含了两个参数,一个是fn,另一个则是id。那么,这么多的宏又有何用??
我们来到init\main.c文件中可以找到函数do_initcalls
-
static void __init do_initcalls(void)
-
{
-
int level;
-
-
for (level =
0; level < ARRAY_SIZE(initcall_levels) -
1; level++)
-
do_initcall_level(level);
-
}
很明显do_initcalls中有一个for循环,
那么此循环就是按照优先级顺序执行一些函数的。那么问题又来了,
执行哪些函数??我们看看do_initcalls这个名字。是不是initcall非常的眼熟?没错就是上面我们提到过的宏定义
xxx_initcall里面就有initcall。
所以,我们先来解释一下这些宏有什么用
还是从我们最熟悉的地方module_init(fn)开始说起,其中fn是module_init的参数,fn是一个函数指针(函数名)。
module_init(fn)---> __initcall(fn) ---> device_initcall(fn) ---> __define_initcall(fn, 6)
所以当我们写module_init(fn)最终我们可以简化成以下内容(假设module_init的参数为test_init)
module_init(test_init) ---> __define_initcall(test_init, 6)
-
#define __define_initcall(fn, id) \
-
static initcall_t __initcall_
##fn##id __used \
-
__attribute__((__section__(
".initcall"
#id ".init"))) = fn
简单补充:
符号 | 作用 | 举例 |
##
|
“##”符号
可以
是连接的意思
|
例如
__initcall_##fn##id 为__initcall_fnid
那么,fn = test_init,id = 6时,
__initcall_##fn##id 为
__initcall_test_init6
|
# |
“#”符号
可以
是字符串化的意思
|
例如 #id 为 “id”,id=6 时,#id 为“6”
|
是不是看起来更加头疼,那么我们说简单一点。通过__attribute__(__section__)设置函数属性,也就是将test_init放在.initcall6.init段中。这个段在哪用?这就要涉及到链接脚本了。
大家可以到kernel目录arch中,根据自己的处理器平台找到对应的链接脚本。例如我现在的平台是君正m200(mips架构),可能大部分是arm架构。
在arch/mips/kernel/vmlinux.lds这个链接脚本里面有如下一段代码
-
__init_begin = .;
-
. =
ALIGN(
4096); .
init.text :
AT(
ADDR(.
init.text) -
0) { _sinittext = .; *(.
init.text) *(.cpuinit.text) *(.meminit.text) _einittext = .; }
-
.
init.data :
AT(
ADDR(.
init.data) -
0) { *(.
init.data) *(.cpuinit.data) *(.meminit.data) *(.
init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . =
ALIGN(
32); __dtb_start = .; *(.dtb.
init.rodata) __dtb_end = .; . =
ALIGN(
16); __setup_start = .; *(.
init.setup) __setup_end = .; __initcall_start = .; *(.initcallearly.
init) __initcall0_start = .; *(.initcall0.
init) *(.initcall0s.
init) __initcall1_start = .; *(.initcall1.
init) *(.initcall1s.
init) __initcall2_start = .; *(.initcall2.
init) *(.initcall2s.
init) __initcall3_start = .; *(.initcall3.
init) *(.initcall3s.
init) __initcall4_start = .; *(.initcall4.
init) *(.initcall4s.
init) __initcall5_start = .; *(.initcall5.
init) *(.initcall5s.
init) __initcallrootfs_start = .; *(.initcallrootfs.
init) *(.initcallrootfss.
init) __initcall6_start = .; *(.initcall6.
init) *(.initcall6s.
init) __initcall7_start = .; *(.initcall7.
init) *(.initcall7s.
init) __initcall_end = .; __con_initcall_start = .; *(.con_initcall.
init) __con_initcall_end = .; __security_initcall_start = .; *(.security_initcall.
init) __security_initcall_end = .; . =
ALIGN(
4); __initramfs_start = .; *(.
init.ramfs) . =
ALIGN(
8); *(.
init.ramfs.info) }
-
. =
ALIGN(
4);
当然,关于链接脚本又有很多多动要讲。所以现在我们不关心里面的具体含义,我们可以观察到上面有这些字符串使我们比较熟悉的:__initcall6_start = .; *(
.initcall6.init) *(.initcall6s.init)。链接脚本里的东西看似很乱很难,其实是非常有逻辑有规律可循的,我们来简单解释下面一行的代码作用
__initcall6_start = .; *(.initcall6.init) *(.initcall6s.init)
其中__initcall6_start是一个符号,链接器用到的。
__initcall6_start = .; ,其中的 '.'符号是对当前地址的一个引用,也就说把当前的地址给了符号__initcall6_start, *(.initcall6.init) *(.initcall6s.init) 的意思是所有的.initcall6.init段和.initcall6s.init段的内容从__initcall6_start为起始地址开始链接。
.initcall0.init .initcall0s.init .initcall1.init .initcall1s.init …… .initcall7.init .initcall7s.init
上面的内容都出现在了链接脚本中,而0,0s,1,1s,2,2s …… 6,6s,7,7s 有没有觉得在哪里见过? 我们回顾一下initcalls里面的定义
-
#define pure_initcall(fn) __define_initcall(fn, 0)
-
-
#define core_initcall(fn) __define_initcall(fn, 1)
-
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
-
#define postcore_initcall(fn) __define_initcall(fn, 2)
-
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
-
#define arch_initcall(fn) __define_initcall(fn, 3)
-
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
-
#define subsys_initcall(fn) __define_initcall(fn, 4)
-
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
-
#define fs_initcall(fn) __define_initcall(fn, 5)
-
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
-
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
-
#define device_initcall(fn) __define_initcall(fn, 6)
-
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
-
#define late_initcall(fn) __define_initcall(fn, 7)
-
#define late_initcall_sync(fn) __define_initcall(fn, 7s)
这里面就有
0,0s,1,1s,2,2s …… 6,6s,7,7s,也就是__define_initcall(fn, id)中的第二个参数 id。很显然这个id的值不是我们在调用module_init的时候传过去的。数字id 0~7代表的是不同的优先级(0最高,module_init对应的优先级为6,所以一般我们注册的驱动程序优先级为6),链接脚本里面根据我们注册不同的id,将我们的函数fn放入对应的地址里面。根据上面的分析,test_init放在.initcall6.init段中。
在kernel启动过程中,会调用do_initcalls函数一次调用我们通过xxx_initcall注册的各种函数,优先级高的先执行。所以我们通过module_init注册的函数在kernel启动的时候会被顺序执行。
由于时间原因,只能把具体执行过程放在linux内核很吊之 module_init解析 (下)再分析了。
欢迎转载,相互学习,但请注明出处,非常感谢!
http://blog.csdn.net/richard_liujh/article/details/45669207
- 刘金辉
写过linux驱动的程序猿都知道module_init() 这个函数。那么我们来了解一下module_init这个函数的具体功能和执行过程
在kernel源码目录中找到include\linux\init.h文件
-
"font-family:SimSun;font-size:14px;">/**
-
* module_init() - driver initialization entry point
-
* @x:
function
to be run at kernel boot time
or
module insertion
-
*
-
* module_init() will either be called during do_initcalls() (
if
-
* builtin)
or at
module insertion time (
if a
module). There can only
-
* be one per
module.
-
*/
-
#define module_init(x) __initcall(x);
这里面就有对module_init 的定义,我们发现
module_init(x)是一个宏定义,那么_initcall(x)又是什么呢?
#define __initcall(fn) device_initcall(fn)
感觉怪怪的,怎么这么多的宏??再解释这个之前,我们再来看看更多的宏定义吧
完整的宏定义如下:
__define_initcall:
-
/* 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.
-
*
-
* The `id
' arg to __define_initcall() is needed so that multiple initcalls
-
* can point at the same handler without causing duplicate-symbol build errors.
-
*/
-
-
#define __define_initcall(fn, id) \
-
static initcall_t __initcall_
##fn##id __used \
-
__attribute__((__section__(
".initcall"
#id ".init"))) = fn
initcalls:
-
"code"
class=
"cpp">/
-
* A
"pure" initcall has no dependencies
on anything
else,
and purely
-
* initializes variables that couldn
't be statically initialized.
-
*
-
* This only exists
for built-
in code,
not
for modules.
-
* Keep main.c:initcall_level_names[]
in sync.
-
*/
-
#define pure_initcall(fn) __define_initcall(fn, 0)
-
-
#define core_initcall(fn) __define_initcall(fn, 1)
-
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
-
#define postcore_initcall(fn) __define_initcall(fn, 2)
-
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
-
#define arch_initcall(fn) __define_initcall(fn, 3)
-
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
-
#define subsys_initcall(fn) __define_initcall(fn, 4)
-
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
-
#define fs_initcall(fn) __define_initcall(fn, 5)
-
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
-
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
-
#define device_initcall(fn) __define_initcall(fn, 6)
-
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
-
#define late_initcall(fn) __define_initcall(fn, 7)
-
#define late_initcall_sync(fn) __define_initcall(fn, 7s)
-
-
#define __initcall(fn) device_initcall(fn)
我们可以看到非常多的xxx_initcall宏函数定义,他们都是通过__define_initcall 实现的。在__define_initcall里面包含了两个参数,一个是fn,另一个则是id。那么,这么多的宏又有何用??
我们来到init\main.c文件中可以找到函数do_initcalls
-
static void __init do_initcalls(void)
-
{
-
int level;
-
-
for (level =
0; level < ARRAY_SIZE(initcall_levels) -
1; level++)
-
do_initcall_level(level);
-
}
很明显do_initcalls中有一个for循环,
那么此循环就是按照优先级顺序执行一些函数的。那么问题又来了,
执行哪些函数??我们看看do_initcalls这个名字。是不是initcall非常的眼熟?没错就是上面我们提到过的宏定义
xxx_initcall里面就有initcall。
所以,我们先来解释一下这些宏有什么用
还是从我们最熟悉的地方module_init(fn)开始说起,其中fn是module_init的参数,fn是一个函数指针(函数名)。
module_init(fn)---> __initcall(fn) ---> device_initcall(fn) ---> __define_initcall(fn, 6)
所以当我们写module_init(fn)最终我们可以简化成以下内容(假设module_init的参数为test_init)
module_init(test_init) ---> __define_initcall(test_init, 6)
-
#define __define_initcall(fn, id) \
-
static initcall_t __initcall_
##fn##id __used \
-
__attribute__((__section__(
".initcall"
#id ".init"))) = fn
简单补充:
符号 | 作用 | 举例 |
##
|
“##”符号
可以
是连接的意思
|
例如
__initcall_##fn##id 为__initcall_fnid
那么,fn = test_init,id = 6时,
__initcall_##fn##id 为
__initcall_test_init6
|
# |
“#”符号
可以
是字符串化的意思
|
例如 #id 为 “id”,id=6 时,#id 为“6”
|
是不是看起来更加头疼,那么我们说简单一点。通过__attribute__(__section__)设置函数属性,也就是将test_init放在.initcall6.init段中。这个段在哪用?这就要涉及到链接脚本了。
大家可以到kernel目录arch中,根据自己的处理器平台找到对应的链接脚本。例如我现在的平台是君正m200(mips架构),可能大部分是arm架构。
在arch/mips/kernel/vmlinux.lds这个链接脚本里面有如下一段代码
-
__init_begin = .;
-
. =
ALIGN(
4096); .
init.text :
AT(
ADDR(.
init.text) -
0) { _sinittext = .; *(.
init.text) *(.cpuinit.text) *(.meminit.text) _einittext = .; }
-
.
init.data :
AT(
ADDR(.
init.data) -
0) { *(.
init.data) *(.cpuinit.data) *(.meminit.data) *(.
init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . =
ALIGN(
32); __dtb_start = .; *(.dtb.
init.rodata) __dtb_end = .; . =
ALIGN(
16); __setup_start = .; *(.
init.setup) __setup_end = .; __initcall_start = .; *(.initcallearly.
init) __initcall0_start = .; *(.initcall0.
init) *(.initcall0s.
init) __initcall1_start = .; *(.initcall1.
init) *(.initcall1s.
init) __initcall2_start = .; *(.initcall2.
init) *(.initcall2s.
init) __initcall3_start = .; *(.initcall3.
init) *(.initcall3s.
init) __initcall4_start = .; *(.initcall4.
init) *(.initcall4s.
init) __initcall5_start = .; *(.initcall5.
init) *(.initcall5s.
init) __initcallrootfs_start = .; *(.initcallrootfs.
init) *(.initcallrootfss.
init) __initcall6_start = .; *(.initcall6.
init) *(.initcall6s.
init) __initcall7_start = .; *(.initcall7.
init) *(.initcall7s.
init) __initcall_end = .; __con_initcall_start = .; *(.con_initcall.
init) __con_initcall_end = .; __security_initcall_start = .; *(.security_initcall.
init) __security_initcall_end = .; . =
ALIGN(
4); __initramfs_start = .; *(.
init.ramfs) . =
ALIGN(
8); *(.
init.ramfs.info) }
-
. =
ALIGN(
4);
当然,关于链接脚本又有很多多动要讲。所以现在我们不关心里面的具体含义,我们可以观察到上面有这些字符串使我们比较熟悉的:__initcall6_start = .; *(
.initcall6.init) *(.initcall6s.init)。链接脚本里的东西看似很乱很难,其实是非常有逻辑有规律可循的,我们来简单解释下面一行的代码作用
__initcall6_start = .; *(.initcall6.init) *(.initcall6s.init)
其中__initcall6_start是一个符号,链接器用到的。
__initcall6_start = .; ,其中的 '.'符号是对当前地址的一个引用,也就说把当前的地址给了符号__initcall6_start, *(.initcall6.init) *(.initcall6s.init) 的意思是所有的.initcall6.init段和.initcall6s.init段的内容从__initcall6_start为起始地址开始链接。
.initcall0.init .initcall0s.init .initcall1.init .initcall1s.init …… .initcall7.init .initcall7s.init
上面的内容都出现在了链接脚本中,而0,0s,1,1s,2,2s …… 6,6s,7,7s 有没有觉得在哪里见过? 我们回顾一下initcalls里面的定义
-
#define pure_initcall(fn) __define_initcall(fn, 0)
-
-
#define core_initcall(fn) __define_initcall(fn, 1)
-
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
-
#define postcore_initcall(fn) __define_initcall(fn, 2)
-
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
-
#define arch_initcall(fn) __define_initcall(fn, 3)
-
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
-
#define subsys_initcall(fn) __define_initcall(fn, 4)
-
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
-
#define fs_initcall(fn) __define_initcall(fn, 5)
-
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
-
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
-
#define device_initcall(fn) __define_initcall(fn, 6)
-
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
-
#define late_initcall(fn) __define_initcall(fn, 7)
-
#define late_initcall_sync(fn) __define_initcall(fn, 7s)
这里面就有
0,0s,1,1s,2,2s …… 6,6s,7,7s,也就是__define_initcall(fn, id)中的第二个参数 id。很显然这个id的值不是我们在调用module_init的时候传过去的。数字id 0~7代表的是不同的优先级(0最高,module_init对应的优先级为6,所以一般我们注册的驱动程序优先级为6),链接脚本里面根据我们注册不同的id,将我们的函数fn放入对应的地址里面。根据上面的分析,test_init放在.initcall6.init段中。
在kernel启动过程中,会调用do_initcalls函数一次调用我们通过xxx_initcall注册的各种函数,优先级高的先执行。所以我们通过module_init注册的函数在kernel启动的时候会被顺序执行。
由于时间原因,只能把具体执行过程放在linux内核很吊之 module_init解析 (下)再分析了。