设计守护进程和服务

后台进程类型

在 macOS 中有四种类型的后台进程。不同之处在下表中进行了总结,并在下面的小节中进行了详细描述。要选择适当类型的后台进程,请考虑以下事项:

  • 它是针对当前登录的用户还是针对所有用户执行某些操作。
  • 它是由单个应用程序使用还是由多个应用程序使用。
  • 它是否需要显示用户界面或启动GUI应用程序。
类型 由 launchd 管理? 在哪种环境下运行 可以显示 UI 吗
Login item 用户
XPC service 用户 否(除非以非常有限的方式使用IOSurface)
Launch Daemon 系统
Launch Agent 用户 不建议

Login items

登录项在用户登录时启动,并继续运行,直到用户注销或手动退出。它们的主要用途是允许用户自动打开常用的应用程序,但也可以由应用程序开发人员使用。例如,登录项可用于额外显示菜单或注册全局热键。

例如,许多待办事项应用程序使用登录项来侦听全局热键,并提供允许用户输入新任务的最小 UI。登录项还通常用于显示用户界面项,例如浮动时钟或计时器,或者用于在菜单栏中显示图标。

另一个示例是带有作为登录启动项的助手应用程序的日历应用程序。助手应用程序在后台运行,并在适当的时候启动主GUI应用程序,以提醒用户即将到来的事件。

login items 有两种添加方式:

使用 Service Management 安装的登录项目在系统首选项中不可见,只能由安装它们的应用程序删除。

使用共享文件列表安装的登录项目在系统首选项中可见;用户可以直接控制它们。如果您使用此API,则用户可以禁用您的登录项,因此在禁用登录项的情况下,与其通信的任何其他应用程序都应该具有合理的后备行为。

使用 Service Management 添加登录项

应用程序可以包含一个助手应用程序作为完整的应用程序包,存储在 Contents/Library/LoginItems 目录下的主应用程序包中。在帮助器应用程序捆绑包的 Info.plist 文件中设置 LSUIElementLSBackround Only 键。

使用 SMLoginItemSetEnabled 函数(在OSXv10.6.6和更高版本中提供)启用助手应用程序。它有两个参数,一个是包含助手应用程序的捆绑包标识符的 CFStringRef ,另一个是指定所需状态的布尔值。传递 true 可立即启动帮助器应用程序,并指示每次用户登录时都应启动该应用程序。传递 false 以终止帮助器应用程序,并指示在用户登录时不应再启动该应用程序。如果请求的更改已生效,则此函数返回 TRUE ;否则返回 FALSE 。此函数可用于管理任意数量的助手应用程序。

使用 Launch Services 添加登录项

此方法在OSXv10.5及更高版本中可用。有关具体详细信息,请参阅 Launch Services Reference

XPC Services

XPC 服务由 Launchd 管理,并向单个应用程序提供服务。它们通常用于将应用程序划分为更小的部分。这可用于通过限制进程崩溃时的影响来提高可靠性,并通过限制进程受损时的影响来提高安全性。

对于传统的单可执行应用程序,如果应用程序的任何部分崩溃,整个应用程序都会终止。通过将应用程序重组为主进程和服务,服务崩溃的影响明显较小。用户可以继续工作;崩溃的服务将重新启动。例如,电子邮件应用程序可以使用 XPC 服务来处理与邮件服务器的通信。即使服务崩溃,暂时中断了与服务器的通信,应用程序的剩余部分仍可用。

沙箱允许指定程序在正常操作期间预期要做的事情。操作系统会强制执行该事件列表,从而限制攻击者可能造成的破坏。例如,文本编辑器可能需要编辑磁盘上已由用户打开的文件,但它可能不需要打开其他位置的任意文件或通过网络进行通信。

您可以将沙箱与 XPC 服务相结合,通过将复杂的应用程序、工具或守护进程拆分成具有定义良好的功能的较小部分来提供权限分离。由于降低了每个单独部分的权限,任何缺陷都不太容易被攻击者利用:没有一个部分以用户的全部功能运行。例如,组织和编辑照片的应用程序通常不需要网络访问。然而,如果它还允许用户将照片上传到照片共享网站,则可以将该功能实现为具有网络访问和对文件系统的中介访问(或无访问)的 XPC 服务。

Launch Daemons

守护程序由 launchd 在系统上下文中代表操作系统进行管理,这意味着它们不知道登录到系统的用户。守护进程不能直接启动与用户进程的联系;它只能响应用户进程发出的请求。因为它们不了解用户,所以守护程序也无法访问 window server,因此无法发布可视界面或启动 GUI 应用程序。守护进程严格意义上是响应低级别请求的后台进程。

大多数守护进程在系统的系统上下文中运行,也即是说,它们在系统的最低级别运行,并使其服务对所有用户会话可用。即使没有用户登录到系统,此级别的守护进程也会继续运行,因此守护进程程序应该不会直接了解用户。相反,守护进程必须等待用户程序与其联系并发出请求。作为该请求的一部分,用户程序通常告诉守护进程如何返回任何结果。

Launch Agents

代理由 launchd 管理,但代表当前登录的用户(即,在用户上下文中)运行。代理可以与同一用户会话中的其他进程通信,也可以与系统上下文中的系统范围后台进程通信。它们可以显示可视界面,但不推荐这样做。

如果您的代码同时提供特定于用户和独立于用户的服务,则可能需要同时创建守护进程和代理。您的守护进程将在系统上下文中运行,并提供独立于用户的服务,而代理的实例将在每个用户会话中运行。代理将与守护进程协调,为每个用户提供服务。