linux的启动过程与系统调用过程

2019-07-13 07:32发布

刚刚考完l嵌入式linux,总结了下linux的启动过程与系统调用的过程,怕以后忘,姑且先记下来,只是个大概的框架。
  1. linux的启动过程
    在上电之后,会产生一个复位异常,程序计数器PC会被强制指向一个地址,一般为0x00000000,从这里执行初始化引导代码。这里要分两种情况:如果是在PC机上,会首先执行固件中的boot代码,即BIOS,然后再执行硬盘MBR中的bootloader,即grub;如果是在开发板上,会直接运行bootloader,一般为uboot。
    以开发板上为例,uboot的启动分为两个阶段,第一阶段的代码用汇编语言编写,短小精悍,运行在Flash磁盘中,用来初始化硬件,主要任务是将第二阶段的部分加载到RAM中,然后跳转到第二阶段;第二阶段代码用c语言编写,运行在RAM中,初始化与本阶段有关的硬件,之所以用c语言编写,是因为在这一阶段可以进行下载模式/加载模式的选择,功能较复杂,用汇编实现比较困难,最后跳转至内核入口。
    linux内核的启动也分为两种情况:如果为未压缩的镜像vmlinux,会直接从入口运行,如果是压缩镜像zImage,则会先运行镜像头部的解压缩文件,然后对内核代码进行重定位,再跳转至内核入口继续运行。内核的入口为head.s的Entry(stext),head.s中的代码用来检测处理器,创建初始化页表目录,初始化堆栈,清理bss段,然后跳转至真正的内核启动入口main.c。
    mian.c 的第一个函数是start_kernel(),它主要完成如下几个步骤:1.调用setup_arch()函数进行与体系相关的初始化,而体系结构的确定是通过machine_desc结构体,它包含处理器型号、机器号等与体系结构有关的数据。2.创建中断向量表,初始化中断处理函数。3.初始化系统核心进程调度器和时钟中断处理机制。4.初始化内存管理……(等等各种初始化)。最后创建init进程,这是系统的1号进程,它来完成根文件系统的挂载,执行用户传来的“init= ”启动参数执行用户指定的命令,或者按顺序执行以下几个进程之一:/sbin/init、/etc/init、/bin/init、/bin/sh,如果这四个进程有一个启动成功,则init服务启动成功,init服务会按照inittab的参数进行后续的初始化,linux启动完毕。
  2. linux系统调用过程
    以open()函数为例,当用户应用程序运行到open()处时,会首先找到glibc库中的open()函数,它封装了open()的系统调用号,所以也成为封装例程。然后执行软中断,如果是在x86体系上软中断指令为int $0x80,在执行软中断前会先将系统调用号保存至eax寄存器,然后调用宏SAVE_ALL保存现场在用户栈,执行软中断后系统会内陷至内核态,因为软中断是一个异常,所以会执行一个异常处理函数,这个函数为system_call(),该函数会将系统调用号取出并乘4作为偏移地址(系统调用号位8位,而地址为32位),与系统调用表的基址相加找到open()对应的系统服务例程所在的地址,然后改变PC指向该地址,执行系统服务例程即sys_open(),sys_open()会调用do_sys_open()执行打开操作,执行完后会调用ret_from_sys_open()返回用户态,然后调用宏RESTORE_ALL恢复现场,系统调用结束。执行过程中,参数的传递是通过栈,因为系统服务例程的定义会加上关键字asmlinkage,它指明是通过栈来传递参数的。