分类: LINUX
2012-12-18 15:56:28
1. __initcall_start是在 arch目录中的相关CPU中的vmlinux.lds文件指定,如i386中位于arch/i386/vmlinux.lds中,至于在 __initcall_start和__initcall_end之间的是由函数声明__init指定 说明:以下内容基于linux2.4.0 asmlinkage void __init start_kernel(void) ··········································································· /* Do the rest non-__init'ed, we're now alive */ start_kernel()中的函数rest_init()将创建第一个核心线程kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND),调用init()函数: static int init(void * unused)------------------- /* /* 如果没有指定init启动参数,则查找下面的目录init进程,成功将不会返回,否则打印出错信息 */ panic("No init found. Try passing init= option to kernel."); } 继而调用函数do_basic_setup()(此时与体系结构相关的部分已经初始化完了,现在开始初始化设备了): /* #ifdef CONFIG_SYSCTL /* Networking initialization needs a process context */ do_initcalls(); //系统初始化(包括设备,文件系统,内核模块等) void __init driver_init(void) /* These are also core pieces, but must come after the static void __init do_initcalls(void) for (call = __initcall_start; call < __initcall_end; call++) { <./include/linux/init.h>--------------------- #ifndef MODULE /* 如果驱动模块静态编译进内核 */ ··············································· /* initcalls are now grouped by functionality into separate #define __define_initcall(level,fn) / #define core_initcall(fn) __define_initcall("1",fn) #define __initcall(fn) device_initcall(fn) #else /* MODULE 如果驱动模块动态加载入内核 */ ··············································· /* Each module must use one module_init(), or one no_module_init */ /* This is only required if you want to be unloadable. */
在看linux核心代码的时候看到/init/main.c 里面的do_initcalls函数
static void (void)
{
*call;
for (call = ; call < ; call++)
(*call);
/* Make sure there is no pending stuff from the initcall sequence */
();
}
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", );
}
}
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_thread
1.static void noinline rest_init(void)
2. __releases(kernel_lock)
3.{
4. system_state = SYSTEM_BOOTING_SCHEDULER_OK;
5.
6. kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);
7. numa_default_policy();
8. unlock_kernel();
9.
10. /*
11. * The boot idle thread must execute schedule()
12. * at least one to get things moving:
13. */
14. __preempt_enable_no_resched();
15. schedule();
16. preempt_disable();
17.
18. /* Call into cpu_idle with preempt disabled */
19. cpu_idle();
20.}
第6行通过kernel_thread创建一个内核线程执行init函数。(其实这里创建的即Linux的1号进程(init进程), 为linux中所有其他进程的父进程,有兴趣的可以自己查资料)
2) do_initcalls
1.static void __init do_initcalls(void)
2.{
3. initcall_t *call;
4. int count = preempt_count();
5.
6. for (call = __initcall_start; call
7. ……
8. result = (*call)();
9. ……
10. }
11.}
其中, initcall_t类型如下:
typedef int (*initcall_t)(void);
__initcall_start和__initcall_end定义在vmlinux.lds内,表示initcall section的起始和结束地址。
/* arch/arm/kernel/vmlinux.lds */
……
__initcall_start = .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
__initcall_end = .;
……
因此,上面6-10行代码的作用为按initcall level等级的顺序,依次循环调用预先存储在initcall section内的所有各个级别的初始化函数。这样,kernel的initcall函数的原理我们就搞清楚了。
最后要注意的是rest_init是在start_kernel函数内最后部分才被调用执行的,rest_init前包含了kernel一系列的初始化工作。另外,这些不同level等级的initcall.init section本身有一定的执行顺序,因此如果你的驱动依赖于特定的执行顺序的话需要考虑到这一点。
initcall机制原理及实践
linux内核中关于网络初始化过程中do_basic_setup等的问题
linux网络初始化时,initmain.c中的start_kernel()函数中调用kernel_thread启动了init进程,该进程对应于 initmain.c中的init函数。init函数中调用do_basic_setup()函数,这个函数又调用了netsocket.c中的 sock_init()函数。
do_basic_setup()的功能:
总线初始化(比如pci_init())
网络初始化(初始化网络数据结构,sock_init();2.4.17中sock_init()只是初始化了网络数据结构,真正的协议初始化是运行do_initcalls()时自动加载各个协议模块的)
创建事件管理核心线程(start_context_thread())
再加载任何模块(用do_initcalls()启动任何使用__initcall标识的函数(方便核心研发者添加启动函数))
#define module_init(x) __initcall(x);这句在linuxinit.h中,通过这么定义,使得系统初始化到调用initmain.c中的do_initcalls时自动加载了模块x(进入x初始化)。而在af_inet.c中有module_init(inet_init);所以也包括初始化inet模块了。呵呵
在2.6.14.2中就比较容易理解了。看
static void __init do_basic_setup(void)
{
/* drivers will send hotplug events */
init_workqueues();
usermodehelper_init();
driver_init();
#ifdef CONFIG_SYSCTL
sysctl_init();
#endif
/* Networking initialization needs a process context */
sock_init();
do_initcalls();
}
主要根据代码看的,比较仓促,因为这不是我现在要做的主要工作,来不及周详整理了,先这样记下好了,有异议的朋友欢迎留言讨论,呵呵。
内核启动时,设备及驱动初始化的实现
Uboot完成系统的引导并将Linux内核拷贝到内存之后,bootm -> do_bootm_linux()跳转到kernel的起始位置;
压缩过的kernel入口在arch/arm/boot/compressed/head.S,它将调用函数 decompress_kernel()<./arch/arm/boot/compressed/misc.c>解压,打印 “Uncompressing Linux...”,调用gunzip(),打印"done, booting the kernel."
然后call_kernel,执行解压后的kernel,经linux/arch/arm/kernel/head.S调用start_kernel转入体系结构无关的通用C代码,在start_kernel()中完成了一系列系统初始化,设备及驱动的注册即在此时完成:
<./init/main.c>-------------------------
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
printk(KERN_NOTICE "Kernel command line: %s/n", saved_command_line);
//打印内核命令行
parse_early_param();
parse_args("Booting kernel", command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
//解析由BOOT传递的启动参数
···········································································
rest_init();
}
{
·······················
do_basic_setup();
······················
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) { //判断在启动时是否指定了init参数
//如果指定则执行用户init进程,成功将不会返回
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults.../n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
* Ok, the machine is now initialized. None of the devices
* have been touched yet, but the CPU subsystem is up and
* running, and memory and process management works.
*
* Now we can finally start doing some real work..
*/
static void __init do_basic_setup(void)-----------------
{
/* drivers will send hotplug events */
init_workqueues();
usermodehelper_init();
driver_init(); //建立设备模型子系统
sysctl_init();
#endif
sock_init();
}
<./drivers/base/init.c>-------------------------
/**
* driver_init - initialize driver model.
*
* Call the driver model init functions to initialize their
* subsystems. Called early from init/main.c.
*/
{
/* These are the core pieces */
devices_init();
<./drivers/base/core.c>-------------
int __init devices_init(void)
{
return subsystem_register(&devices_subsys);
}
-----------------------
buses_init();
classes_init();
firmware_init();
* core core pieces.
*/
platform_bus_init();
system_bus_init();
cpu_dev_init();
memory_dev_init();
attribute_container_init();
}
---------------------------
extern initcall_t __initcall_start[], __initcall_end[];
{
initcall_t *call;
int count = preempt_count();
··················
(*call)(); //调用一系列初始化函数
···················
}
---------------------------
__initcall_start和__initcall_end界定了存放初始化函数指针区域的起始地址,即从__initcall_start开始到 __initcall_end结束的区域中存放了指向各个初始化函数的函数指针。 由 (*call)()完成各个部分的初始化工作,且便于扩充。具体实现如下:
<./arch/arm/kernel/vmlinux.lds.S>-----------------
__initcall_start = .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
__initcall_end = .;
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*/
static initcall_t __initcall_##fn __attribute_used__ /
__attribute__((__section__(".initcall" level ".init"))) = fn
#define postcore_initcall(fn) __define_initcall("2",fn)
#define arch_initcall(fn) __define_initcall("3",fn)
//此处初始化了设备
/*----eg:arch_initcall(at91sam9261_device_init)---
static int __init at91sam9261_device_init(void)
{
at91_add_device_udc();
at91_add_device_dm9000();
armebs3_add_input_buttons();
return platform_add_devices(at91sam9261_devices, ARRAY_SIZE(at91sam9261_devices));
}
------------------------*/
#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)
/**
* 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);
//静态编译的驱动模块作为device_initcall在内核启动就被do_initcalls
/**
* module_exit() - driver exit entry point
* @x: function to be run when driver is removed
*
* module_exit() will wrap the driver clean-up code
* with cleanup_module() when used with rmmod when
* the driver is a module. If the driver is statically
* compiled into the kernel, module_exit() has no effect.
* There can only be one per module.
*/
#define module_exit(x) __exitcall(x);
#define module_init(initfn) /
static inline initcall_t __inittest(void) /
{ return initfn; } /
int init_module(void) __attribute__((alias(#initfn)));
//insmod 是通过系统调用sys_init_module(const char *name_user, struct module *mod_user)
//将动态驱动模块载入到内核空间
#define module_exit(exitfn) /
static inline exitcall_t __exittest(void) /
{ return exitfn; } /
void cleanup_module(void) __attribute__((alias(#exitfn)));