systemd的启动流程

2026-01-06
15916 分钟
...

一、systemd的启动流程

当Linux系统启动时,systemd作为第一个用户空间进程(PID 1)被内核启动,其初始化过程如下:

  1. 内核移交控制权

    • 内核完成硬件初始化后,执行/sbin/init(指向systemd的符号链接)
    • systemd以PID 1身份启动,继承内核创建的初始环境
  2. 基础环境准备

    • 挂载基础虚拟文件系统:/proc, /sys, /dev
    • 设置基本cgroups层次结构
    • 应用系统时钟和区域设置
  3. 单元加载与依赖解析

    • 扫描所有单元文件路径(/usr/lib/systemd/system/, /etc/systemd/system/等)
    • 构建完整的依赖关系图
    • 确定启动顺序和并行启动可能性
  4. 启动阶段(target)执行

    graph LR
    A[Basic.target] --> B[sysinit.target]
    B --> C[sockets.target]
    B --> D[paths.target]
    B --> E[timers.target]
    C --> F[systemd-journald.socket]
    C --> G[dbus.socket]
    B --> H[multi-user.target]
    H --> I[graphical.target]
  5. 最终目标激活

    • 根据配置启动multi-user.target(命令行模式)或graphical.target(图形界面)
    • 启动getty进程提供登录提示
    • 完成系统启动,进入运行状态

二、systemd服务单元的创建

1. 单元文件结构与位置

systemd服务单元定义在配置文件中,有严格的目录层次:

  • 系统默认单元/usr/lib/systemd/system/(软件包安装)
  • 系统管理员自定义/etc/systemd/system/(优先级最高)
  • 运行时生成/run/systemd/system/(临时,重启后消失)

2. 创建一个基本服务单元

以创建一个简单的Web应用服务为例:

# 1. 创建服务文件
sudo nano /etc/systemd/system/mywebapp.service
[Unit]
Description=My Web Application Server
Documentation=https://example.com/docs
After=network.target nginx.service
Requires=nginx.service

[Service]
Type=simple
User=webapp
Group=webapp
WorkingDirectory=/opt/mywebapp
ExecStart=/usr/bin/python3 /opt/mywebapp/app.py --port=8000
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
Restart=on-failure
RestartSec=5s
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
Environment="PYTHONPATH=/opt/mywebapp/lib"

[Install]
WantedBy=multi-user.target
# 3. 重载systemd配置
sudo systemctl daemon-reload

# 4. 启用并启动服务
sudo systemctl enable --now mywebapp.service

3. 服务类型详解

不同服务类型影响systemd如何判断服务状态:

  • simple(默认):执行ExecStart后立即视为激活

    [Service]
    Type=simple
    ExecStart=/usr/sbin/nginx -g "daemon off;"
  • forking:服务会fork,父进程退出后才视为激活

    [Service]
    Type=forking
    ExecStart=/usr/sbin/apache2ctl start
    PIDFile=/var/run/apache2.pid
  • oneshot:一次性执行,完成后服务处于非活动状态

    [Service]
    Type=oneshot
    ExecStart=/usr/bin/migration_tool --apply
    RemainAfterExit=yes
  • notify:服务通过sd_notify()通知systemd准备就绪

    [Service]
    Type=notify
    ExecStart=/usr/bin/my_daemon --daemonize=no

三、systemd服务的启动机制

1. 依赖关系与启动顺序

systemd通过多种指令控制服务间的依赖关系:

[Unit]
Description=Database Service
# 强依赖若mysql失败则本服务不会启动
Requires=mysql.service
# 弱依赖仅在mysql存在时才需要它
Wants=mysql.service
# 在网络服务启动后启动本服务
After=network.target
# 在本服务启动前必须启动的服务
Before=webapp.service
# 冲突关系不能同时运行
Conflicts=old-database.service

2. 启动过程的生命周期

一个systemd服务的启动过程包含以下阶段:

  1. 准备阶段

    • 解析单元文件
    • 验证依赖关系
    • 分配cgroups和资源
  2. 执行阶段

    • 设置用户/组权限
    • 配置环境变量
    • 设置工作目录
    • 应用资源限制(如MemoryLimit, CPUQuota)
    • 执行ExecStartPre命令(如果有)
  3. 主进程启动

    • 执行ExecStart命令
    • 根据服务类型等待服务准备就绪
  4. 监控与维护

    • 持续监控主进程状态
    • 根据Restart策略处理失败
    • 记录日志到journald

3. 按需启动机制(Socket Activation)

systemd支持通过监听套接字按需启动服务,延迟资源使用直到实际需要:

# /etc/systemd/system/myapp.socket
[Unit]
Description=MyApp Socket

[Socket]
ListenStream=127.0.0.1:8000
Accept=yes  # 每个连接创建一个实例

[Install]
WantedBy=sockets.target
# /etc/systemd/system/myapp@.service
[Unit]
Description=MyApp Service
After=network.target

[Service]
ExecStart=/usr/bin/myapp-handler
StandardInput=socket
StandardError=journal

启用此配置后:

  1. systemd监听8000端口
  2. 当第一个连接到达时,systemd启动myapp@.service实例
  3. 将连接的socket传递给服务进程
  4. 服务处理请求,完成后可配置自动退出

四、系统启动优化

systemd提供多种机制优化启动过程:

1. 并行启动

  • 服务可以并行启动,而不是传统的串行启动
  • 依赖树被分析,无依赖关系的服务同时启动
  • 通过systemd-analyze critical-chain识别启动瓶颈

2. 启动时间分析

# 分析总体启动时间
systemd-analyze

# 按启动时间排序服务
systemd-analyze blame

# 查看关键链最长依赖链
systemd-analyze critical-chain

# 生成启动过程可视化图表
systemd-analyze plot > boot-process.svg

3. 启动延迟配置

[Unit]
Description=Delayed Service
# 延迟启动避免资源竞争
After=network.target
# 确保网络完全就绪
# Wants=network-online.target
# After=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/my_service
# 在执行前延迟3秒
ExecStartPre=/bin/sleep 3

五、调试systemd服务启动问题

当服务无法正常启动时,使用以下方法诊断:

# 1. 检查服务状态
systemctl status myservice.service

# 2. 查看详细日志
journalctl -u myservice.service --since "5 minutes ago"

# 3. 详细调试模式
sudo systemctl stop myservice.service
sudo systemd-run --unit=debug-myservice --service-type=exec \
  -p Environment=SYSTEMD_LOG_LEVEL=debug \
  -p StandardOutput=journal \
  -p StandardError=journal \
  -- /usr/bin/my_service --debug

# 4. 检查单元文件语法
systemd-analyze verify /etc/systemd/system/myservice.service

# 5. 跟踪实时启动过程
journalctl -f -u myservice.service

六、实战:创建一个完整的systemd服务

以下是一个生产级Nginx缓存清理服务示例:

[Unit]
Description=Nginx Cache Purge Service
Documentation=https://example.com/nginx-cache-docs
After=network.target nginx.service
Requires=nginx.service

[Service]
Type=oneshot
User=www-data
Group=www-data
# 清理24小时前的缓存
ExecStart=/usr/bin/find /var/cache/nginx -type f -mtime +1 -delete
# 重新加载Nginx配置
ExecStartPost=/usr/sbin/nginx -s reload
# 资源限制
MemoryLimit=100M
Nice=10
IOSchedulingClass=idle

[Install]
WantedBy=multi-user.target

配合定时器:

[Unit]
Description=Daily Nginx Cache Purge Timer

[Timer]
OnCalendar=*-*-* 03:00:00  # 每天凌晨3点
Persistent=true
Unit=nginx-cache-purge.service

[Install]
WantedBy=timers.target

启用服务:

sudo systemctl daemon-reload
sudo systemctl enable --now nginx-cache-purge.timer

总结

systemd通过单元文件定义服务,通过依赖解析实现并行启动,通过套接字激活实现按需启动,形成了一个高效、灵活的系统初始化和服务管理体系。理解systemd的启动机制和服务创建方法,是掌握现代Linux系统管理的关键。通过合理设计单元文件,可以实现复杂的服务依赖、资源限制和自动恢复策略,大幅提升系统的可靠性和性能。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
Linux2025-07-17
Ubuntu 22.04下Docker安装
Ubuntu22.04下Docker的安装 为什么要安装Docker,在这里就不用多说了,在我的另一篇文章里有告诉大家原因。大家可以去看看 下面是具体的步骤: 准备条件 准备安装 安装Docker 安装Docker 命令补全工具 允许非Root用户执行docker 命令 当我们安装好了Docker之后,有两种方式来执行docker 命令 在docker命令前加上sudo, 比如:sudo docker ps sudo i 切换至root...
学习
Linux2025-06-12
Ubuntu 安装Redis
更新系统包 在安装 Redis 之前,先更新系统的包列表: 安装 Redis 运行以下命令安装 Redis 服务器: 启动 Redis 服务 安装完成后,Redis 服务会自动启动。如果没有启动,可以手动启动: 设置 Redis 开机自启动 确保 Redis 在系统启动时自动启动: 测试 Redis 使用 Redis 客户端连接到 Redis 服务器并测试: 在 Redis CLI 中运行以下命令测试: 如果返回 ,说明 Redis 正...
学习
Linux2024-07-12
ssh登陆报错“IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!“问题原因及解决方法
最近给 重装完固件连接时发现ssh报错,如下 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! 警告:远程主机标识已更改! 这个报错主要是因为远程主机的ssh公钥发生了变化,两边不一致导致的 删除本地对应ip的在known hosts相关信息 删除之后重新连接,成功!
学习
Linux2024-07-12
Ubuntu 一站式 Zsh 和 Oh My Zsh 安装配置指南
本文介绍了如何在Ubuntu系统上安装和配置zsh和oh my zsh,包括基本安装步骤、配置插件等操作。提高终端的使用效率和定制性。 步骤一:安装 Zsh 安装Zsh前,需要更新Ubuntu的包列表: 接下来,安装Zsh 验证安装: 步骤二: 安装 oh my zsh 使用一行命令安装 也可以手动脚本安装 步骤三: 装插件 安装插件 安装自动补全插件和语法高亮插件以提高命令行的效率和易用性。 启用插件 编辑 文件以启用下载的插件。 在...
学习
Linux2024-07-12
在Ubuntu 22.04 LTS 上安装 MySQL两种方式
Ubuntu安装MySQL 介绍: 是一款基于 的免费开源发行版,广受欢迎。它以稳定性、安全性和用户友好性而闻名,适用于桌面和服务器环境。Ubuntu提供了大量的软件包和应用程序,拥有庞大的社区支持和活跃的开发者社区。它的长期支持(LTS)版本获得5年的安全更新,为企业和个人提供了可靠的选择。 Ubuntu的桌面版本具有直观的用户界面,而服务器版本则适用于构建强大的Web服务器和云计算平台。 当你需要 时,有两种主要的方式:在线安装和离...
学习
Linux2024-06-19
Ubuntu查看文件夹大小
在 中,你可以使用 命令来查看文件夹的大小。 命令的全称是"disk usage",用于估计和显示目录或文件的磁盘使用空间。 如果你想查看某个特定文件夹的大小,你可以使用以下命令: 在这个命令中, 选项表示"summary",它将只给出总计的大小,而不是每个子目录的大小。 选项表示"human readable",它将以易于阅读的格式(例如KB,MB,GB)显示大小。 例如,如果你想查看名为"Documents"的文件夹的大小,你可以运...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录