进程、服务与系统资源

共 35 题
📑 题目列表 35 题
#
★★★

1. Linux 进程状态 R、S、D、Z、T 的含义及典型场景

Linux 进程状态 R、S、D、Z、T 分别代表什么?各自典型场景是什么?

  • 各进程状态语义
  • 典型场景
  • 排障意义

R(running):进程在运行或可运行队列中,消耗 CPU;S(sleeping):可中断睡眠,等待 I/O 或事件(如等待网络、定时器),能被信号唤醒;D(uninterruptible sleep):不可中断睡眠,多为等待磁盘 I/O(内核态,无法被信号中断),大量 D 状态常表示磁盘 IO 瓶颈;Z(zombie):僵尸进程,子进程已退出但父进程未 wait() 回收,占 PID 不占资源,过多僵尸说明父进程未回收;T(stopped):进程被停止(如 Ctrl-Z 或 SIGSTOP),暂停执行。典型场景:R 是 CPU 密集负载;S 是正常等待(绝大多数进程);D 是磁盘/IO 等待(IO wait 高时多);Z 是父进程未回收子进程(僵尸);T 是调试/暂停。排障:top 看状态分布,D 多查 IO,Z 多查父进程。

核心是"状态语义 + 场景映射"。R 运行、S 可中断等待、D 不可中断 IO 等待、Z 僵尸、T 停止,D/Z 多需重点排查。

ps -eo stat,pid,comm | sort | uniq -c
top -b -n1 | grep -E 'R|D|Z'
# 查僵尸进程
ps -eo stat,pid,ppid,comm | awk '$1~/Z/'
#
★★★

2. systemctl daemon-reload 与 daemon-reexec 的差异

systemctl daemon-reload 与 daemon-reexec 的差异是什么?

  • daemon-reload 重载配置
  • daemon-reexec 重启管理进程
  • 差异

systemctl daemon-reload 重新加载 systemd 的 unit 配置文件(从磁盘读取 .service/.socket 等),应用配置变更,不重启已运行服务;用于修改 unit 文件后让 systemd 感知新配置。systemctl daemon-reexec 重新执行 systemd 守护进程本身(杀掉并重启 systemd PID 1),用于升级 systemd 或修复 systemd 状态,会中断 systemd 管理(但服务通常不受影响,因为 systemd 只是重新 exec)。差异:daemon-reload 只重载配置(轻量、不影响服务),daemon-reexec 重启 systemd 进程本身(较重,用于 systemd 升级/状态修复)。日常修改 unit 用 daemon-reload;升级 systemd 或用 daemon-reexec 关键时要谨慎。注意:daemon-reload 后需重启相关服务才生效(配置变更对已运行服务需 restart)。

核心是"重载配置 vs 重启守护进程"。daemon-reload 重载 unit 配置不影响服务,daemon-reexec 重启 systemd 进程本身。

systemctl daemon-reload
systemctl daemon-reexec
#
★★★

3. systemd unit 文件中 ExecStart、ExecReload、ExecStop 的语义

systemd unit 文件中 ExecStart、ExecReload、ExecStop 的语义是什么?

  • 三个 Exec 指令
  • 启动/重载/停止
  • 语义

ExecStart 指定服务启动时执行的主命令(必须,定义服务主进程);ExecReload 指定 systemctl reload 时执行的命令(重载配置,不重启服务);ExecStop 指定 systemctl stop 时执行的命令(停止服务,默认为 SIGTERM)。语义:ExecStart 是主进程(服务存活的核心),ExecReload 是"热重载"(如 nginx -s reload 重载配置),ExecStop 是"停止钩子"(清理资源、优雅停止)。注意:ExecStart 多个用多行(systemd 允许),ExecStart 命令是主进程(Type=simple 时直接是主进程);ExecReload 若未定义,reload 会失败或报错;ExecStop 执行后 systemd 仍会发 SIGTERM。配置:ExecStart 必填,ExecReload/ExecStop 可选。运维:热重载用 ExecReload,优雅停止用 ExecStop。

核心是"启动/热重载/停止钩子"。ExecStart 主进程、ExecReload 热重载、ExecStop 停止钩子,三者管服务生命周期。

[Service]
ExecStart=/usr/bin/app
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/usr/bin/app stop
#
★★★

4. systemd unit 的 WantedBy 与 RequiredBy 在 enable 时的差异

systemd unit 的 WantedBy 与 RequiredBy 在 enable 时的差异是什么?

  • WantedBy 与 RequiredBy
  • enable 的软链
  • 依赖强度

WantedBy 与 RequiredBy 都用于 enable 时把 unit 加入某个 target(如 multi-user.target),在 /etc/systemd/system/multi-user.target.wants/ 或 .requires/ 下创建符号链接。差异:WantedBy 创建的是"弱依赖"(wants 目录),unit 被 target 启动但不是必须,target 启动时启动该 unit,但该 unit 失败不影响 target(其他 wanted 单元);RequiredBy 创建"强依赖"(requires 目录),unit 被 target 强制启动,若该 unit 失败,target 也会失败(或停止)。因此 WantedBy 常用(如 WantedBy=multi-user.target 开机自启),RequiredBy 用于关键依赖(target 需要该 unit 必须成功)。enable 时 WantedBy 生成 .wants 软链、RequiredBy 生成 .requires 软链。选型:普通自启用 WantedBy,关键必启用 RequiredBy。

核心是"强/弱依赖"。WantedBy 弱依赖(.wants)失败不影响 target,RequiredBy 强依赖(.requires)失败拖垮 target。

# .service
[Install]
WantedBy=multi-user.target
# 或
RequiredBy=multi-user.target
systemctl enable app
#
★★★

5. systemd unit 的依赖 After、Before、Wants、Requires 的真实语义

systemd unit 的依赖 After、Before、Wants、Requires 的真实语义是什么?

  • 顺序依赖 vs 要求依赖
  • After/Before 与 Wants/Requires
  • 语义

systemd 依赖分两类:顺序依赖(After/Before)与要求依赖(Wants/Requires)。After=unit 表示"本单元在指定单元之后启动"(顺序),Before 反之;After 只控制顺序,不要求被依赖单元存在/成功。Wants=unit 表示"温柔依赖":启动本单元时也启动指定单元,但指定单元失败不影响本单元;Requires=unit 表示"硬依赖":启动本单元时强制启动指定单元,若指定单元启动失败,本单元也会失败(或被停止)。真实语义:After/Before 管"先后顺序",Wants/Requires 管"是否一同启动/是否必须成功"。最佳实践:既装顺序又装要求(After= + Requires=)才是"先启动依赖且依赖必须成功"。单独 After 不保证依赖已成功启动。选型:需要顺序用 After,需要一并启动用 Wants,需要必须成功用 Requires,常组合。

核心是"顺序 vs 要求"。After/Before 管顺序,Wants 温柔依赖、Requires 硬依赖,常组合 After+Requires 实现"先启动且必须成功"。

[Unit]
After=network.target
Requires=network.target
Wants=monitor.service
#
★★★

6. systemd unit 类型 service、socket、target、timer 的差异

systemd unit 类型 service、socket、target、timer 的差异是什么?

  • 各 unit 类型
  • 用途
  • 差异

service:定义长期运行的服务进程(最常用,ExecStart 运行应用);socket:定义监听 socket(如 TCP/Unix socket),配合 service 实现 socket 激活(按需启动服务);target:逻辑分组/同步点(如 multi-user.target、graphical.target),把多个 unit 组织在一起代表一种系统状态/启动级别;timer:定义定时触发(类似 cron),配合 service 实现定时任务(timer 触发 service)。差异:service 是运行服务,socket 是套接字监听(socket 激活),target 是分组/目标,timer 是定时器。用途:跑服务用 service,按需连接用 socket(socket activation),组织启动关系用 target,定时任务用 timer(替代 cron)。它们可组合:socket 激活 service、timer 触发 service、target 聚合 service。

核心是"各类型职责"。service 运行服务、socket 监听激活、target 分组、timer 定时,可组合使用。

systemctl list-units --type=service
systemctl list-units --type=target
systemctl list-units --type=timer
systemctl list-units --type=socket
#
★★★

7. systemd 中的 slice、scope、cgroup 关系

systemd 中的 slice、scope、cgroup 关系是什么?

  • slice/scope/service
  • cgroup 层级
  • 关系

systemd 把进程组织进 cgroup 树,unit 类型对应 cgroup 层级:service(服务)、scope(外部进程组,如用户会话)、slice(cgroup 容器/分组,如 system.slice、user.slice)。关系:cgroup 是资源控制(CPU/内存/IO)的层级树;slice 是 cgroup 的"容器"(分组),scope 是"外部进程组"(临时集合,如 ssh 会话),service 是"服务进程组"。三层结构:systemd 的 cgroup 树顶层是 slice(.slice),下面挂 service/scope。例如 user@1000.service 在 user.slice 下,各服务在 system.slice 下。cgroup 层级决定资源配额/继承(slice 的限额作用于其下所有 unit)。资源控制:CPUQuota、MemoryMax 等配置在 slice/unit 上,作用于该 cgroup 的进程组。tune 用 systemctl set-property 调整。

核心是"cgroup 层级 + 三类 unit"。slice 分组、scope 外部进程组、service 服务进程组,都在 cgroup 树中,资源配额按层级继承。

systemctl status app.service
systemd-cgls
systemctl cat user.slice
cat /sys/fs/cgroup/system.slice/cgroup.procs
#
★★★

8. systemd 的 CPUQuota、MemoryHigh、MemoryMax 在资源控制上的语义

systemd 的 CPUQuota、MemoryHigh、MemoryMax 在资源控制上的语义是什么?

  • CPUQuota 与 CPUWeight
  • MemoryHigh/MemoryMax
  • 资源控制语义

CPUQuota= 限制 CPU 使用百分比(如 CPUQuota=200% 表示最多 2 核,CPUQuota=50% 表示半个核),是硬性上限;MemoryMax= 硬性内存上限(超过则 OOM 杀死进程),MemoryHigh= 软性内存上限(超过则尽力回收,不立即杀死,压力警告)。语义:CPUQuota 是 CPU 硬上限;MemoryHigh 是软限制(压力下回收,常触发 swap),MemoryMax 是硬限制(超过 OOM)。对比 CPUWeight 是相对权重(按比例分配,非绝对上限)。运维:用 CPUQuota 限制 CPU 占用,MemoryMax 限制内存防 OOM,MemoryHigh 作为内存压力预警(配合软回收)。注意 MemoryMax 设太小会 OOM 杀服务,需合理设置;MemoryHigh 只提示压力。资源控制作用于 cgroup。

核心是"硬/软限制"。CPUQuota 硬性 CPU 上限,MemoryHigh 软性内存上限(压力回收),MemoryMax 硬性内存上限(OOM)。

[Service]
CPUQuota=200%
MemoryHigh=1G
MemoryMax=2G
#
★★★

9. systemd 的 Environment 与 EnvironmentFile 在配置注入的差异

systemd 的 Environment 与 EnvironmentFile 在配置注入的差异是什么?

  • Environment 直接定义
  • EnvironmentFile 引用文件
  • 差异

Environment= 在 unit 文件内直接定义环境变量(Environment=FOO=bar),权限可控在 unit 内,不便于动态修改;EnvironmentFile= 从外部文件读取环境变量(EnvironmentFile=/etc/app.env),配置文件分离,便于运维修改、多环境复用、密钥/配置管理。差异:Environment 内联、静态、随 unit 文件;EnvironmentFile 外部文件、可独立修改、便于 CI/环境切换。工程实践:敏感/可变配置用 EnvironmentFile(便于管理、可版本化),常量和静态用 Environment。注意 EnvironmentFile 文件格式(每行 KEY=VALUE,可省略前缀),权限需收紧(若含密钥)。systemd 还支持 EnvironmentFile 的 - 前缀(忽略缺失)。选型:配置注入用 EnvironmentFile(解耦),简单变量用 Environment。

核心是"内联 vs 外部文件"。Environment 内联静态,EnvironmentFile 外部文件可独立修改,便于解耦与配置管理。

[Service]
Environment=APP_ENV=prod
EnvironmentFile=-/etc/app.env
#
★★★

10. systemd 的 FailureAction 与 OnFailure 在失败自愈的差异

systemd 的 FailureAction 与 OnFailure 在失败自愈上的差异是什么?

  • FailureAction 系统级动作
  • OnFailure 触发单元
  • 自愈差异

OnFailure= 指定当 unit 失败时启动的另一个 unit(如 OnFailure=notify-failure.service),用于通知/记录/执行补救,是"失败时触发"的自愈/告警机制;FailureAction= 在 unit 失败时执行系统级动作(如 reboot、poweroff、exit),是"失败时系统级响应"(如关键服务失败重启系统)。差异:OnFailure 是"启动另一个 unit 做处理"(通知、告警、补救),FailureAction 是"执行系统动作"(重启/关机),力度不同。使用场景:OnFailure 用于服务失败时发告警/通知/拉起依赖;FailureAction 用于关键进程失败需重启系统(或容器内 exit)。二者可配合:OnFailure 通知,FailureAction 重启。自愈设计:服务失败用 Restart/OnFailure,关键失败用 FailureAction 重启节点。

核心是"触发补救 vs 系统动作"。OnFailure 启动另一个 unit 处理,FailureAction 执行系统级动作(reboot 等)。

[Unit]
OnFailure=notify-on-failure.service
FailureAction=reboot
#
★★★

11. systemd 的 PrivateTmp、PrivateDevices、PrivateNetwork 的隔离边界

systemd 的 PrivateTmp、PrivateDevices、PrivateNetwork 的隔离边界是什么?

  • 三个隔离选项
  • 隔离边界
  • 应用

PrivateTmp=yes 给服务独立的 /tmp 与 /var/tmp(私有命名空间),服务看不到系统公共 /tmp,防止其他进程/服务窥探或干扰该服务的临时文件,也防止服务污染公共 /tmp。PrivateDevices=yes 给服务独立的 /dev(copy 只读设备节点,隐藏 /dev/mem 等敏感设备),服务无法访问敏感设备,减小攻击面。PrivateNetwork=yes 给服务独立的网络命名空间(只有 loopback),服务无法访问外部网络,用于隔离网络(如不需要网络的服务防外联)。隔离边界:三者分别隔离"临时目录、设备、网络",都基于命名空间,实现服务间隔离与最小权限。应用:安全要求高的服务(如解析器、解压服务)加 PrivateTmp/PrivateNetwork;需设备访问的服务不能开 PrivateDevices。注意这些隔离会改变服务可见环境,需验证服务兼容。

核心是"三类命名空间隔离"。PrivateTmp 隔离临时目录、PrivateDevices 隔离设备、PrivateNetwork 隔离网络,实现最小权限。

[Service]
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
#
★★★

12. systemd 的 ProtectSystem、ProtectHome 在加固容器化进程的边界

systemd 的 ProtectSystem、ProtectHome 在加固容器化进程的边界是什么?

  • ProtectSystem 只读系统
  • ProtectHome 隔离家目录
  • 加固边界

ProtectSystem=yes 使 /usr、/boot、/etc 只读(- 时 /usr、/boot 只读,strict 时整个文件系统只读,除 /home 等),ProtectHome=yes 使 /home、/root、/run/user 不可访问(或只读),用于加固服务进程环境。边界:ProtectSystem 让系统关键目录只读,防止服务篡改系统文件;ProtectHome 隐藏服务对家目录的访问,防止服务读取用户家目录(敏感数据)。两者配合提供"只读系统 + 隔离家目录"的加固,类似容器的只读 rootfs。注意:服务需要写 /etc、/var 时 ProtectSystem=strict 会阻断,需配合 ReadWritePaths 允许特定写路径。加固边界:隔离文件系统访问,但不停 seccomp/namespace 网络进程隔离;需结合其他加固。应用:高安全服务加 ProtectSystem=strict + ProtectHome=yes + ReadWritePaths。

核心是"只读系统 + 隔离家目录"。ProtectSystem 只读系统目录,ProtectHome 隔离家目录,配合 ReadWritePaths 开放写路径。

[Service]
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/app
#
★★★

13. systemd 的 Restart 与 RestartSec 的反直觉组合

systemd 的 Restart 与 RestartSec 有什么反直觉的组合?如何正确使用?

  • Restart 策略
  • RestartSec 间隔
  • 反直觉点

Restart= 指定服务退出后的重启策略(always、on-failure、on-abnormal、on-success 等),RestartSec= 指定重启前的等待秒数。反直觉点:一是 Restart=always 在服务被 systemctl stop 时不会重启(stop 是主动操作,不触发 Restart),被 kill 或崩溃才重启;二是 RestartSec=0 时立即重启,可能造成快速重启风暴(crash loop)且无退避;三是 on-failure 只对非零退出码/信号重启,exit 0 不重启;四是 Restart 与 StartLimitBurst/StartLimitIntervalSec 组合——默认 StartLimit 限制重启次数,超过后停止重启(可能表现为"服务反复失败不再拉起")。正确使用:崩溃用 Restart=on-failure + RestartSec 设置退避(如 5s),快速失败用 StartLimitBurst 限制防止风暴;了解 Stop 不触发 Restart。反直觉点:always 不等于 stop 也重启,RestartSec 需配合限流。

核心是"Restart 触发条件 + 退避/限流"。stop 不触发 Restart,RestartSec 设退避,StartLimit 限重启次数防风暴。

[Service]
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=60
StartLimitBurst=5
#
★★★

14. systemd 的 SystemCallFilter 与 seccomp 的关系

systemd 的 SystemCallFilter 与 seccomp 的关系是什么?

  • SystemCallFilter 过滤系统调用
  • seccomp 机制
  • 关系

SystemCallFilter= 是 systemd 通过 seccomp 机制为服务设置系统调用过滤(白名单/黑名单),限制服务可调用的系统调用(如禁止 exec、mount、reboot 等危险 syscall),实现沙箱加固。seccomp 是 Linux 内核的安全机制,允许进程进入受限模式,只允许指定的系统调用(seccomp filter 白名单/黑名单)。关系:systemd 的 SystemCallFilter 是 seccomp 的上层封装(systemd 生成 seccomp filter 应用到服务 cgroup),SystemCallFilter=@system-service 等内置集合,也支持自定义 syscall 列表;SystemCallErrorNumber= 指定被过滤调用返回的错误。边界:SystemCallFilter 只过滤系统调用,不与 namespace/权限隔离等价;需配合 NoNewPrivileges、ProtectSystem 等。用途:加固服务进程,限制危险 syscall 减少攻击面。

核心是"封装关系"。SystemCallFilter 是 systemd 层的 seccomp 封装,过滤系统调用,需配合 NoNewPrivileges 等加固。

[Service]
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
NoNewPrivileges=yes
#
★★★

15. systemd 的 Type=notify 与 Type=simple 的差异

systemd 的 Type=notify 与 Type=simple 的差异是什么?

  • Type=simple 的启动语义
  • Type=notify 的 sd_notify
  • 差异

Type=simple 是默认:systemd 认为 ExecStart 启动的进程即主进程,立即认为服务已启动(不等待就绪),配合 RemainAfterExit 使用;适合简单服务,但无法感知"就绪"(可能依赖就绪延迟)。Type=notify 服务启动后通过 sd_notify(systemd 通知机制)发送 READY=1 通知 systemd"已就绪",systemd 等待该通知才认为服务启动完成;适合需要就绪确认的服务(如数据库、Web 服务),避免后续依赖过早启动。差异:simple 立即视为启动、不等待就绪;notify 等待服务主动通知就绪。notify 需要服务用 sd_notify 或告警库(如 systemd 通知),且需 WatchdogSec 配合。选型:无需就绪信号用 simple,需要就绪确认用 notify(配合 Type=notify)。注意 notify 服务若未发通知会超时。

核心是"是否等待就绪通知"。simple 立即启动、notify 等 sd_notify READY,需就绪确认用 notify。

[Service]
Type=notify
ExecStart=/usr/bin/app
# 服务端需调 sd_notify(0, "READY=1")
#
★★★

16. systemd 的 journal 在日志持久化与非持久化的差异

systemd 的 journal 在日志持久化与非持久化的差异是什么?

  • journal 持久化配置
  • 非持久化(内存)
  • 差异

journal 默认把日志存在 /run/log/journal(内存,非持久化,重启丢失),除非 /var/log/journal 存在(持久化)。持久化:把日志写入 /var/log/journal(磁盘),重启后日志保留,可设置 Storage=persistent(/etc/systemd/journald.conf);非持久化:日志在内存,重启丢失,适合无需留存日志的系统。差异:持久化保留日志(可审计、排障),但占磁盘、需管理大小(SystemMaxUse/vacuum);非持久化省磁盘但重启丢失。运维:生产环境应持久化(Storage=persistent),并配置 SystemMaxUse 限制磁盘占用、journalctl --vacuum-size 清理。转向持久化:mkdir /var/log/journal 并重启 journald。审计/合规要求日志持久化留存。

核心是"日志是否落盘"。持久化存 /var/log/journal 重启保留,非持久化存内存重启丢失,生产应持久化并限容。

# /etc/systemd/journald.conf
Storage=persistent
SystemMaxUse=1G
systemctl restart systemd-journald
journalctl --vacuum-size=500M
#
★★★

17. 僵尸进程为何无法被 kill -9 清除,需如何处理

僵尸进程为何无法被 kill -9 清除?如何处理?

  • 僵尸进程本质
  • 为何 kill -9 无效
  • 处理方式

僵尸进程(Z)是子进程已终止但父进程未调用 wait() 回收其退出状态,其 PCB 保留在进程表中等待父进程读取。因此僵尸进程已"死",无法被 kill 信号操作(kill -9 只能作用于存活进程,对僵尸无效)。清理方式:一是让父进程调用 wait() 回收(父进程正常执行并回收子进程);二是向父进程发送信号(SIGCHLD/SIGTERM)促使其回收,或重启/杀死父进程后由 init/systemd 收养并回收;三是父进程退出后,僵尸被 init 收养并立即回收。若父进程反复创建僵尸(如程序 bug 不回收),需修复程序或重启父进程。僵尸进程本身不占 CPU/内存(只占 PID 表一项),但 PID 被占、过多可能耗尽 PID 或影响监测。处理:定位父进程(ps -o ppid),让父进程回收或重启父进程。

核心是"僵尸已死、需父进程 wait 回收"。kill -9 对已死进程无效,靠父进程 wait 或父进程退出后 init 回收。

ps -eo stat,pid,ppid,comm | awk '$1~/Z/'
# 让父进程回收或重启父进程
kill -TERM <ppid>
#
★★

18. Linux 中 nohup、disown、setsid 让进程脱离终端的差异

Linux 中 nohup、disown、setsid 让进程脱离终端的差异是什么?

  • nohup 忽略挂断信号
  • disown 从 shell 作业移除
  • setsid 新会话

nohup 让进程忽略 SIGHUP(挂断信号),即使终端关闭进程也不被 SIGHUP 杀掉,输出重定向到 nohup.out 或指定文件;disown 是 shell 内建,把作业从 shell 的作业表移除,使终端关闭时 shell 不发 SIGHUP 给该作业(但进程可能仍属于该会话);setsid 让进程创建一个新会话(setsid 命令或程序内 setsid()),完全脱离原会话/进程组/控制终端,成为独立会话领导,不受终端挂断影响。差异:nohup 是"忽略 SIGHUP",disown 是"从 shell 作业移除",setsid 是"新会话脱离终端"。三者都让进程在终端关闭后继续运行,但机制不同:nohup 忽略信号、disown 移除作业、setsid 独立会话。生产上长期后台进程推荐 systemd 服务或 setsid/nohup + 日志重定向。注意 nohup 不防 SIGHUP 外的其他信号,disown 只对 shell 作业有效。

核心是"防挂断机制不同"。nohup 忽略 SIGHUP、disown 移出作业表、setsid 新会话脱离终端。

nohup cmd > out.log 2>&1 &
jobs
disown %1
setsid cmd &
#
★★

19. journalctl --vacuum-size 与 --vacuum-time 在容量控制上的取舍

journalctl --vacuum-size 与 --vacuum-time 在容量控制上的取舍是什么?

  • vacuum-size 按大小清理
  • vacuum-time 按时间清理
  • 取舍

journalctl --vacuum-size=500M 清理 journal 使其总大小不超过指定值(删除最旧日志,保留最新);--vacuum-time=7d 清理超过指定时间的日志(删除 7 天前的)。取舍:--vacuum-size 按磁盘容量控制(保证不超容量上限,适合磁盘紧张),--vacuum-time 按留存时间控制(保证日志保留指定时长,适合合规/审计要求留存期限)。选型:磁盘紧张用 vacuum-size,合规留存用 vacuum-time,可组合(先按 size 再按 time)。也可用 journald.conf 的 SystemMaxUse/SystemMaxFileSize 自动限容,配合 MaxRetentionSec 限时间。运维:定期 vacuum 或用配置自动限容,避免磁盘满与日志无限增长。注意 vacuum 是手动命令,长期用 journald.conf 自动策略。

核心是"按容量 vs 按时间"。vacuum-size 控容量、vacuum-time 控留存时长,按场景选,长期用 journald.conf 自动限容。

journalctl --vacuum-size=500M
journalctl --vacuum-time=7d
# journald.conf
SystemMaxUse=1G
MaxRetentionSec=30d
#
★★

20. journald 的 ForwardToSyslog/MaxRetentionSec 与 rsyslog 转发链路的配置要点

journald 的 ForwardToSyslog/MaxRetentionSec 与 rsyslog 转发链路的配置要点是什么?

  • journald 转发 syslog
  • MaxRetentionSec 留存
  • rsyslog 链路

journald 的 ForwardToSyslog=yes 把 journal 日志转发到 syslog(rsyslog),使日志进入传统 syslog 体系(/var/log/messages 等);MaxRetentionSec 限制 journal 日志留存时间。rsyslog 转发链路:journald 转发到 rsyslog,rsyslog 再按规则写文件或转发远端(集中日志)。配置要点:一是 journald.conf 设 ForwardToSyslog=yes、MaxRetentionSec 控制留存;二是 rsyslog 配置接收 journald 日志、按 facility 写文件、转发远端(. @server:514);三是避免重复——journald 转发 rsyslog 可能造成日志双写(journal + syslog),需规划;三是配置转发远端实现集中审计,本地留存受限。链路:应用 → journald → rsyslog → 文件/远端。注意 ForwardToSyslog 与 Storage 配合,避免日志丢失。

核心是"journald→rsyslog 转发链路"。ForwardToSyslog 转发到 rsyslog,MaxRetentionSec 控留存,rsyslog 写文件/转发远端,注意避免双写。

# /etc/systemd/journald.conf
ForwardToSyslog=yes
MaxRetentionSec=30d
# /etc/rsyslog.conf
*.* @@logserver.example.com:514
#
★★

21. systemd unit 中如何配置 AppArmorProfile 并验证其生效?

systemd unit 中如何配置 AppArmorProfile 并验证其生效?

  • AppArmorProfile 配置
  • 验证生效
  • 限制

在 systemd unit 的 [Service] 段配置 AppArmorProfile=profilename 指定服务使用某个 AppArmor 配置文件(如 AppArmorProfile=docker-default),服务启动时内核加载该 profile 限制服务的能力。验证生效:一是 aa-status 查看服务进程是否在 profile 下(enforced/complain);二是 cat /proc/<pid>/attr/current 查看进程当前 AppArmor profile;三是 aa-status 确认 profile 加载与模式(enforced)。注意:AppArmorProfile 需系统已加载 AppArmor 且 profile 存在(/etc/apparmor.d/),使用前需 apparmor_parser -a /etc/apparmor.d/profile 加载;若 profile 不存在,服务可能启动失败或忽略。验证:启动服务后 aa-status 看对应 profile 的进程数,或 cat /proc/<pid>/attr/current。限制:AppArmor 是 LSM,需内核启用。

核心是"AppArmorProfile 指定 + 验证"。配置在 [Service] 段,用 aa-status /proc/pid/attr/current 验证加载与模式。

[Service]
AppArmorProfile=app-profile
# 验证
aa-status
cat /proc/<pid>/attr/current
#
★★

22. systemd 的 CPUWeight 与 CPUQuota 的差异

systemd 的 CPUWeight 与 CPUQuota 的差异是什么?

  • CPUWeight 相对权重
  • CPUQuota 绝对上限
  • 差异

CPUWeight= 是 CPU 相对权重(cpu.weight,cgroup v2),按权重在所有竞争的 cgroup 间按比例分配 CPU,不设绝对上限(空闲时可用更多);CPUQuota= 是 CPU 绝对上限(cpu.max quota),限制最多使用的 CPU 百分比(如 200% 最多 2 核),超限则受限制。差异:CPUWeight 是"软性相对分配"(按比例、无固定上限),CPUQuota 是"硬性绝对限制"(固定上限)。选型:需要"按比例公平分配"(多服务共享 CPU)用 CPUWeight;需要"限制某服务最大 CPU 占用"用 CPUQuota。可组合:CPUQuota 设上限,CPUWeight 设比例。运维:防某服务占满 CPU 用 CPUQuota;需要多服务按重要性分配用 CPUWeight。注意 cgroup v1/v2 语法差异。

核心是"相对权重 vs 绝对上限"。CPUWeight 按比例分配无上限,CPUQuota 固定上限,按需选型可组合。

[Service]
CPUWeight=100
CPUQuota=200%
#
★★

23. systemd 的 ImportCredential、LoadCredential 如何为服务注入密钥?

systemd 的 ImportCredential、LoadCredential 如何为服务注入密钥?

  • LoadCredential 加载密钥
  • ImportCredential 导入
  • 密钥注入

systemd 的凭据机制(Credentials)为服务安全注入密钥/配置。LoadCredential= 从文件或目录加载凭据(如 LoadCredential=db_password:/etc/cred/db_pass),文件内容挂载到服务只读的 /run/credentials/ / 下,服务可读取;ImportCredential= 从 systemd 的凭据存储(如 systemd-import-credentials 从网络/本地导入,或从之前注入的凭据继承)导入。作用:把密钥/令牌以只读凭据注入服务,避免明文写在命令行/环境变量(易被 ps 泄露)。使用:服务通过读取 /run/credentials/ / 获取密钥;LoadCredential 从文件加载,ImportCredential 从凭据存储导入。边界:凭据挂载为只读(0600),root 可读、服务用户可读指定;比环境变量更安全。systemd 还支持 systemd-creds 加密/解密凭据。

核心是"只读凭据注入密钥"。LoadCredential 从文件加载,ImportCredential 从存储导入,挂载到 /run/credentials 只读,避免明文泄露。

[Service]
LoadCredential=db_password:/etc/cred/db_pass
# 服务内读取 /run/credentials/<unit>/db_password
#
★★

24. systemd 的 MemoryDenyWriteExecute 如何实现 W^X?

systemd 的 MemoryDenyWriteExecute 如何实现 W^X?

  • W^X 概念
  • MemoryDenyWriteExecute
  • 实现

W^X(Write XOR Execute)原则:内存要么可写(W)要么可执行(X),不能同时可写且可执行,防止攻击者把代码注入可写内存再执行(如 JIT 注入、shellcode)。systemd 的 MemoryDenyWriteExecute=yes 通过 seccomp 过滤 mmap/mprotect 等系统调用,禁止进程创建"可写且可执行"的内存映射,从而强制 W^X。实现:systemd 生成 seccomp filter,拦截 mmap/mprotect/mremap 等调用,若请求 W+X 权限则拒绝(返回 EPERM)。边界:适用于不依赖 JIT 动态生成可执行代码的服务;对需要 JIT 的服务(如某些 VM、浏览器)会破坏功能,需评估。配合 NoNewPrivileges、SystemCallFilter 增强加固。验证:服务启动后可用 status 检查,或确认无 W+X 映射。MemoryDenyWriteExecute 是纵深防御的一环。

核心是"W^X 防注入"。MemoryDenyWriteExecute 用 seccomp 拦截 mmap/mprotect 阻止 W+X 内存映射,但破坏 JIT 应用。

[Service]
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
#
★★

25. systemd 的 ReadOnlyPaths、InaccessiblePaths 如何实现只读挂载?

systemd 的 ReadOnlyPaths、InaccessiblePaths 如何实现只读挂载?

  • ReadOnlyPaths 只读
  • InaccessiblePaths 不可访问
  • 实现

ReadOnlyPaths= 指定服务对这些路径挂载为只读(绑定挂载只读),服务无法修改这些路径下的文件(如 ReadOnlyPaths=/etc),实现保护;InaccessiblePaths= 指定服务对这些路径完全不可访问(绑定挂载为空/隐藏),服务无法看到(如 InaccessiblePaths=/home),实现隔离。实现:systemd 通过绑定挂载(bind mount)重新挂载路径为只读或空目录,私有命名空间内生效。差异:ReadOnlyPaths 只读(可读不可写),InaccessiblePaths 不可见(完全隐藏)。配合 ProtectSystem=strict 等实现只读/隔离加固。注意:ReadOnlyPaths 对服务写需求的路径需排除(用 ReadWritePaths 覆盖);InaccessiblePaths 后服务无法访问该路径。应用:保护敏感配置用 ReadOnlyPaths,隐藏敏感目录用 InaccessiblePaths。

核心是"只读 vs 隐藏"。ReadOnlyPaths 绑定挂载只读,InaccessiblePaths 隐藏路径,实现文件系统加固。

[Service]
ReadOnlyPaths=/etc/app
InaccessiblePaths=/home
#
★★

26. systemd 的 RuntimeDirectory、StateDirectory、CacheDirectory 在状态隔离上的价值

systemd 的 RuntimeDirectory、StateDirectory、CacheDirectory 在状态隔离上的价值是什么?

  • 三个目录类型
  • 状态隔离
  • 价值

systemd 为服务自动创建并管理目录(设属主/权限、可配合 tmpfs):RuntimeDirectory= 创建 /run/ (运行时数据,重启即清,/run 是 tmpfs);StateDirectory= 创建 /var/lib/ (持久状态数据,重启保留);CacheDirectory= 创建 /var/cache/(缓存数据,可清理)。价值:状态隔离——服务数据分开放置(运行时/持久/缓存),权限由 systemd 管理(服务属主),避免服务写错位置、权限混乱;利于清理(cache 可清、run 重启清)、备份(state 持久)、安全(目录属主正确)。边界:目录由 systemd 创建并设属主(服务用户),服务无需手动 mkdir;配合 StateDirectory 的持久化与 RuntimeDirectory 的临时性管理。工程:用这些目录而非服务自己 mkdir,实现规范化的状态管理。

核心是"规范化状态目录"。RuntimeDirectory 临时、StateDirectory 持久、CacheDirectory 缓存,systemd 自动创建管理权限,实现状态隔离。

[Service]
RuntimeDirectory=app
StateDirectory=app
CacheDirectory=app
#
★★

27. systemd 的 WatchdogSec 如何实现服务看门狗健康检查?

systemd 的 WatchdogSec 如何实现服务看门狗健康检查?

  • WatchdogSec 看门狗
  • sd_notify 喂狗
  • 健康检查

WatchdogSec= 设置看门狗超时(如 WatchdogSec=30s),服务需定期通过 sd_notify 发送 WATCHDOG=1 通知 systemd"存活"(喂狗);若在超时时间内未收到通知,systemd 认为服务无响应,按 Restart 策略重启服务。实现:服务内调用 sd_notify(0, "WATCHDOG=1") 周期性喂狗;systemd 监控超时。配合 Type=notify(服务需通知机制)。边界:WatchdogSec 只对实现了 sd_notify 喂狗的服务生效(简单服务不喂狗会超时重启);是"服务存活"健康检查,非应用功能检查。价值:检测服务"假死"(进程活着但无响应/卡死),及时重启,比普通 Restart(仅崩溃)更能发现卡死。注意:配置 WatchdogSec 需服务支持喂狗,否则会误杀。

核心是"喂狗防假死"。WatchdogSec 设超时,服务周期性 sd_notify WATCHDOG=1 喂狗,超时未喂则重启,检测卡死。

[Service]
Type=notify
WatchdogSec=30s
Restart=on-failure
# 服务内:periodic sd_notify(0, "WATCHDOG=1")
#
★★

28. systemd 的 path unit 与 timer unit 在自动化触发上的差异

systemd 的 path unit 与 timer unit 在自动化触发上的差异是什么?

  • path unit 文件/目录触发
  • timer unit 时间触发
  • 差异

path unit(.path)监控文件/目录变化(文件创建、修改、删除、目录变化),当事件发生时触发关联的 service(PathExists、PathChanged、PathModified 等),实现"事件驱动"(如新文件到达处理);timer unit(.timer)按时间计划触发(OnCalendar 定时、OnBootSec 启动后、OnUnitActiveSec 周期),类似 cron,实现"时间驱动"(如定时备份)。差异:path 是"事件触发"(文件变化),timer 是"时间触发"(定时计划)。使用:需要文件变化即处理用 path unit,需要定时任务用 timer unit(替代 cron)。可组合:timer 按时间、path 按事件。边界:path unit 监控需在支持的文件系统;timer 用 OnCalendar 精确调度。二者都触发关联 service。

核心是"事件触发 vs 时间触发"。path unit 按文件变化触发,timer unit 按时间计划触发,分别替代事件驱动与 cron。

# file.path
[Unit]
Description=Watch incoming
[Path]
PathChanged=/data/incoming
[Timer]
OnCalendar=*-*-* 02:00:00
#
★★

29. systemd 的 resolved 如何实现 DNS 解析?

systemd 的 resolved 如何实现 DNS 解析?

  • systemd-resolved 架构
  • 存根解析器
  • DNS 缓存与转发

systemd-resolved(.service)是 systemd 的 DNS 解析器,提供 /run/systemd/resolve/stub-resolv.conf(存根解析器 127.0.0.53),通过 D-Bus 与 systemd-networkd、应用交互。实现:resolved 管理 DNS 服务器(从网络配置、DHCP 获取)、维护 DNS 缓存(缓存解析结果)、按域路由(split DNS,不同域用不同 DNS)、支持 DoT/DoH(加密 DNS)、与 NSS 集成(nss-resolve)。应用通过 127.0.0.53 存根解析器把查询转发给 resolved,resolved 缓存并向上游 DNS 转发。边界:依赖 systemd-resolved 运行与文化 /etc/resolv.conf 指向;resolved 是可选服务,未启用时用传统 resolv.conf。运维:检查 resolved 状态(resolvectl status)、DNS 服务器(resolvectl dns)、缓存(resolvectl statistics),配置 split DNS 用 resolvectl 或 /etc/systemd/resolved.conf。

核心是"systemd 的 DNS 解析器"。resolved 提供存根解析器、缓存、按域路由、加密 DNS,resolvectl 管理。

systemctl status systemd-resolved
resolvectl status
resolvectl dns
resolvectl statistics
#
★★

30. systemd 的 sysusers.d 如何实现启动期创建系统用户?

systemd 的 sysusers.d 如何实现启动期创建系统用户?

  • sysusers.d 机制
  • 启动期创建用户
  • 配置

systemd 的 sysusers.d 机制(/etc/sysusers.d/、/usr/lib/sysusers.d/)在启动早期(systemd-sysusers 服务)根据配置文件创建系统用户/组。配置格式:u username uid "description" home shellg groupnamem username group(加入组)等。systemd-sysusers 在启动时读取这些文件,创建缺失的用户/组(不覆盖已有),用于为服务提供专用账号。实现:启动时 systemd-sysusers.service 运行,按 sysusers.d 配置创建用户/组,保证服务启动前账号存在。价值:声明式管理系统账号,随包/镜像部署(打包时提供 sysusers.d 文件),无需手动 useradd。边界:只在用户不存在时创建;配置在 /etc/sysusers.d 可覆盖。运维:自定义服务用 sysusers.d 声明账号,替代 useradd 脚本。验证:systemd-sysusers 执行后 getent passwd 查看。

核心是"声明式创建账号"。sysusers.d 在启动期由 systemd-sysusers 创建用户/组,声明式管理,无需手动 useradd。

# /etc/sysusers.d/app.conf
u app - "App user" /var/lib/app /usr/sbin/nologin
g app
m app app
# 立即执行
systemd-sysusers
#
★★

31. systemd 的单元重启策略 on-failure 与 always 的差异

systemd 的单元重启策略 on-failure 与 always 的差异是什么?

  • on-failure 与 always
  • 触发条件
  • 选型

Restart=always 只要服务退出(无论退出码)就重启(除非被 systemctl stop 主动停止);Restart=on-failure 只在服务"失败"时重启(非零退出码、被信号终止、超时等),正常退出(exit 0)不重启。差异:触发条件不同——always 无条件重启(除主动 stop),on-failure 仅失败时重启。选型:需要"无论怎样都保持运行"的常驻服务用 always(如守护进程);需要"正常退出不重启、失败才重启"用 on-failure(如一次性/状态退出服务)。注意:always 与 on-failure 都受 StartLimit 限制(防重启风暴);on-abnormal 只在异常(信号/超时)时重启。实践:绝大多数常驻服务用 on-failure(避免正常退出被拉起),少数用 always。on-failure 更优(避免误重启正常退出)。

核心是"触发条件"。always 无条件重启(除 stop),on-failure 仅失败重启,常驻服务常用 on-failure 避免误重启。

[Service]
Restart=on-failure
RestartSec=5
#
★★

32. 孤儿进程由 PID 1(systemd)收养的机制,以及 systemd 对子进程回收(Reaper)责任与僵尸进程清理的关系

孤儿进程由 PID 1(systemd)收养的机制是什么?systemd 对子进程回收(Reaper)责任与僵尸进程清理的关系是什么?

  • 孤儿进程收养
  • systemd 的 Reaper 责任
  • 僵尸清理

当父进程退出,其子进程成为孤儿,由 PID 1(init/systemd)收养:子进程的 PPID 变为 1。systemd 作为 PID 1 收养所有孤儿进程,并负责回收(reap)它们——当收养的子进程退出成僵尸时,systemd 调用 wait() 回收其退出状态,清理僵尸。systemd 的 Reaper 责任:systemd 是通用 subreaper(可以配置 Subreaper=yes),不仅收养孤儿,还负责回收孤儿产生的僵尸进程,防止僵尸堆积。关系:孤儿进程被 systemd 收养后,若其退出,systemd 负责 reaping(wait 回收),因此正常不会长期残留僵尸;但若 systemd 自己未及时回收或子进程未退出,僵尸可能短暂存在。僵尸清理与 systemd:systemd 回收僵尸依赖其 wait 循环;若僵尸的父进程是普通进程(非孤儿),则需该父进程回收,systemd 不介入。因此 systemd 的 Reaper 责任范围是"收养的孤儿"。

核心是"孤儿被 PID 1 收养 + systemd 负责回收"。孤儿 PPID 变 1,systemd 作为 subreaper 负责 wait 回收其僵尸,但父进程存活的僵尸需父进程回收。

# 查看孤儿 PPID=1
ps -eo pid,ppid,stat,comm | awk '$2==1'
# systemd 回收策略
[Service]
Subreaper=yes
#

33. systemd unit 中 SELinuxContext 如何为服务配置 SELinux 上下文?

systemd unit 中 SELinuxContext 如何为服务配置 SELinux 上下文?

  • SELinuxContext 配置
  • SELinux 上下文
  • 应用

SELinuxContext= 在 systemd unit 的 [Service] 段指定服务使用哪个 SELinux 上下文(如 SELinuxContext=system_u:system_r:sshd_t:s0),服务的进程将以此标签运行,SELinux 按该标签做访问控制。用途:覆盖默认 SELinux 标签,为服务指定特定的类型(type)以匹配其权限需求(如需要更高安全域的服务)。配置:SELinuxContext=system_u:system_r:myservice_t:s0;需标签存在且符合策略。验证:ps -eZ(查看进程 SELinux 标签)、getenforce(查看模式)、seinfo -t 查看类型。注意:SELinuxContext 需 SELinux 启用(enforcing)且配置正确,否则服务可能启动失败或权限异常;标签与策略不匹配会导致拒绝访问。通常用默认标签(由策略自动),特殊需求才显式指定。边界:SELinuxContext 覆盖进程标签,需确保策略允许。

核心是"指定服务 SELinux 标签"。SELinuxContext 设置服务进程的 SELinux 上下文,用 ps -eZ 验证,需策略匹配。

[Service]
SELinuxContext=system_u:system_r:app_t:s0
# 验证
ps -eZ | grep app
getenforce
#

34. systemd 的 TasksMax 如何限制服务的最大任务数?

systemd 的 TasksMax 如何限制服务的最大任务数?

  • TasksMax 限制线程/进程
  • 与 pids cgroup
  • 配置

TasksMax= 限制服务(cgroup)内允许的最大任务数(进程+线程数),通过 pids cgroup(cgroup v2 的 pids.max)实现。当服务 fork 的进程/线程总数超过 TasksMax 时,fork 返回 EAGAIN 失败,防止服务进程/线程无限制增长(如线程泄漏)耗尽系统资源。配置:TasksMax=1000(也可用百分比/后缀)。默认有系统默认值(如 4915 或按配置)。边界:TasksMax 是 cgroup 级的任务数上限,独立于进程 fd/内存限制;超限后新 fork 失败,已有进程不受影响。运维:监控服务任务数(systemctl status 显示 Tasks),结合 TasksMax 防线程泄漏;排查 EAGAIN 时检查是否超 TasksMax。systemd 的 DefaultTasksMax 定义全局默认。验证:systemctl status app 看 Tasks 数,cat /sys/fs/cgroup/.../pids.max

核心是"pids cgroup 限制任务数"。TasksMax 通过 pids.max 限制服务进程/线程总数,超限 fork 失败,防线程泄漏。

[Service]
TasksMax=1000
# 查看
systemctl status app
cat /sys/fs/cgroup/system.slice/app.service/pids.max
#

35. systemd 的 UserNamespace 如何实现非 root 容器的用户映射?

systemd 的 UserNamespace 如何实现非 root 容器的用户映射?

  • UserNamespace 用户命名空间
  • 非 root 容器
  • 用户映射

systemd 的 UserNamespace=true 在 unit 中启用用户命名空间(或用 PrivateUsers=yes),为服务创建独立的用户命名空间,把容器内 UID/GID 映射到宿主的不同 UID 范围(如容器内 root 映射到宿主非 root UID),实现"非 root 容器"——容器内进程看起来是 root,但宿主侧是无特权用户,无法影响宿主其他资源。实现:通过用户命名空间(user namespace)的 uid_map/gid_map 映射,容器内 root(UID 0)映射到宿主普通 UID,容器内其他 UID 也映射。边界:需内核支持用户命名空间(userns)且允许非特权创建;容器内 root 只在自己命名空间内有权,无法提权到宿主。用途:启用非 root 容器(rootless),提升安全性,减少 root 权限。配合 systemd 的 AmbientCapabilities、NoNewPrivileges 增强。验证:容器内 id 显示 root,宿主看进程 UID 是被映射的非 root。

核心是"用户命名空间映射"。UserNamespace 把容器内 UID 映射到宿主非 root,实现非 root 容器,提升安全。

[Service]
UserNamespace=true
# 或 PrivateUsers=yes
# 容器内 id 显示 root,宿主是非 root UID