1. launchd
Mac系统下通用的进程管理器,是Mac系统下非常重要的一个进程,一般来说该进程不允许直接以命令行的形式调用。只能通过其控制管理界面,launchctl来进行控制。
launchd主要功能是进程管理。可以理解成是一个常驻在后台的进程,根据用户的配置,来响应特定的系统事件。launchd既可以用于系统级别的服务,又可以用于个人用户级别的服务。
2. 在launchd的语境中,常驻进程有两种:
daemon # 也就是我们常说的守护进程,这种一般对所有用户都有相同的行为,响应相同的事件,始终运行于后台,没有前台交互界面。
agent # 这种是用户级别的服务进程,一般以用户的身份运行。
3. 守护进程(daemon)
是指在UNIX或其他多任务操作系统中在后台执行的电脑程序,并不会接受电脑用户的直接操控。此类程序会被以进程的形式初始化。守护进程程序的名称通常以字母“d”结尾:例如,syslogd就是指管理系统日志的守护进程。
通常,守护进程没有任何存在的父进程(即PID=1),且在UNIX系统进程层级中直接位于init之下。守护进程程序通常通过如下方法使自己成为守护进程:对一个子进程调用fork,然后使其父进程立即终止,使得这个子进程能在init下运行。这种方法通常被称为“脱壳”。
系统通常在启动时一同起动守护进程。守护进程为对网络请求,硬件活动等进行响应,或其他通过某些任务对其他应用程序的请求进行回应提供支持。守护进程也能够对硬件进行配置(如在某些Linux系统上的devfsd),运行计划任务(例如cron),以及运行其他任务。
4. Mac的守护进程目录有以下几处:
~/Library/LaunchAgents # 用户的进程
/Library/LaunchAgents # 管理员设置的用户进程
/Library/LaunchDaemons # 管理员提供的系统守护进程
/System/Library/LaunchAgents # Mac操作系统提供的用户进程
/System/Library/LaunchDaemons # Mac操作系统提供的系统守护进程
以上是launchd的相关配置的存放目录,可以看到,一般我们个人编写的守护进程,都应该放到~/Library/LaunchAgents目录里面。
将定时任务加入系统
#首先要把plist文件放入对相应的目录中(参看4. Mac的守护进程目)
# 加载 your.plist
$ launchctl load /Library/LaunchAgents/your.plist
//添加后进程会直接运行,不用重启电脑
#######查看服务运行状态
$ launchctl list
PID Status Label
- 0 yourLabelName
如果移除移除的话
# 移除 xyz.hanks.spider
$ launchctl unload /Library/LaunchAgents/your.plist
5.plist文件键值
键值 | 描述(作用) |
---|---|
Label <string> |
必选, 进程的名字唯一标示符 |
Program <string> |
要运行的程序, 如果省略这个选项,会把ProgramArguments的第一个元素作为要运行的程序, 任务执行绝对路径 |
ProgramArguments <array of strings> |
字段值会作为Program <string>的参数传入(一般把任务写成可执行文件, 然后紧跟参数), |
KeepAlive <boolean or dictionary of stuff> |
必选, 是否持续执行任务 |
WatchPaths <array of strings> |
可选, 作用:监听路径. 当任何一个被监听的路径(array中指定的string)被修改时,都会重启任务 |
QueueDirectories <array of strings> |
可选, 和WatchPaths的作用很像,作用是监听路径的修改. 不同之处是,只有这个路径是一个目录并且这个目录不为空时 ,任务才会被重启. |
StartOnMount <boolean> |
This optional key causes the job to be started every time a filesystem is mounted. 具体作用未明 |
StartInterval <integer> |
可选, 每间隔 N 秒 重启任务. 当系统是睡眠状态时,任务会在下次系统被唤醒时重启,睡眠时的多个任务重启事件会被合并成一个事件 (系统睡眠时,无论多少个N秒,系统被唤醒时只会执行一次) |
StartCalendarInterval <dictionary of integers or array of dictionary of integers> |
可选, 在指定的日历格式的时间点 重启任务, 缺少的参数会被当做通配符(对应参数时间段内任意时间点), 这个命令和 crontab 命令很像(在系统休眠是也会执行任务), 和 corn (系统休眠时不执行任务,系统被唤醒时,多次执行命令合并成一次.同StartInterval <integer>) 不一样. 参数格式详见下 附表2 |
StandardInPath <string> |
可选, 执行任务的标准数据输入的文件路径 |
StandardOutPath <string> |
可选, 任务执行时保存标准输出的文件路径 |
StandardErrorPath <string> |
可选, 任务执行时,保存错误日志的文件路径 |
Debug <boolean> |
可选, 当任务执行,launchd 将临时的调整它的日志mask到LOG_DEBUG |
附表2
键值 | 描述 |
---|---|
Minute <integer> |
第几分钟时执行任务 |
Hour <integer> |
第几个小时执行任务(采用24时制) |
Day <integer> |
每个月的第几天执行任务 |
Weekday <integer> |
周几执行任务,(0~7) |
Month <integer> |
第几个月执行任务 |
6定时任务样例
<?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>Label</key>
<string>xyz.hanks.spider</string>
<!-- 要运行的程序, 如果省略这个选项,会把ProgramArguments的第一个
元素作为要运行的程序 -->
<key>Program</key>
<string>/Users/zhanks/work/Bookshelf/run.sh</string>
<!-- 每天18:30 -->
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>30</integer>
<key>Hour</key>
<integer>18</integer>
</dict>
<!-- 运行间隔,与StartCalenderInterval使用其一,单位为>秒 -->
<!-- <key>StartInterval</key>-->
<!-- integer>5</integer>-->
<!-- 标准错误输出文件,错误日志 -->
<key>StandardErrorPath</key>
<string>/Users/zhanks/run-err.log</string>
</dict>
</plist>
其他定时任务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>Label</key>
<!-- 名称,要全局唯一 -->
<string>xyz.hanks.spider</string>
<!-- 要运行的程序, 如果省略这个选项,会把ProgramArguments的第一个
元素作为要运行的程序 -->
<key>Program</key>
<string>/Users/hanks/run.sh</string>
<!-- 命令, 第一个为命令,其它为参数-->
<key>ProgramArguments</key>
<array>
<string>/Users/hanks/run.sh</string>
</array>
<!-- 运行时间 -->
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>30</integer>
<key>Hour</key>
<integer>9</integer>
<key>Day</key>
<integer>1</integer>
<key>Month</key>
<integer>5</integer>
<!-- 0和7都指星期天 -->
<key>Weekday</key>
<integer>0</integer>
</dict>
<!-- 运行间隔,与StartCalenderInterval使用其一,单位为秒 -->
<key>StartInterval</key>
<integer>30</integer>
<!-- 标准输入文件 -->
<key>StandardInPath</key>
<string>/Users/hanks/run-in.log</string>
<!-- 标准输出文件 -->
<key>StandardOutPath</key>
<string>/Users/hanks/run-out.log</string>
<!-- 标准错误输出文件 -->
<key>StandardErrorPath</key>
<string>/Users/hanks/run-err.log</string>
</dict>
</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>Label</key>
<string>wxx.example</string>
<key>StandardErrorPath</key>
<string>/Users/computername/Desktop/eeee.txt</string>
<key>StandardOutPath</key>
<string>/Users/computername/Desktop/eeee.txt</string>
<key>KeepAlive</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>python</string>
<string>/Users/computername/Desktop/tests/test.py</string>
</array>
</dict>
</plist>