使用 launchd/launchctl 管理 MacOS 服务

launchd

launchd is an init and operating system service management daemon created by Apple Inc. as part of macOS to replace its BSD-style init and SystemStarter. There have been efforts to port launchd to FreeBSD and derived systems.

维基百科 所说,launchd 是 MacOS 上所使用的一套服务管理框架。它作为系统启动的第一个进程,系统的其它进程均由它直接或间接创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  hunterx.xyz git:(master) ✗ ps -ef | grep launchd | grep -v grep
0 1 0 0 25 623 ?? 272:23.76 /sbin/launchd
213 546 1 0 25 623 ?? 4:06.61 /System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/Resources/usbmuxd -launchd
0 567 1 0 25 623 ?? 26:32.81 /usr/libexec/corebrightnessd --launchd
0 582 1 0 25 623 ?? 0:13.89 /System/Library/CoreServices/backupd.bundle/Contents/Resources/backupd-helper -launchd
501 1345 1 0 25 623 ?? 0:14.19 /System/Library/PrivateFrameworks/AMPDevices.framework/Versions/A/Support/AMPDeviceDiscoveryAgent --launchd
501 1653 1 0 25 623 ?? 3:26.36 /System/Library/CoreServices/Siri.app/Contents/MacOS/Siri launchd
501 1658 1 0 25 623 ?? 0:24.43 /System/Library/CoreServices/AirPlayUIAgent.app/Contents/MacOS/AirPlayUIAgent --launchd
501 48141 1 0 9:05上午 ?? 0:12.19 /System/Library/PrivateFrameworks/AMPDevices.framework/Versions/A/Support/AMPDevicesAgent --launchd
0 60348 1 0 18 823 ?? 0:00.08 /usr/libexec/xpcroleaccountd -launchd
0 61549 1 0 18 823 ?? 0:02.47 /usr/libexec/biometrickitd --launchd
501 72715 1 0 19 823 ?? 0:04.52 /System/Library/PrivateFrameworks/AMPLibrary.framework/Versions/A/Support/AMPLibraryAgent --launchd
501 72843 1 0 19 823 ?? 0:00.15 /System/Library/PrivateFrameworks/AMPLibrary.framework/Versions/A/Support/AMPArtworkAgent --launchd

在 Linux 系统下,有一个与之对应的进程为 systemd,它的对应管理工具为 systemctl,launchd 的管理工具就是下文所要介绍的 launchctl。

plist 文件

launchd 对一个服务的管理行为需要记录在以 .plist 为后缀的 xml 文件中,通常我们将这类文件称为 plist 文件。plist 的定义语法可以参考 文档,以下是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd >
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.exampled</string>
<key>ProgramArguments</key>
<array>
<string>exampled</string>
</array>
<key>KeepAlive</key>
<true/>
</dict>
</plist>

不同类型的 plist 文件存放的目录和运行用户有所不同:

类型 路径 说明
User Agents ~/Library/LaunchAgents 为当前登录用户启动
Global Agents /Library/LaunchAgents 为当前登录用户启动
Global Daemons /Library/LaunchDaemons root 或者通过 UserName 配置指定的用户
System Agents /System/Library/LaunchAgents 当前登录用户
System Daemons /System/Library/LaunchDaemons root 或者通过 UserName 配置指定的用户

launchctl 使用

使用 launchctl 可以非常方便地对查询/添加/删除服务,以下是我经常使用到的一些命令:

1
2
3
4
5
6
7
8
9
10
11
# 列出当前加载的服务
launchctl list

# 卸载某一个服务
launchctl unload xyz.hunterx.plist

# 加载某一个服务
launchctl load xyz.hunterx.plist

# 无视 Disabled 配置值,加载并启动某一个服务
launchctl load -w xyz.hunterx.plist

注意事项

  1. 如果一个服务之前已经被加载,使用 launchctl load 命令时会报错,可以尝试先对服务进行卸载:

    1
    launchctl list | grep xyz.hunterx.plist && launchctl unload xyz.hunterx.plist
  2. 如果启动的服务需要一些环境变量(如用户登录后自动 source 引入的变量),需要在配置中使用 EnvironmentVariables 定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
    http://www.apple.com/DTDs/PropertyList-1.0.dtd >
    <plist version="1.0">
    <dict>
    <key>Label</key>
    <string>com.example.exampled</string>
    <key>ProgramArguments</key>
    <array>
    <string>exampled</string>
    </array>
    <key>KeepAlive</key>
    <true/>
    <key>EnvironmentVariables</key>
    <dict>
    <key>PYTHON_PATH</key>
    <key>/usr/bin/python</key>
    </dict>
    </dict>
    </plist>

参考资料

  1. 使用 launchctl 管理 MacOS 服务
  2. macOS服务管理 – launchd、launchctl、brew services详解
  3. launchd wikipedia
  4. launchd.plist