kernel入口函数编译运行

内核的start_kenel函数在调用rest_init函数之前,其主要工作与操作系统核心层相关,包括进程调度、内存管理和中断系统等主要模块的初始化。而rest_init函数将创建kernel_init进程,并由该进程调用do_basic_setup->do_initcalls函数完成所有外部设备的初始化。

1 extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
2 
3 static void __init do_initcalls(void)
4 {
5     initcall_t *fn;
6 
7     for (fn = __early_initcall_end; fn < __initcall_end; fn++)
8         do_one_initcall(*fn);
9 }

 

 do_inicalls函数的主体是将的__early_inicall_end和__initcall_end指针之间的函数全部执行一遍,这两个指针在vmlinux.lds中定义。在生成的操作系统内核时,一些需要在linux系统初始化时的函数指针被加入到这两个参数之间,之后由do_initcalls函数统一调用这些函数。linux系统定义了一系列需要在系统初始化时执行的模块,如下所示:

 1 #define early_initcall(fn)      __define_initcall("early",fn,early)
 2 
 3 /*
 4  * A "pure" initcall has no dependencies on anything else, and purely
 5  * initializes variables that couldn't be statically initialized.
 6  *
 7  * This only exists for built-in code, not for modules.
 8  */
 9 #define pure_initcall(fn)       __define_initcall("0",fn,0)
10 
11 #define core_initcall(fn)       __define_initcall("1",fn,1)
12 #define core_initcall_sync(fn)      __define_initcall("1s",fn,1s)
13 #define postcore_initcall(fn)       __define_initcall("2",fn,2)
14 #define postcore_initcall_sync(fn)  __define_initcall("2s",fn,2s)
15 #define arch_initcall(fn)       __define_initcall("3",fn,3)
16 #define arch_initcall_sync(fn)      __define_initcall("3s",fn,3s)
17 #define subsys_initcall(fn)     __define_initcall("4",fn,4)
18 #define subsys_initcall_sync(fn)    __define_initcall("4s",fn,4s)
19 #define fs_initcall(fn)         __define_initcall("5",fn,5)
20 #define fs_initcall_sync(fn)        __define_initcall("5s",fn,5s)
21 #define rootfs_initcall(fn)     __define_initcall("rootfs",fn,rootfs)
22 #define device_initcall(fn)     __define_initcall("6",fn,6)
23 #define device_initcall_sync(fn)    __define_initcall("6s",fn,6s)
24 #define late_initcall(fn)       __define_initcall("7",fn,7)
25 #define late_initcall_sync(fn)      __define_initcall("7s",fn,7s)
26 
27 #define __initcall(fn) device_initcall(fn)
28 
29 #define __exitcall(fn) \
30     static exitcall_t __exitcall_##fn __exit_call = fn
31 …………
32 #define module_init(x)  __initcall(x);

以上初始化模块按照_define_initcall定义的顺序执行。如果linux设备驱动程序采用build-in的方式而不是作为module形式加载时,将使用device_initcall函数或者device_initcall_sync函数加载。
在linux系统初始化时运行的模块需要使用以上的宏,定义该模块的函数指针之后该模块的函数指针将加入linux的__early_inicall_end和__initcall_end之间。

在linux内核的System.map文件中,可以找到__early_initcall_end和__initcall_end中的所有函数指针。

经过以上的分析,可以知道,这些入口函数,只要被编译进内核了,就一定会被链接器放到指定的位置,然后内核在运行时就一定会运行到指定的函数。

在内核要想编译某个文件可以经过如下步骤,下面以driver/video/cirrusfb.c文件为例。

这个文件中有个函数通过module_init注册。为了使这个函数在在内核运行时就运行,需要将这个函数编译进内核。可以通过以下步骤来确定编译了这个文件。

1:首先找这个文件所在的目录,打开这个目录下的Makefile文件,找到这个文件对应的选项

1 obj-$(CONFIG_FB_CIRRUS)       += cirrusfb.o

可以看到要想编译这个文件,需要将CONFIG_FB_CIRRUS的值赋为y。这可以直接在arch/XXX/config/下的config文件中直接赋值。但是做到这不一定就可以正确编译到这个文件了。首先由于内核Makefile的编译方式,它会根据目录,一层层的往下调用Makefile,因此,要确保这个文件编译,就需要确定它的上层目录也是要被编译的。即driver目录下的Makefile要编译video目录

1 obj-y               += video/

2:做到以上这些还不够,因为由于设备相关性本身的逻辑关系,还要把与这个文件相关的上层依赖都编译。这个可以看同目录下的Kconfig文件

1 config FB_CIRRUS
2     tristate "Cirrus Logic support"
3     depends on FB && (ZORRO || PCI) 
4     select FB_CFB_FILLRECT
5     select FB_CFB_COPYAREA
6     select FB_CFB_IMAGEBLIT

需要打开FB和PCI,所以我同样可以在arch/XXX/config/下的config文件中也打开这个选项。打开后如下:

1 CONFIG_FB=y
2 CONFIG_FB_PUV3_UNIGFX=n
3 CONFIG_FB_CIRRUS=y

确保了以上这些后,就可以正确编译cirrusfb.c文件了,如此,module_init中的函数也会在运行内核时自动运行。

posted on 2012-04-12 20:12  hydah  阅读(1298)  评论(0编辑  收藏  举报