Linux初始化系统

想要设置开发板的某程序开机时自启动,有多种方式,本质上是使用了Linux的初始化系统(在linux内核启动后期会尝试加载用户空间的init程序,init程序是由内核启动的第一个用户空间程序(PID为1),该程序负责启动用户空间的服务和程序),init程序由初始化系统提供:

可以通过以下方法查看系统的init进程到底是谁:

法1:

image-20250818145324336

image-20250818092207319

法2:pstree -p

Busybox

BusyBox 是一个高度集成的嵌入式Linux工具集,它既是一个轻量级初始化系统,也是一个精简版的Unix命令集合。它的核心设计目标是用最小的空间提供完整的Linux系统功能

BusyBox 提供了一个简化的init程序,用于替代传统的 SysVinitsystemd,它的init程序有2种实现形式:

(1) 传统 SysVinit 风格

  • 配置文件:/etc/inittab

  • 启动流程:

    1. 内核启动后执行 /sbin/init(通常是 BusyBox 的符号链接)
    2. BusyBox 读取 /etc/inittab,定义运行级别和启动脚本
    3. 执行 /etc/init.d/中的脚本(如 rcSrc.local

    示例 /etc/inittab

    1
    2
    3
    4
    ::sysinit:/etc/init.d/rcS
    ::askfirst:/bin/sh
    ::ctrlaltdel:/sbin/reboot
    ::shutdown:/sbin/swapoff -a

(2) 简化的 BusyBox init

  • 如果没有 /etc/inittab,BusyBox 会执行默认行为:
  • 挂载 /proc/sys
  • 运行 /etc/init.d/rcS(如果存在)
  • 启动一个登录 shell(如 /bin/sh

System V Init

利用Linux的System V Init机制,它主要依靠内核启动的第一个用户进程initd来运行一些脚本来实现

  1. 内核启动后运行 /sbin/init

  2. init 读取 /etc/inittab 确定默认运行级别

    • SystemV给出7个运行级别,分别对应rc0.d~rc6.d目录image-20250723214655397
    • 并且还有个rcS.d目录,它在所有运行级别之前运行
  3. 执行相应运行级别的rc脚本(如 /etc/rc.d/rcX.d

  4. rcX.d脚本按顺序执行该目录下的服务脚本链接

用该方法的好处主要是可以控制各个脚本的启动顺序

具体操作:

  • /etc/init.d/ 目录下添加自启动脚本

Linux 开机的时候,会加载运行 /etc/init.d 目录下的程序,因此我们可以把想要自动运行的脚本放到这个目录下即可。系统服务的启动就是通过这种方式实现的

  • 添加完后务必设置文件的可执行权限
  • 脚本必须以S或者K开头,比如:S01syslogd 、S02klogd 、S02sysctl 、S40network,S代表启动、K代表关机,这些脚本按数字升序依次执行

缺点

  • 启动时间长。init进程是串行启动,只有前一个进程启动完,才会启动下一个进程
  • 启动脚本复杂。init进程只是执行启动脚本,不管其他事情。脚本需要自己处理各种情况,这往往使得脚本变得很长

参考链接

systemd

定义

  • systemd 是现代Linux系统的初始化系统与系统管理守护进程
  • 它在 Linux 内核启动完成之后,作为第一个用户态进程(PID 1)运行,负责启动和管理系统中的所有其他进程。
  • 它不仅仅是一个 init 程序,还包括一系列工具和守护进程,用于:
    • 启动服务(Service Management)
    • 依赖管理和并行化启动
    • 日志管理(journalctl)
    • 设备管理(udev 集成)
    • 挂载点和自动挂载管理
    • 定时任务(timer)替代 cron

几乎所有主流 Linux 发行版(Fedora、Red Hat、Debian、Ubuntu、Arch Linux 等)都已经采用 systemd 作为默认 init 系统

切换到systemd后,可能会保留SystemV初始化系统的一些文件,比如/etc/init.d之类的,里面的文件不会直接起作用,而会被systemd转成适用于它的单元文件,再起作用。之所以这样做是为了兼容一些软件(它们在安装时使用了SystemV来进行自启动)

设计目标

  1. 更快的启动速度
    • 使用依赖关系树和并行化启动(不像 SysVinit 顺序执行脚本)
    • 利用 socket 激活(服务按需启动)
  2. 统一管理接口
    • 所有服务、挂载点、套接字、设备等都被抽象为 unit,统一管理
  3. 增强可靠性
    • 服务崩溃时自动重启
    • 使用 cgroup 控制服务进程,防止“孤儿进程”
  4. 模块化和扩展性
    • 提供 systemctljournalctl 等工具
    • 替代部分传统组件(cron、inetd、syslog)

核心概念

Unit(单元)

  • systemd 中所有受管对象都叫 unit,以配置文件描述,文件放在:
    • /etc/systemd/system/
    • /run/systemd/system/
    • /lib/systemd/system/
  • unit文件的类型包括:
    • service:守护进程或一次性任务(如 nginx.service
    • socket:套接字(systemd 可通过 socket 激活启动服务)
    • device:内核设备(udev 管理)
    • mount/automount:挂载点与自动挂载
    • target:服务组(类似于 SysV 的运行级别 runlevel),可以看成同步点
    • timer:定时器(替代 cron)
    • slice:资源控制分组(基于 cgroup)
    • scope:非 systemd 启动的外部进程组

配置文件(Unit 文件)

.service 为例:

1
2
3
4
5
6
7
8
9
10
[Unit]
Description=My Example Service
After=network.target # 指定启动顺序,该服务必须在network.target中所有服务启动完毕后启动

[Service]
ExecStart=/usr/bin/my_service
Restart=on-failure

[Install]
WantedBy=multi-user.target # 指定该服务被enable时,应该挂到哪个target下
  • [Unit]:描述、依赖关系
  • [Service]:如何启动、停止、重启
  • [Install]:该服务被挂到哪些 target 下启用(即开机自启设置)

Target(服务组)

  • 类似 SysV 的运行级别 (runlevel)
  • 它本身不执行命令,而是作为一个「分组」和「同步点」:
    • 分组作用:把一堆服务组织在一起,统一管理(像运行级别 runlevel)
    • 同步作用:让 systemd 知道某个阶段达成了,可以继续下一个阶段(类似「里程碑」)
  • 常见 target:
    • graphical.target → 图形界面模式
    • multi-user.target → 多用户命令行模式
    • rescue.target → 单用户维护模式
    • default.target → 系统默认启动级别(一般指向上面某个 target)

当 systemd 启动时,它会:

  1. 看到默认目标假如是 multi-user.target
  2. 查找 multi-user.target 依赖的所有target和service
  3. 并行启动它依赖的target,然后再启动它自己的service

Journal(日志系统)

  • systemd-journald 负责日志收集。

  • 统一收集:

    • 内核日志
    • init 输出
    • 服务标准输出/错误输出
    • syslog
  • 使用 journalctl 查看:

    1
    2
    3
    journalctl -u ssh.service       # 查看 ssh 服务日志
    journalctl -b # 查看本次启动日志
    journalctl -f # 实时日志(类似 tail -f)

systemd 的运行流程

  1. Linux 内核启动完成后,执行 systemd 作为 PID 1
  2. 读取默认目标(default.target),加载对应的 unit
  3. 解析 unit 之间的依赖关系,尽可能并行地启动服务
  4. 启动过程中会进行 socket/device/mount 等激活
  5. 最终进入目标运行级别(如 multi-user.targetgraphical.target

systemd 常用命令

服务管理

1
2
3
4
systemctl start nginx.service       # 启动服务
systemctl stop nginx.service # 停止服务
systemctl restart nginx.service # 重启服务
systemctl status nginx.service # 查看服务状态

开机自启

1
2
3
systemctl enable nginx.service      # 设置开机自启
systemctl disable nginx.service # 禁止开机自启
systemctl is-enabled nginx.service # 检查是否开机自启

Target 管理

1
2
systemctl isolate multi-user.target # 切换到多用户模式
systemctl set-default graphical.target # 设置默认 target

查看信息

1
2
systemctl list-units --type=service    # 查看所有已加载的服务
systemctl list-unit-files # 查看所有 unit 文件

使用systemd创建一个开机自启动的服务的流程是什么?

1.准备好要启动的进程

2.创建一个unit文件,比如xxx.service,并写好内容

3.通过sudo systemctl daemon-reload刷新一下unit文件

4.systemctl start xxx.servicesystemctl status xxx.service启动服务并查看其状态是否正常

5.systemctl enable xxx.service设置开机自启动