Daemons 和 Service 编程引导
有许多应用都不需要用户交互而是在后台运行, 即守护进程(daemon), 例如可以使用 daemon 进行:
- 提供服务器功能, 比如 web 服务器
- 提供共享资源的访问, 比如数据库
- 为前台应用提供服务, 比如文件系统访问
Daemon 和 Service 都是通过 launchd
启动, 有两种上下文:
- Startup Session
- Login Session
可以通过本文的内容理解如何开发 daemon, 同时为系统管理员提供管理启动项的知识.
本文中的 service
表示为 GUI 应用提供支持的后台进程, daemon
表示除了 service 外的其他后台进程(特别是没有任何用户界面的那种).
关于 Daemons 和 Agents 的解释, 详见这篇文档.
后台进程的类型
有四种类型的后台进程:
- Login Item: 由每个用户单独的 launchd 实例启动, 但 launchd 不会对这些 item 进行管理. 这些 item 会被 launchd 在用户登录时启动, 在退出登录时或手动退出时退出. 特别是菜单栏 APP 或一些需要在后台持续观察的 APP 会使用到 Login Item.
- XPC service: 由 launchd 管理, 并为单个的 APP 提供服务. 主要用于将一个 APP 分割为多个小的部分. 另外还可以用来实现 priviledged helper.
- Launch Daemons: 由 launchd 管理, 在 system 上下文中代表系统, 并非在用户登录时启动. 这些 daemon 不会主动和用户进程通信, 而是由用户进程向它们发起请求. 因此不会拥有可视化界面或主动打开某个 GUI 应用. 绝大多数 Daemon 都运行在system 上下文, 为所有用户的应用提供服务.
- Launch Agents: 由 launchd 管理, 并代表某个已登录的用户运行(在用户上下文中运行). Agents 可以和其他的用户登录会话中的进程通信, 或和系统范围内的 daemon 通信. 它们可以有可视化界面, 但不推荐实现可视化界面.
其中, Daemon 可以通过四种方式和它们的客户通信: XPC, 本地的 C/S 通信(包括 Apple Events, TCP/IP, UDP, 以及其他的 socket 机制), RPC(Mach RPC, Sun RPC, 分布式对象), 内存映射(比如 CG API 使用这样的方式).
XPC 是一种最简单的启动和与 daemon 沟通的方式.
可以通过 Activity Monitor 查看当前运行的 Daemon.
Daemon 生命期
macOS 的 root 进程是 launchd, 负责初始化系统, 同时负责按顺序启动 system daemons. 另外 launchd 可以按需加载 daemon, 如果 daemon 在启动后一段时间没有活动, 可以被关闭, 而后在服务请求到达后, launchd 会查看 daemon 是否启动, 如果没有, 则将 daemon 启动起来然后处理这个服务请求.
添加 Login Items
有两种方式添加 Login Items: 利用 Service Framework, 或使用 shared file list.
其中使用 Service Management Framework 安装的 Login Items 不会在系统设置中展示出来, 只能通过安装它们的 APP 移除.
通过 share file list 安装的 login item 可以在 system preference 中看到.
使用 Service Management Framework 添加 Login Item
这些 item 对应的 APP 一般是放到主 APP 包中的 Contents/Library/LoginItems
目录, 并根据情况设置 item 对应 info.plist 中 LSUIElement
(比如菜单栏 APP) or LSBackgroundOnly
(仅后台) 为真.
使用 SMLoginItemSetEnabled
调用来打开这些 APP 的自启动, 包含两个参数, 第一个参数 CFStringRef
是对应 item 的 BundleID, 第二个参数是设置它的状态, 传入 true
表示允许自启动, 且调用后直接启动这个 item APP. 而传入 false 表示关闭自启动, 并且调用后会直接结束这个 item APP. 这个函数调用的返回值是当操作成功 则返回 true, 否则返回 false.
自启动的含义是每次当用户登录后都自启动.
另外当有多个包都包含相同的 BundleID 的 Login Item, 只有最大的 bundle version 的会启动, 但如果两个 bundle version 相同的情况下, 启动的就不确定了.
使用 Shared File List 添加 Login Item
可以使用 Launch Services 中的相关 API 添加 Login Item.