macOS系统的启动项


=Start=

缘由:

简单整理一下macOS系统开机启动的相关知识,方便以后有需要的时候参考。

正文:

参考解答:
为什么要关心macOS系统的启动项?
  • 为了方便使用,我们希望某些软件在开机(登录)后自动启动,比如 mysql 服务;
  • 为了减少资源占用以及出于安全考虑,还有一些默认开机(登录)后启动的软件我们不想让它自动启动,很多不常用的软件以及我们没想到的程序。
launchd 是什么?
Wikipedia defines launchd as "a unified, open-source service management framework for starting, stopping and managing daemons, applications, processes, and scripts. Written and designed by Dave Zarzycki at Apple, it was introduced with Mac OS X Tiger and is licensed under the Apache License."

根据上面的描述可以认为launchd是一套统一的开源服务管理框架,它用于启动、停止以及管理后台程序、应用程序、进程和脚本。它是由Apple开发的,并在 Mac OS X Tiger 操作系统上第一次被引入,并基于 Apache License 进行授权。

launchd 是macOS系统上第一个启动的进程,该进程(/sbin/launchd)的PID为1,整个系统的其他进程都是它创建的。当launchd启动后,它会扫描 /System/Library/LaunchDaemons 和 /Library/LaunchDaemons 中的plist文件并加载他们;当输入密码登录系统后,launchd会扫描 /System/Library/LaunchdAgents、/Library/LaunchAgents、~/Library/LaunchAgents 这三个目录中的plist文件并加载它们。

LaunchAgents 和 LaunchDaemons 的异同

守护进程(Daemon)是在后台运行的程序,不需要用户输入。例如,典型的守护进程一般用于执行日常维护任务或在有设备连入时进行恶意软件扫描。(A daemon is a program running in the background without requiring user input. A typical daemon might for instance perform daily maintenance tasks or scan a device for malware when it is connected.)

一句话描述就是——对于launchd来说,Agent和Daemon的主要区别在于代理(Agent)是以当前登录用户的权限运行的,而守护进程(Daemon)是以root或通过UserName键指定的用户权限运行的。

当电脑开机,macOS系统启动后,守护进程(Daemon)就会启动;而代理(Agent)只有在用户输入密码,登录系统的图形化界面后才会启动。

不同位置的 LaunchAgents 和 LaunchDaemons 的异同:

  • ~/Library/LaunchAgents #用户agents,以当前登录用户的身份运行
  • /Library/LaunchAgents #全局agents,以当前登录用户的身份运行
  • /Library/LaunchDaemons #全局daemons,以root或指定用户的身份运行
  • /System/Library/LaunchAgents #【不要自己擅自改动的】系统agents,以当前登录用户的身份运行
  • /System/Library/LaunchDaemons #【不要自己擅自改动的】系统daemons,以root或指定用户的身份运行
plist文件简单说明

launchd 是通过以“.plist”后缀结尾的xml⽂件来指定⼀个 agent/daemon 的行为,我们⼀般称它为plist⽂件。根据 plist文件 存放位置的不同,我们会分别将它们视为 agent 或 daemon 。

每个plist文件都是一个任务,加载不代表立即运行,只有设置了 RunAtLoad 为true或 keepAlive 为true时,才会加载并同时启动这些任务。

下面以brew提供的 mysql.plist 文件为例学习一下「.plist文件」的编写方法:

$ cat ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist
<?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>
  <key>KeepAlive</key> //这个key表明你的daemon是按需启动还是需要一直运行
  <true/>
  <key>Label</key> //必须要有的key,下面的<string>为key对应的值,需要是唯一可辨识的
  <string>homebrew.mxcl.mysql</string>
  <key>ProgramArguments</key> //必须要有的key,下面的<array>为命令的路径和启动所需要的选项
  <array>
    <string>/usr/local/opt/mysql/bin/mysqld_safe</string>
    <string>--bind-address=127.0.0.1</string>
    <string>--datadir=/usr/local/var/mysql</string>
  </array>
  <key>RunAtLoad</key> //在被加载时运行
  <true/>
  <key>WorkingDirectory</key> //官方文档推荐使用 WorkingDirectory 这个key来指定工作目录
  <string>/usr/local/var/mysql</string>
</dict>
</plist>

根据文档来看,一个 plist文件 至少有 3 个关键的key需要指定:

  • Label #理论上在daemon或agent范围内部需要分别唯一可识别,但建议全局唯一
  • Program/ProgramArguments #指定要运行的程序和启动所需要的选项,建议直接用 ProgramArguments 就行
  • RunAtLoad #它决定程序是否在plist文件被加载时就运行

还有 1 个对于需要长期运行的daemon来说很关键的key是:

  • KeepAlive #它表明你的daemon是按需启动还是需要一直运行,为true则表示一直运行

其它的就很简单了,照着已有示例改改就能用。

加载 和 启动 plist文件 的区别

加载一个plist文件并不一定意味着启动plist文件中指定的程序。程序何时启动由plist文件中的设置决定。事实上,只有当指定了 RunAtLoad 或 KeepAlive 时,launchd才会在加载plist文件后无条件地启动指定的程序。(Loading a job definition does not necessarily mean to start the job. When a job is started is determined by the job definition. In fact, only when RunAtLoad or KeepAlive have been specified,launchd will start the job unconditionally when it has been loaded.)

launchctl 的使用

列出所有由launchd管理的进程

launchctl list
launchctl list | grep 'keyword'

手动加载某个 .plist 文件:

launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist
# -w选项的作用是,如果该服务被禁用了,则会在加载的同时设置为启用
launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist

加载/卸载/启动/停止/移除/启用/停用/……

launchctl load|unload|start|stop|remove|enable|disable|... /path/to/name.plist

检查某个 .plist 文件的语法/格式是否正确:

plutil -lint ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist
# launchctl help
# launchctl load --help


Usage: launchctl <subcommand> ... | help [subcommand]
Many subcommands take a target specifier that refers to a domain or service
within that domain. The available specifier forms are:

system/[service-name]
Targets the system-wide domain or service within. Root privileges are required
to make modifications.

user/<uid>/[service-name]
Targets the user domain or service within. A process running as the target user
may make modifications. Root may modify any user's domain. User domains do not
exist on iOS.

gui/<uid>/[service-name]
Targets the GUI domain or service within. Each GUI domain is associated with a
user domain, and a process running as the owner of that user domain may make
modifications. Root may modify any GUI domain. GUI domains do not exist on iOS.

session/<asid>/[service-name]
Targets a session domain or service within. A process running within the target
security audit session may make modifications. Root may modify any session
domain.

pid/<pid>/[service-name]
Targets a process domain or service within. Only the process which owns the
domain may modify it. Even root may not do so.

When using a legacy subcommand which manipulates a domain, the target domain is
inferred from the current execution context. When run as root (whether it is
via a root shell or sudo(1)), the target domain is assumed to be the
system-wide domain. When run from a normal user's shell, the target is assumed
to be the per-user domain for that current user.

Subcommands:
...    
    enable          Enables an existing service.
    disable         Disables an existing service.
    kickstart       Forces an existing service to start.
...    
    kill            Sends a signal to the service instance.
...    
    runstats        Prints performance statistics for a service.
...    
    load            Recommended alternatives: bootstrap | enable. Bootstraps a service or directory of services.
    unload          Recommended alternatives: bootout | disable. Unloads a service or directory of services.
    remove          Unloads the specified service name.
    list            Lists information about services.
    start           Starts the specified service.
    stop            Stops the specified service if it is running.
...    
参考链接:

Mac上有些软件无法禁止开机启动怎么办?
https://www.zhihu.com/question/28268529

MacOS:Launchd&LaunchDaemon&LaunchAgent&.plist文件编写
https://blog.csdn.net/dddgggd/article/details/122599616

What is launchd?
https://launchd.info/

Mac服务管理 – launchd、launchctl、LaunchAgent、LaunchDaemon、brew services详解
https://www.xiebruce.top/983.html

了解LaunchDaemons
https://afoo.me/posts/2014-12-12-understanding-launch-daemons-of-macosx.html

【知识】管理 macOS 的启动项
https://zhuanlan.zhihu.com/p/118129638

macOS开机启动项设置
https://blog.csdn.net/qq_27198345/article/details/108544720

苹果全新版本macOS Ventura操作系统 macOS Ventura新功能
https://baijiahao.baidu.com/s?id=1735773440804215272&wfr=spider&for=pc

What are the differences between LaunchAgents and LaunchDaemons?
https://apple.stackexchange.com/questions/290945/what-are-the-differences-between-launchagents-and-launchdaemons

https://ss64.com/osx/launchctl.html

macOS: Know the difference between launch agents and daemons, and use them to automate processes
https://www.techrepublic.com/article/macos-know-the-difference-between-launch-agents-and-daemons-and-use-them-to-automate-processes/

How to Catch and Remove Hidden LaunchDaemons and LaunchAgents on Mac
https://www.makeuseof.com/tag/hidden-launchdaemons-launchagents-mac/

Creating Launch Daemons and Agents
https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html

=END=


《 “macOS系统的启动项” 》 有 3 条评论

  1. # 在macOS上尝试关闭向日葵的自启动
    `
    # 查看非Apple的启动项
    $ launchctl list | grep -v com.apple.
    # 现在推荐用bootstrap | bootout | enable | disable来替代load 和 unload
    $ man launchctl

    load | unload [-wF] [-S sessiontype] [-D searchpath] paths …
    Recommended alternative subcommands: bootstrap | bootout | enable | disable

    $ sudo launchctl unload -w /Library/LaunchAgents/com.oray.sunlogin.agent.plist
    Warning: Expecting a LaunchDaemons path since the command was ran as root. Got LaunchAgents instead.
    `launchctl bootout` is a recommended alternative.
    /Library/LaunchAgents/com.oray.sunlogin.agent.plist: Could not find specified service
    Unload failed: 113: Could not find specified service

    $ sudo launchctl disable -w /Library/LaunchAgents/com.oray.sunlogin.startup.plist
    Unrecognized target specifier, did you mean
    system/com.apple.dt.automationmode-writer
    system/com.apple.periodic-weekly
    system/com.apple.periodic-monthly
    system/org.apache.httpd
    system/com.apple.periodic-daily

    Usage: launchctl disable
    takes a form of /.
    Please refer to `man launchctl` for explanation of the specifiers.
    $
    $ launchctl list | grep -i sun
    – 0 com.oray.sunlogin.client.startup
    25464 -9 com.oray.sunlogin.desktopagent
    $
    $ sudo launchctl disable com.oray.sunlogin.client.startup
    Unrecognized target specifier, did you mean
    gui/501/com.oray.sunlogin.client.startup

    Usage: launchctl disable
    takes a form of /.
    Please refer to `man launchctl` for explanation of the specifiers.
    $
    $ sudo launchctl disable com.oray.sunlogin.desktopagent
    Unrecognized target specifier, did you mean
    gui/501/com.oray.sunlogin.desktopagent

    Usage: launchctl disable
    takes a form of /.
    Please refer to `man launchctl` for explanation of the specifiers.
    $
    # 使用提示中的来进行disable
    $ sudo launchctl disable gui/501/com.oray.sunlogin.desktopagent
    $ sudo launchctl disable gui/501/com.oray.sunlogin.client.startup
    `

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注