嵌入式 Linux init 进程 | 深入剖析原理、自启与方案抉择
1. init是什么?
init是Linux系统中的“天字一号”进程:
它的进程号(PID)恒为1,是所有其他进程的父进程。其启动流程如:
init进程的核心功能包括:
2. init的分类
嵌入式Linux系统中,init的分类,从简单到复杂,三种主流方案:BusyBox init、SysVinit和systemd。Buildroot等构建系统也明确将这三类列为可选的初始化系统。
当前系统init类型的指令:
cat /proc/1/comm2.1 BusyBox init——轻量级选手
BusyBox init是专门为资源受限的嵌入式环境设计的最轻量级解决方案。它是Buildroot的默认init方案,对于大多数嵌入式系统而言已经足够使用。
比如,Buildroot_2019.02:
2.2 SysVinit——经典传统派
SysVinit源自AT&T在1983年发布的System V Unix系统,曾是绝大多数桌面Linux发行版采用的init方案。
2.3 systemd——现代全能型选手
systemd由Lennart Poettering主导开发,是当前Linux桌面和服务器领域最主流的init系统,已被Ubuntu、Fedora、Debian等主流发行版全面采用。
3. 各init开机自启动应用的区别
三种init方案在开机自启动应用的方式上有显著差异:
3.1 SysVinit
SysVinit的启动脚本命名规则为Sxxname(S表示Start,xx为两位数字编号,数字越小越先启动),启动过程中会按编号顺序逐个执行脚本,例如:
init进程启动之后,会对/etc/inittab文件的解释及执行。/etc/inittab文件里有什么内容如:
可以看到里面用到了两个脚本文件:
/etc/init.d/rcS
/etc/init.d/rcK
其中,可以看出/etc/init.d/rcS是在系统开机之后执行的脚本;/etc/init.d/rcK是在系统关机时执行的脚本。
下面看看/etc/init.d/rcS里面的内容:
这个rcS脚本会循环调用/etc/init.d文件夹下的以S+数字开头的脚本文件。即:
SysVinit这种机制简单直观,但随着服务数量的增长,串行启动的效率瓶颈越来越明显。
3.2 BusyBox init
BusyBox init与SysVinit程序自启动的方式大同小异。可以把 BusyBox init 看作 SysVinit 的精简子集,它们之间最核心的区别在于 SysVinit 拥有运行级别的概念,而 BusyBox init 则巧妙地绕过了它,但又能“模拟”出近乎一致的效果。
配置文件:/etc/inittab或/etc/init.d/rcS。通过 rcS 脚本挂载,适合一次性启动,不自动守护。通过 inittab 守护进程,自动重启保活。
自启动方式:直接编辑 inittab 或创建 S* 脚本。
3.3 systemd
systemd则截然不同:
我们只需编写一个简单的.service文件描述服务的启动命令、运行条件和其他依赖关系,然后执行systemctl enable即可设置开机自启动。启动时,systemd会解析所有Unit文件间的依赖关系,并行启动不冲突的服务,大大缩短了启动时间。自定义服务必须放在/etc/systemd/system/下。
开机自启动自定义服务的例子:
1. 将程序安装到合适的位置
systemd 服务通常使用绝对路径来指定可执行文件。建议将二进制文件放在/usr/local/bin/或/opt/下,例如自定义一个hello程序:
sudo cp ~/hello /usr/local/bin/同时确保文件具有可执行权限:
sudo chmod +x /usr/local/bin/hello2. 创建 systemd 服务单元文件
在/etc/systemd/system/目录下创建hello.service:
sudo vim /etc/systemd/system/hello.service写入以下内容:
[Unit] Description=Hello Systemd Demo Service After=network.target [Service] Type=simple ExecStart=/usr/local/bin/hello Restart=always RestartSec=5s User=nobody Group=nogroup StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target关键参数解释:
Type=simple:服务主进程就是ExecStart启动的进程。ExecStart:指定要运行的程序完整路径。Restart=always:无论程序因何退出(包括正常退出),都自动重启。因为我们循环中用了sleep(10),理论上不会退出,加上always可以兜底。User=nobody/Group=nogroup:使用低权限用户运行,降低安全风险。StandardOutput=journal:将程序的标准输出重定向到 systemd 日志(journalctl)。WantedBy=multi-user.target:将服务挂钩到多用户目标,实现开机自启。
注意:如果你的程序需要写文件或访问某些目录,
nobody用户可能没有权限,可以根据实际需求修改为其他普通用户(如www-data或你自己的用户名)。
3. 启动服务并设置开机自启
# 1. 重新加载 systemd 配置,让 systemd 识别新的服务文件 sudo systemctl daemon-reload # 2. 立即启动服务(验证是否能正常运行) sudo systemctl start hello.service # 3. 查看服务状态和最近的日志 sudo systemctl status hello.service此时能看到下面的输出,且Active状态为active (running),日志中已经出现了hello systemd:
# 4. 设置开机自动启动 sudo systemctl enable hello.service执行enable后,systemd 会在/etc/systemd/system/multi-user.target.wants/目录下创建一个指向hello.service的符号链接:
下次系统启动时,就会自动运行该服务。
4. 验证开机自启
重启系统:
sudo reboot重新登录后,检查服务状态:
systemctl status hello.service常用指令:
命令 | 用途 |
|---|---|
sudo systemctl start hello | 立即启动服务 |
sudo systemctl stop hello | 立即停止服务 |
sudo systemctl restart hello | 重启服务 |
sudo systemctl enable hello | 设置开机自启 |
sudo systemctl disable hello | 取消开机自启 |
systemctl status hello | 查看服务状态和最近日志 |
journalctl -u hello -b | 查看本次启动以来服务的完整日志 |
journalctl -u hello -f | 实时跟踪日志输出(类似 |
4. 当前主流的init方案
在桌面和服务器领域,systemd已毫无疑问地成为绝对主流。
在嵌入式Linux领域,情况则更为多元:Yocto Project虽然默认仍使用SysVinit,但已全面支持systemd切换;Buildroot默认选择BusyBox init,因为它在资源受限的嵌入式系统中最轻量、最高效。
选型建议:
此外,还有一些小众但值得关注的init方案:
4. 小结
init进程虽小(PID=1),却是整个Linux系统的“第一推动力”。从极致精简的BusyBox init,到经典稳健的SysVinit,再到功能强大的systemd,每一种方案都有其适合的应用场景。
在嵌入式系统开发中,正确选择init方案并掌握其开机自启动配置方法,是确保产品稳定可靠的关键一步。
