经历了跟体系结构密切相关的汇编代码之后,就可以进入C语言编写的结构无关的代码了。这个入口的函数是start_kernel函数,它主要更进一步地初始化系统相关的内容,以便系统进入一种服务状态,提供一种虚拟机的服务,提供各种API调用的服务。在start_kernel函数里,需要非常注意的是里面初始化函数的顺序,这些初始化函数不能随便调换初始化顺序,否则就会导致系统运行出错。由于这个函数的内容非常多,涉及的内容也非常广泛,每个函数都有一个比较大的概念,一种原理,一种想法。因此,对于这个函数的学习需要很多时间,需要有漫长学习的心理准备。由于本书基于ARM体系的一种结构学习,其它与此体系结构无关的代码,就不再分析介绍。好了,现在就来开始学习第一节的内容,代码如下:

asmlinkage void__init start_kernel(void)

{

char * command_line;

看了这段代码,首先发现asmlinkage__init与一般开发C语言的应用程序有着明显的差别,导致看不懂这两个宏到底是用来做干什么用的。其实这两个宏是写内核代码的一种特定表示,一种尽可能快的思想表达,一种尽可能占用空间少的思路。asmlinkage是一个宏定义,它的作用主要有两个,一个是让传送给函数的参数全部使用栈式传送,不用寄存器来传送。因为寄存器的个数有限,使用栈可以传送更多的参数,比如在X86CPU里只能使用6个寄存器传送,只能传送4个参数,而使用栈就没有这种限制;另外一个用处是声明这个函数是给汇编代码调用的。不过在ARM体系里,并没有使用栈传送参数的特性,原因何在?由于ARM体系的寄存器个数比较多,多达13个,这样绝大多数的函数参数都可以通过寄存器来传送,达到高效的目标。因此,看到文件kernel/include/linux/linkage.h里的asmlinkage宏定义如下:

#ifdef __cplusplus

#defineCPP_ASMLINKAGE extern "C"

#else

#defineCPP_ASMLINKAGE

#endif


#ifndef asmlinkage

#define asmlinkageCPP_ASMLINKAGE

#endif

在这里可以看到asmlinkage,其实没有定义,所以ARM体系里还是通过寄存器来传送参数的。如果看一下X86下的代码,就会定义如下:

#ifdef CONFIG_X86_32

#define asmlinkageCPP_ASMLINKAGE __attribute__((regparm(0)))

这里定义asmlinkage为通过栈传送参数。

由此可见,不同的体系结构对代码的优化细节是不一样的,设计的理念不一样。接着来看另外一个宏定义__init,这个宏定义主要用来标志这个函数编译出来的目标代码放在那一段里。对于应用程序的编译和连接,不需要作这样的考虑,但是对于内核代码来说,就需要了,因为不同的段代码有着不同的作用,比如初始化段的代码,当系统运行正常以后,这段代码就没有什么用了,聪明的做法就是回收这段代码占用的内存,让内核的代码占最少的内存。还有另外一个作用,比如同一段的代码都是编译在一起,让相关联的代码尽可能同在一片内存里,这样当CPU加载代码到缓存时,就可以一起命中,提高缓存的命中率,这样就大大提高代码的执行速度。宏__init定义在文件kernel/include/linux/init.h里,代码如下:

#define__init __section(.init.text) __cold notrace

使用这个宏声明的函数,编译时就会把目标代码放到段.init.text里,这段都是放置初始化的代码。


最后看到声明一个字符的指针command_line,这个指针是指向命令行参数的指针,主要用来指向引导程序传送给内核的命令行参数,在后面的函数setup_arch和函数setup_command_line就会对它进行处理。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐