3. Systemd入门
聊到系统启动,就绕不开Systemd。说实话,我刚接触Linux那会儿,用的还是SysV init那一套,后来Systemd横空出世,我一开始还挺抗拒的。但用久了你会发现,它确实把服务管理这件事做得更清晰了。今天我就带你把它掰开揉碎,好好讲讲。
3.1 Unit文件:Systemd的“积木块”
Systemd把系统里的每一个资源都抽象成了一个“Unit”。你可以把它想象成一块块乐高积木。服务是一个Unit,挂载点是一个Unit,甚至一个定时任务也是一个Unit。
每个Unit都有一个对应的配置文件,我们叫它Unit文件。这些文件通常放在两个地方:
- /usr/lib/systemd/system/:软件包自带的默认Unit文件,别动它。
- /etc/systemd/system/:系统管理员自定义或覆盖的Unit文件,优先级更高。
Unit文件的命名规则是 名称.类型。比如 nginx.service、dev-sda.device。类型决定了这个Unit是干什么的。常见的类型有:
| Unit类型 | 文件后缀 | 作用 |
|---|---|---|
| Service | .service | 管理系统服务(最常用) |
| Socket | .socket | 管理进程间通信的套接字 |
| Target | .target | 逻辑分组,类似运行级别 |
| Mount | .mount | 管理文件系统挂载点 |
| Timer | .timer | 管理定时任务(替代cron) |
/usr/lib/systemd/system/ 下的文件。需要自定义时,我会在 /etc/systemd/system/ 下创建一个同名的文件,或者用 systemctl edit 命令生成覆盖片段。这样升级软件包时,我的配置不会被冲掉。
3.2 Service Unit详解:从启动到守护
Service Unit是我们打交道最多的。一个典型的Service文件长这样:
[Unit]
Description=My Custom Service
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/myapp
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
我们来拆解一下。文件分三个部分:
[Unit] 段:描述与依赖
- Description:服务的简单描述,
systemctl status时会显示。 - After:指定在哪些Unit之后启动。注意,它不强制依赖,只控制顺序。
- Requires:强依赖。如果依赖的Unit启动失败,当前Unit也会失败。
- Wants:弱依赖。依赖的Unit启动失败,不影响当前Unit。
[Service] 段:核心行为
这是最关键的。Type字段决定了Systemd如何判断服务是否启动成功。我遇到过不少坑,都是Type没选对。
| Type | 含义 | 适用场景 |
|---|---|---|
| simple | 主进程启动后,Systemd就认为服务已启动 | 大多数后台服务 |
| forking | 主进程会fork一个子进程,父进程退出 | 传统Unix守护进程(如nginx) |
| oneshot | 执行一次就退出,不会常驻内存 | 初始化脚本、一次性任务 |
| notify | 服务启动后会通过sd_notify()通知Systemd | 需要精确控制启动状态的服务 |
其他关键字段:
- ExecStart:启动命令。必须写绝对路径。
- ExecStop:停止命令。如果不写,Systemd会发SIGTERM信号。
- ExecReload:重载配置的命令。
- Restart:什么情况下自动重启。可选值:no, on-success, on-failure, on-abnormal, always。
- RestartSec:重启前等待的秒数。
- User/Group:以哪个用户身份运行。安全考虑,别用root。
[Install] 段:安装与启用
这个段定义了服务如何被“安装”到启动链中。最常用的是 WantedBy。比如 WantedBy=multi-user.target,意思就是当系统进入multi-user.target时,这个服务会被启动。
你执行 systemctl enable myapp 时,Systemd会在 /etc/systemd/system/multi-user.target.wants/ 目录下创建一个符号链接,指向你的Service文件。这就是“启用”的本质。
3.3 Target与运行级别:从数字到名字
在SysV时代,我们用数字0-6表示运行级别。Systemd用Target替代了它。Target说白了就是一组Unit的集合。系统启动时,会激活一个默认的Target,然后这个Target会拉起来所有它“想要”的Unit。
常见的Target对应关系:
| SysV运行级别 | Systemd Target | 说明 |
|---|---|---|
| 0 | poweroff.target | 关机 |
| 1 | rescue.target | 单用户模式(救援模式) |
| 2, 3, 4 | multi-user.target | 多用户文本模式(无图形界面) |
| 5 | graphical.target | 多用户图形模式 |
| 6 | reboot.target | 重启 |
你想想看,Target的好处是什么?它不再是简单的数字,而是有意义的名称。而且Target之间可以建立依赖关系。比如 graphical.target 会 Wants multi-user.target,所以进入图形模式前,系统会先进入多用户模式。
查看当前默认的Target:
systemctl get-default
设置默认Target:
systemctl set-default multi-user.target
切换到另一个Target(不重启):
systemctl isolate graphical.target
WantedBy 挂到某个Target上,就融入了这个启动链条。
下面这张图,是我画的一个简化版启动流程,帮你理解Unit、Service和Target之间的关系:
嗯,到这里你应该明白了。Systemd的Unit文件就是服务的“身份证”,Target就是服务的“集合地”。写好自己的Service文件,挂到合适的Target上,系统启动时它就会自动跑起来。说白了,就是这么回事。