Linux启动流程梳理

将生成的u-boot.bin文件使用imxdownload软件转换为u-boot.imx文件并烧写到SD卡中,设置imx6ull从SD卡启动,当imx6ull上电后,芯片内部的boot rom程序将SD卡中的u-boot.imx文件拷贝到链接地址处,就可以开始运行U-Boot了。

U-Boot启动后,会通过汇编操作,完成中断向量表的设置、堆栈的初始化、内存的分配管理等等一些操作,最终,会调用arch/arm/lib/crt0.S文件中的_main函数,在该函数中主要设置了一些运行环境以及调用了一些初始化函数,其中,在U-Boot的移植过程中,比较关心或需要修改的是board_init_f()、board_init_r()这几个函数。

_main中会调用board_init_f()函数,该函数位于common/board_f.c 中,主要用来初始化 DDR,定时器,完成代码拷贝等等 。

在board_init_f()函数中,调用了一些列的函数来初始化一些外设和运行环境等,但是,在board_init_f中并没有初始化完所有的外设,还需要通过board_init_r来完成。板级初始化、初始化NAND、初始化EMMC、初始化环境变量、初始化各种输入输出设备、板子后续初始化、初始化网络设备。

在board_init_r函数的最后,会调用run_main_loop()函数,会进入一个main_loop的死循环,在main_loop()函数里面,会根据是否按下回车键,进入uboot命令模式,还是在倒计时结束之后,通过执行环境变量bootcmd里的命令,bootcmd里面保存着默认的启动命令,用于启动Linux内核。要启动Linux,需要先将Linux镜像文件、设备树文件拷贝到DRAM中。然后使用bootz命令来启动Linux内核即可。

U-Boot通过bootz或其它命令启动Linux内核之后,U-Boot就完成了其最终的任务,CPU将交由Linux内核进行管理。

在Linux内核链接脚本文件arch/arm/kernel/vmlinux.lds中,通过ENTRY(stext) 明确了Linux内核的入口,入口函数为stext,stext 定义在文件arch/arm/kernel/head.S 中。

在stext函数中,会获取CPU的一些基础信息以及CPU运行初始化操作,其中,对于应用程序或者Linux内核运行来说,比较重要的是验证了设备树的合法性以及最终调用start_kernel()函数来启动Linux内核。start_kernel()函数定义在文件init/main.c中。

start_kernel()通过调用众多的子函数来完成 Linux 启动之前的一些初始化工作,start_kernel()函数最后会调用rest_init()(init/main.c)函数即可。在rest_init()函数中,最重要的就是调用函数kernel_thread()创建kernel_init进程,也就是init内核进程。

init进程的PID为1。init 进程一开始是内核进程(也就是运行在内核态),后面init进程会在根文件系统中查找名为“init”这个程序,这个“init”程序处于用户态,通过运行这个“init”程序, init 进程就会实现从内核态到用户态的转变。init进程的进程函数是kernel_init()。

  1. 在kernel_init()函数中,调用kernel_init_freeable()函数,在kernel_init_freeable()函数中,通过调用函数prepare_namespace()来挂载根文件系统。根文件系统也是由命令行参数指定的,也就是U-Boot的bootargs 环境变量。比如“root=/dev/mmcblk1p2 rootwait rw”就表示根文件系统在/dev/mmcblk1p2 中,也就是 EMMC 的分区 2 中。
  2. ramdisk_execute_command是一个全局的char指针变量,此变量值为“/init”,也就是根目录下的init程序。ramdisk_execute_command也可以通过U-Boot传递,在bootargs中使用“rdinit=xxx”即可,xxx 为具体的 init 程序名字。
  3. 如果存在“/init”程序的话就通过函数run_init_process来运行此程序。
  4. 如果ramdisk_execute_command为空的话就看execute_command是否为空,execute_command的值是通过U-Boot传递,在bootargs中使用“init=xxxx”就可以了,比如“init=/linuxrc”表示根文件系统中的 linuxrc 就是要执行的用户空间init程序。
  5. 如果ramdisk_execute_command和execute_command都为空,那么就依次查找“/sbin/init”、“/etc/init”、“/bin/init”和“/bin/sh”,这四个相当于备用init程序,如果这四个也不存在,那么 Linux 启动失败!
  6. 如果以上步骤都没有找到用户空间的 init 程序,那么就提示错误发生!

你可能感兴趣的:(驱动开发,linux,运维,服务器)