main.c
asmlinkage void __init start_kernel(void)
-- rest_init();
--kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
--static int __ref kernel_init(void *unused)
--kernel_init_freeable();
--do_basic_setup();
--do_initcalls()
static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}
static void __init do_initcall_level(int level)
{
extern const struct kernel_param __start___param[], __stop___param[];
initcall_t *fn;
strcpy(static_command_line, saved_command_line);
parse_args(initcall_level_names[level],
static_command_line, __start___param,
__stop___param - __start___param,
level, level,
&repair_env_string);
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(*fn);
}
init.h
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn
/*
* Early initcalls run before initializing SMP.
*
* Only for built-in code, not modules.
*/
#define early_initcall(fn) __define_initcall(fn, early)
/*
* 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)
#define module_init(x) __initcall(x);
i2c-ambarella.c
static int __init ambarella_i2c_init(void)
{
printk("ambarella_i2c_init\n");
return platform_driver_register(&ambarella_i2c_driver);
}
static void __exit ambarella_i2c_exit(void)
{
platform_driver_unregister(&ambarella_i2c_driver);
}
subsys_initcall(ambarella_i2c_init);
ambbus.c
static int __init amb_bus_init(void)
{
int error;
error = bus_register(&amb_bus_type);
if (!error) {
error = device_register(&amb_bus);
if (error)
bus_unregister(&amb_bus_type);
}
return error;
}
device_initcall(amb_bus_init);
static int __init pl011_init(void)
{
int ret;
printk(KERN_INFO "Serial: AMBA PL011 UART driver\n");
ret = uart_register_driver(&amba_reg);
if (ret == 0) {
ret = amba_driver_register(&pl011_driver);
if (ret)
uart_unregister_driver(&amba_reg);
}
return ret;
}
static void __exit pl011_exit(void)
{
amba_driver_unregister(&pl011_driver);
uart_unregister_driver(&amba_reg);
}
/*
* While this can be a module, if builtin it's most likely the console
* So let's leave module_exit but move module_init to an earlier place
*/
arch_initcall(pl011_init);
build.c
late_initcall(ubi_init);
在内核源代码中,platform 设备的初始化(注册)用arch_initcall()调用,它的initcall 的level为3; 而驱动的注册用module_init()调用,即device_initcall(),它的initcall 的level为6。 #define module_init(x) __initcall(x); #define __initcall(fn) device_initcall(fn) kernel 初始化时(kernel_init@init/main.c),按照内核链接文件中(arm系统:kernel/arch/arm/vmlinux.lds)的__init call_start段的序列依次执行, 这样level小的初始化函数先于level大的初始化函数被调用。 所以platform设备先被注册,驱动加载时会调用驱动程序中的probe(),扫描系统中已注册的设备,找到匹配设备后将驱动和设备绑定。
比如I2C,适配器先需要 设备注册;
=============================
LINUX内核中有很多的初始化指示标志postcore_initcall(), arch_initcall(), subsys_initcall(), device_initcall(), etc. 这些起什么作用呢?
先看这些宏的定义(定义在文件include/linux/init.h中)
2、__define_initcall
这些宏都用到了__define_initcall(),再看看它的定义(同样定义在文件include/linux/init.h中)
这其中initcall_t是函数指针,原型如下,
而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。
所以__define_initcall的含义是:
1) 声明一个名称为__initcall_##fn的函数指针;
2) 将这个函数指针初始化为fn;
3) 编译的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"的section中。
3.放置.initcall.init SECTION
明确了__define_initcall的含义,就知道了是分别将这些初始化标号修饰的函数指针放到各自的section中的。
SECTION“.initcall”level”.init”被放入INITCALLS(include/asm-generic/vmlinux.lds.h)
__initcall_start和__initcall_end以及INITCALLS中定义的SECTION都是在arch/xxx/kernel/vmlinux.lds.S中放在.init段的。
4. 初始化.initcallxx.init里的函数
而这些SECTION里的函数在初始化时被顺序执行(init内核线程->do_basic_setup()[main.c#778]->do_initcalls())。
程序(init/main.c文件do_initcalls()函数)如下,do_initcalls()把.initcallXX.init中的函数按顺序都执行一遍。
=======================