Skip to content

macOS Launchd 入门

Launchd 是 macOS 的系统服务管理工具(相当于 Windows 服务和 Linux systemd),它允许用户创建、启动、停止和配置 macOS 系统服务。

Launchd 的基本概念

launchd 分为两个部分:launchdlaunchctl

launchd 是常驻后台的守护进程,负责读取各种 *.plist 配置进行启动、停止、重启服务等操作,是真正干活的力工。

launchctl 是用来管理 launchd 的操作台,负责向 launchd 发送指令,属于下发命令的管理层。

Launchd 配置文件

Launchd 配置是一个 XML 格式的文本文件:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">
<dict>
    <!-- 必填:全局唯一 ID,通常反域名 -->
    <key>Label</key>
    <string>com.example.hello</string>

    <!-- 必填:启动命令与参数 -->
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/you/scripts/hello.sh</string>
    </array>

    <!-- 可选:指定启动命令程序的工作目录 -->
    <key>WorkingDirectory</key>
    <string>/Users/you</string>

    <!-- 可选:开机/登录时立即运行一次 -->
    <key>RunAtLoad</key>
    <true/>

    <!-- 可选:每 300 秒执行一次 -->
    <key>StartInterval</key>
    <integer>300</integer>

    <!-- 可选:每天 09:30 执行一次 -->
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>9</integer>
        <key>Minute</key>
        <integer>30</integer>
    </dict>

    <!-- 可选:保持存活;true 时退出后会被拉起 -->
    <key>KeepAlive</key>
    <true/>

    <!-- 可选:常规日志输出 -->
    <key>StandardOutPath</key>
    <string>/tmp/com.example.hello.out.log</string>

    <!-- 可选:异常日志输出 -->
    <key>StandardErrorPath</key>
    <string>/tmp/com.example.hello.err.log</string>
</dict>
</plist>

Launchd 配置文件必须以 .plist 作为后缀名,文件名通常与配置中的 Label 一致。

Launchd 配置文件通常放在以下目录中,放置目录会决定自动加载行为、运行权限和启动时机:

目录运行权限启动时机适用场景
/Library/LaunchDaemons/root系统启动时需要高权限的系统级服务,如网络守护进程、硬件监控、系统备份等
/Library/LaunchAgents/登录用户所有用户登录时对所有用户可用的服务,如全局快捷键、多用户应用助手等
~/Library/LaunchAgents/当前用户当前用户登录时最常用,仅当前用户需要的服务,如个人定时任务、应用通知、同步工具等

以上目录会被系统在启动/登录流程中自动扫描并加载。

除了等待登录时自动加载外,也可以手动 bootstrap 指定任意可访问路径的 plist

使用 bootstrap 启动服务只是临时加载到目标域,当域的生命周期结束后丢失,下次系统启动/登录时只会自动加载配置目录中未被 disable 的 plist 配置。

常用管理命令

配置校验

bash
# 校验 plist 语法是否正确(推荐先执行)
plutil -lint ~/Library/LaunchAgents/com.example.hello.plist

# 查看 plist 内容(格式化输出)
plutil -p ~/Library/LaunchAgents/com.example.hello.plist

服务加载与卸载

bash
# 加载并激活服务
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.hello.plist

# 移除服务(与 bootstrap 相对)
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.example.hello.plist

参数说明:

  • $(id -u):获取当前用户 ID

服务状态查看

bash
# 列出所有已加载的服务
launchctl list

# 查找特定服务
launchctl list | grep com.example.hello

# 查看详细状态(推荐)
launchctl print gui/$(id -u)/com.example.hello

# 查看服务是否正在运行
ps aux | grep -i "hello.sh"

状态字段说明:

PID    Status   Label
56789  0        com.example.hello
#      ↑        ↑
#      │        └─ 服务标识符
#      └─ 退出代码(0 表示正常)

服务控制

bash
# 重启服务(一键操作)
launchctl kickstart gui/$(id -u)/com.example.hello

# 强制停止服务(不卸载)
launchctl kill SIGTERM gui/$(id -u)/com.example.hello

# 立即触发一次运行(即使不在定时时间)
launchctl kickstart --immediate gui/$(id -u)/com.example.hello

常用信号:

信号说明使用场景
SIGTERM正常终止优先尝试
SIGKILL强制杀死无响应时使用
SIGHUP重新加载配置支持热重载的服务

系统级服务(需要 sudo)

bash
# 加载系统级服务
sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.daemon.plist

# 卸载系统级服务
sudo launchctl bootout system /Library/LaunchDaemons/com.example.daemon.plist

# 查看系统服务状态
sudo launchctl print system/com.example.daemon