嵌入式Linux启动流程之启动内核(基于Arm)

2019-07-13 02:25发布

文章嵌入式Linux启动流程之BootLoader 中提到BootLoader完成的最后一项工作便是设置Linux的启动参数并调用 Linux内核。

调用Linux内核镜像

目前传递启动参数主要采用两种方式:即通过struct param_struct 和struct tag(标记列表,tagged list)两种结构传递。
struct param_struct 是一种比较老的参数传递方式,在2.4 版本以前的内核中使用较多。从2.4 版本以后Linux 内核基本上采用标记列表的方式。
但为了保持和以前版本的兼容性,它仍支持struct param_struct 参数传递方式,只不过在内核启动过程中它将被转换成标记列表方式。
标记列表方式是种比较新的参数传递方式,它必须以ATAG_CORE 开始,并以ATAG_NONE 结尾。中间可以根据需要加入其他列表。
Linux内核在启动过程中会根据该启动参数进行相应的初始化工作。
如果 Linux 内核存放在 Flash 中,并且可直接在上面运行(这里的 Flash 指 Nor Flash),那么可直接跳转到内核中去执行。但由于在 Flash 中执行代码会有种种限制,而且速度也远不及 RAM 快,所以一般的嵌入式系统都是将 Linux内核拷贝到 RAM 中,然后跳转到 RAM 中去执行。不论哪种情况,在跳到 Linux 内核执行之前 CUP的寄存器必须满足以下条件:r0=0,r1=处理器类型,r2=标记列表在 RAM中的地址。

Linux内核的启动过程

在 BootLoader将 Linux 内核映像拷贝到 RAM 以后,可以通过下例代码启动 Linux 内核:call_linux(0, machine_type, kernel_params_base)。 其中,machine_tpye 是 bootloader检测出来的处理器类型, kernel_params_base 是启动参数在 RAM 的地址。通过这种方式将 Linux 启动需要的参数从 bootloader传递到内核。 Linux 内核有两种映像:一种是非压缩内核,叫 Image,另一种是它的压缩版本,叫zImage。根据内核映像的不同,Linux 内核的启动在开始阶段也有所不同。zImage 是 Image经过压缩形成的,所以它的大小比 Image 小。但为了能使用 zImage,必须在它的开头加上解压缩的代码,将 zImage 解压缩之后才能执行,因此它的执行速度比 Image 要慢。但考虑到嵌入式系统的存储空容量一般比较小,采用 zImage 可以占用较少的存储空间,因此牺牲一点性能上的代价也是值得的。所以一般的嵌入式系统均采用压缩内核的方式。 下面简单说一下压缩内核的几个概念   vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。Linux 支持虚拟内存,不像老的操作系统比如DOS有640KB内存的限制。Linux能够使用硬盘空间作为虚拟内存,因此得名“vm”。vmlinuz是可执行的Linux内核,它位于/boot/vmlinuz,它一般是一个软链接。
  vmlinuz的建立有两种方式。一是编译内核时通过“make zImage”创建,然后通过:“cp /usr/src/linux-2.4/arch/i386/linux/boot/zImage /boot/vmlinuz”产生。zImage适用于小内核的情况,它的存在是为了向后的兼容性。二是内核编译时通过命令make bzImage创建,然后通过:“cp /usr/src/linux-2.4/arch/i386/linux/boot/bzImage /boot/vmlinuz”产生。bzImage是压缩的内核映像,需要注意,bzImage不是用bzip2压缩的,bzImage中的bz容易引起误解,bz表示“big zImage”。 bzImage中的b是“big”意思。
  zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip压缩的。它们不仅是一个压缩文件,而且在这两个文件的开头部分内嵌有gzip解压缩代码。所以你不能用gunzip 或 gzip –dc解包vmlinuz。
  内核文件中包含一个微型的gzip用于解压缩内核并引导它。两者的不同之处在于,老的zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么可以采用zImage 或bzImage之一,两种方式引导的系统运行时是相同的。大的内核采用bzImage,不能采用zImage。 目前很多嵌入式Linux系统(比如Android)都是使用大内核。 Linux通常有以下两种可选的启动方式:
Flash本地运行方式:内核的未经压缩的可执行映像固化在Flash,系统启动时内核在Flash中开始逐句执行。kernel映象为非压缩格式,通过make Image获得,那么真正的入口就是arch/arm/kernel/head_armv.S

压缩内核加载方式:内核的压缩映像固化在Flash上,系统启动时由附加在压缩映像前的解压复制程序读取压缩映像,在内存中解压后执行,这种方式相对复杂,但是运行速度更快(RAM的存取速率要比Flash高)。如果是zImage,那么程序的入口是arch/arm/boot/compressed/head.S。 我们主要介绍压缩内核的加载方式
在最初的引导过程中,是通过arch/arm/boot/compressed/head.S利用misc.c中定义的decompress_kernel()函数,将内核vmlinuz解压。decompress_kernel函数打印出信息 "Uncompressing Linux..."后,调用解压缩函数将内核解压到内存的指定位置。 【注:在linux的源码中可以看到与misc相关的文件或函数名,使用misc来命名主要是表示该文件目前还没归类好,不知道将它归到哪个方面或者放置在哪个地方比较好,所以暂时用misc。】 之后,进入到kernel/arch/arm/kernel/head.S中vmlinux的入口地址。 最后就跳转到start_kernel()中去了。start_kernel()是"init/main.c"中的定义的函数,start_kernel()中调用了一系列初始化函数,以完成kernel本身的设置。start_kernel()函数中,做了大量的工作来建立基本的Linux核心环境。如果顺利执行完start_kernel(),则基本的Linux核心环境已经建立起来了。