Linux启动过程
一、linux系统启动过程
1、BIOS/UEFI 自检
当计算机启动时,BIOS/UEFI 将会对硬件进行自检,包括 RAM、硬盘、键盘、显卡等设备。自检完成后,BIOS/UEFI 会寻找一个可引导的设备来加载操作系统。
2、引导加载器 ( Flash分区一般在这个阶段,即 uboot 阶段)
计算机启动过程中,BIOS/UEFI 确定了要引导的硬盘后,就会在硬盘的启动扇区(MBR)中读取引导程序。引导程序负责加载操作系统内核。
引导程序通常是 GRUB(Grand Unified Bootloader)或者 LILO(Linux Loader)。
uboot提供了第一层和第二层的功能(因此可以简化第一层和第二层为BIOS/Bootloader启动)
其主要任务包括:硬件初始化、系统(内核)引导、内存控制、设备驱动、日志打印等等。同时,U-Boot 还提供了一组与硬件交互的命令行工具,可以方便地进行调试和诊断。
在 U-Boot 中,硬件平台需要「板级支持包」(BSP)来实现。BSP 是针对特定硬件平台的软件包,其中包含了初始化硬件所需的所有代码,包括:中断控制器、串行口驱动、网络控制器等等。
3、内核初始化
当引导程序将内核加载到 RAM 中后,内核开始启动初始化过程。首先,内核会检查硬件设备,并初始化硬件设备驱动程序。然后,初始化内存管理、进程管理和文件系统。在完成这些初始化后,内核创建第一个用户进程 init,并将控制权转移到 init 进程。
4、系统初始化
init 进程接管控制权后,会重新读取 /etc/inittab 文件,并按照其中的指令来初始化系统。在这个过程中,会启动各种守护进程,例如网络服务、设置用户、打印服务等等,并完成一些其他的系统初始化工作。如:读取初始化脚本,加载系统服务、设备驱动程序和其他必要组件。这些任务通常由 /etc/rc.d , /etc/init.d 或 /etc/rc.local
等目录下的启动脚本完成。传统SysVinit系统中,init进程执行/sbin/init
或/etc/init
。而在现代的Linux发行版中,一般会使用systemd或Upstart作为初始化系统。
在systemd中:启动脚本通常被称为单元(unit)。这些单元文件存储在/etc/systemd/system/
目录和/usr/lib/systemd/system/
目录中
在Upstart中:启动脚本被称为job。通常在/etc/init/
目录中,命名为*.conf
5、启动用户进程--- 包括APP程序的运行
(1)简介:在系统初始化完成后,init 进程负责启动一些后台进程和服务,一般情况下,大多数的linux操作系统会调用/etc/init.d/
目录下的启动脚本来配置应用程序的自动启动,还可用于管理系统服务的启动和停止,init进程一般是系统启动过程的第一个进程(进程ID为1),它负责启动和管理系统中的各个服务和进程。
还可直接在/etc/rc.local脚本中添加命令,系统开机后自动运行,如下通过打印看出这个脚本确实会开机自动运行。
注意/etc/rc.local
某些Linux发行版中已被弃用
请注意现代的 Linux 发行版不再使用init.d
目录,通常使用 systemd 作为初始化系统,如需了解可以ChartAI搜索 “使用 systemd 的基本步骤”
(2)init
又通过运行级别(runlevel)来确定是否执行脚本。运行级别用来确定系统处于哪个状态,每个状态决定了哪些服务应该启动或停止,具体来说,当系统启动或运行级别更改时,init
会调用 /etc/init.d
目录中特定名称的脚本并传递参数,例如 start
启动服务,stop
停止服务,restart
重启服务,status
获取服务状态等。大多数 Linux 发行版中,运行级别被定义在 /etc/inittab
文件中。格式如下:
在 systemd 中,运行级别被替换为 “targets”(目标)的概念,systemctl list-units --type=target
命令查看各个状态下对应的脚本或目标。
通过下面日志看出运行级别省略了
由图可知 系统初始化时会执行 init.d/rcS 脚本,通常用于执行一些基本系统设置、加载必要的模块和配置等操作
由图可知在 init.d/rcS 脚本里又循环执行了 init.d/ 下所有按命名约定的脚本
第一条命令:source /etc/profile 系统级配置文件,包含所有用户环境变量和初始化脚本。
第二条是一个 for
循环语句,遍历 /etc/init.d
下以 S
开头且后跟两个字符的文件。通常是系统启动时需执行的脚本,按命名约定,S??*
通配符,以 S
开头,后两个字符确定执行顺序。
也可以直接/etc/init.d/S22appinit 这样运行脚本
(3)启动脚本文件:通常位于/etc/init.d/
目录,通常按执行顺序排列。此目录中的启动脚本由系统初始化进程(init进程)调用。
写一个简单的 test.c,实现 “hello word!” ,再写一个runapp.sh的脚本和上面对应, makefile如下,编译后app和脚本都放到/usr/data/
至此,我们的APP应用程序开始执行(注意我们在编译代码的时候要用开发板厂家提供的交叉编译器才能在开发板上正常运行)
(4)、查看当前运行进程:ps,如图1,使用lsof
命令查看进程打开的文件和进程ID的相关信息:lsof -p 343 如图2:
init前 linuxrc
是在系统引导过程中由init进程调用的特殊脚本,它执行一些常见的任务,如加载内核模块、挂载文件系统、设置系统环境变量、执行初始化脚本、启动系统服务等等。通常是由发行版的开发团队或系统管理员编写和配置,以适应特定的系统需求和配置。
(5)、不同开发板可能启动方式不同,但是原理都一样,如下是另一个开发板启动日志,此开发板没有网络,程序只能烧录到Flash对应的app区,从下面可以看到,会先将程序从flash拷贝到/tmp/Cerb/CerbApp,从图二脚本看出是运行/tmp/Cerb/下的APP,并且还拷贝一些配置出来,为了方便用户读写。
我们也可以看看它驱动是如何加载的:
bootload: 此开发板由于系统较为简单且启动配置需求较少,因此也没用uboot而是用X-Loader,启动时仅有如下打印:
rootnfs: 由于系统简单,构建根文件系统时也常常使用:busybox
(6)、再来看一个m6ull开发板的启动过程
这些以S
开头的脚本通常包含一些系统初始化和配置任务,如设置系统变量、加载模块(必要的内核模块)、启动服务等。
6、内核空间、用户空间:分隔是为了保证系统的安全性和稳定性。
(1)、简介:在Linux操作系统中,存在内核空间和用户空间两个不同的虚拟地址空间。系统调用处于内核空间,应用程序处于用户空间。
(2)、内核空间:
●操作系统内核运行的区域,拥有最高的特权级别。
● 内核空间可访问系统的所有硬件、内存和其他资源。
● 内核代码、设备驱动程序以及核心操作系统的其他组件都在内核空间中执行。
● 用户程序不能直接访问,必须通过系统调用接口发起请求,如:open()。也可使用C库。C库最终也是调用系统调用函数,如:fopen()。同理,内核驱动里也有一个与之相对应的open()函数。每一个系统调用都有一个编号。
(3)、用户空间:
●用户程序运行的区域,拥有较低的特权级别。
● 包含应用程序、库文件、用户数据等。
● 程序只能访问受限资源,并且不能直接访问底层硬件。
● 程序通过系统调用(System Call)接口向内核发起请求,以访问和利用内核提供的服务和功能。
7、从简单的内核驱动到app程序调用驱动并运行:
(1)、假设有这样一个字符设备驱动,只实现了驱动的框架,如下加载驱动会打印如下,删除驱动打印的是_exit。
(2)、写完驱动也是需要使用交叉编译器编译的,如下是驱动加载的指令,我们看到加载成功了,并且使用cat打印如下:
(3)、编写的app测试程序如下:可以看到main函数有两个参数,实际运用中如open(/usr/test.c, O_RDWR),则可用main(void),也可使用如下格式,参数不用管。
(4)、app程序运行如下:
8、加载驱动:驱动可以是内核自带的,也可以是编译为模块的外部驱动。加载驱动程序通常涉及到分配内存、注册设备和中断处理等操作。
(1)、加载编译为模块外部驱动的示例:
(2)、加载驱动的脚本通常在 /etc/modules-load.d
或自定义的脚本目录中,如果没有此目录,也可在init.d目录下加载:
这就跟我们 5、(3)、点对应起来了。