linux 上的开机自启动很简单,通过 systemd 就能搞定。对于 macos 和 windows 的开机自启动则没有记录过,这里记录下。
MacOS 开机自启动
Macos 提供三种开机自启动的方式,详情可以看这里三种方式配置 Mac OS X 的启动项。这是一篇 12 年的老文章了。
这里挑选一种和 linux 上的 systemd 很像的方式,使用 launchd 来进行开机自启动。和 systemd 一样,launchd 也是 MacOS 上的第一个进程,并且提供和 systemctl 很类似的 launchctl 工具。
plist 文件
使用 Launchd 设置开机自启动,仅仅需要编写一个plist
文件并将其放到~/Library/LaunchAgents/
。以下是一个应用开机自启的 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>com.arloor.sslocal</string>
<!-- 退出后是否重启 -->
<key>KeepAlive</key>
<false />
<!-- 加载后立即启动,即开机自启。如果设置为false,则bootstrap后还需要kickstart才会启动 -->
<key>RunAtLoad</key>
<true />
<key>WorkingDirectory</key>
<string>/tmp</string>
<key>EnvironmentVariables</key>
<dict>
<key>aPATH</key>
<string>/bin:/usr/bin:/usr/local/bin</string>
</dict>
<key>ProgramArguments</key>
<array>
<string>/Users/bytedance/.cargo/bin/sslocal</string>
<string>--local-addr</string>
<string>localhost:2080</string>
<string>-k</string>
<string>xxxxxxxx</string>
<string>-m</string>
<string>aes-256-gcm</string>
<string>-s</string>
<string>host:port</string>
<string>-v</string>
</array>
<!-- 软资源限制 -->
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>65536</integer>
</dict>
<!-- 硬资源限制 -->
<key>HardResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>65536</integer>
</dict>
<!-- 标准输出路径 -->
<key>StandardOutPath</key>
<string>/tmp/sslocal.log</string>
<!-- 标准错误路径 -->
<key>StandardErrorPath</key>
<string>/tmp/sslocal.log</string>
</dict>
</plist>
如果需要其他字段来自定义能力,可以参考 man launchd.plist
,或者看launchd.info
启动和停止
如果想实现类似systemctl restart xx
的能力,可以使用下面的脚本:
#! /bin/bash
# 使用while循环读取参数
while [ $# -gt 0 ]; do
if [ "$1" == "stop" ]; then
stop=1
elif [ "$1" != "start" ]; then
service_name=$1
fi
shift # 移除第一个参数
done
[ "$service_name" == "" ] && {
service_name="com.arloor.sslocal"
}
[ "$stop" == "1" ] && {
echo "stop and disable [${service_name}]"
} || {
echo "start and enable [${service_name}]"
}
get_cur_pid() {
launchctl list | grep ${service_name} | awk '{print $1}'
}
old_pid=$(get_cur_pid)
if [ "$old_pid" != "" ]; then
echo 关闭老进程 $old_pid
launchctl bootout gui/$(id -u)/${service_name}
launchctl disable gui/$(id -u)/${service_name}
fi
if [ "$stop" != "1" ]; then
launchctl enable gui/$(id -u)/${service_name}
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/${service_name}.plist
pid=$(get_cur_pid)
if [ "$pid" != "" ]; then
echo 新进程 $pid
else
echo 启动失败
fi
fi
把这个脚本命名成 systemctl
,那你就可以:
systemctl start com.arloor.sslocal
systemctl stop com.arloor.sslocal
service 是否被 disable 的 db 文件地址如下。MacOS 不会自动删除 db 文件中无效的 service,这导致执行launchctl print-disabled gui/$(id -u)
时会看到一些无效的 service。如果想手动删除这些无效的 service,需要先在恢复模式关闭安全模式,然后才能通过 vim 修改。
/private/var/db/com.apple.xpc.launchd/disabled.$(id -u).plist
老命令
unload 和 load 是老旧的 launchctl 命令,man launchctl
能看到,官方推荐我们使用 bootstrap | bootout | enable | disable
unload -w
等同于bootout + disable
,停止进程并禁用开机自启动。load -w
等同于enable + bootstrap
,启动进程并设置开机自启动。bootstrap
和bootout
只有在 service 是 enable 的状态下才有效。所以下面的脚本中,bootout 在 disable 之前,bootstrap 后 enable 之后。bootstrap
需要使用 plist 的路径,而不是 service-namelaunchctl kickstart -p
用于打印当前进程的 pid
#! /bin/sh
service_name="com.arloor.sslocal"
get_cur_pid() {
launchctl list | grep ${service_name} | awk '{print $1}'
}
old_pid=$(get_cur_pid)
if [ "$old_pid" != "" ]; then
echo 关闭老进程 $old_pid
launchctl unload -w ~/Library/LaunchAgents/${service_name}.plist
fi
if [ "$1" != "stop" ]; then
sleep 1
launchctl load -w ~/Library/LaunchAgents/${service_name}.plist
pid=$(get_cur_pid)
if [ "$pid" != "" ]; then
echo 新进程 $pid
else
echo 启动失败
fi
fi
全局资源限制
unix 系统都限制了可打开文件数,上面的 plist 修改了单个进程的文件描述符数量限制。如何修改全局资源限制呢?
- 新建/Library/LaunchDaemons/limit.maxfiles.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>limit.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxfiles</string>
<string>64000</string>
<string>524288</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>
- 修改文件权限
sudo chown root:wheel /Library/LaunchDaemons/limit.maxfiles.plist
sudo chmod 644 /Library/LaunchDaemons/limit.maxfiles.plist
- 加载 plist 文件(或重启系统后生效 launchd 在启动时会自动加载该目录的 plist)
sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist
- 确认更改后的限制
launchctl limit maxfiles
macOS 定时任务
<?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>com.arloor.job</string>
<!-- 加载后立即启动,即开机自启 -->
<key>RunAtLoad</key>
<true />
<key>WorkingDirectory</key>
<string>/tmp</string>
<key>ProgramArguments</key>
<array>
<string>/Users/arloor/bin/work</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<!-- 每天10点 -->
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<!-- 标准输出路径 -->
<key>StandardOutPath</key>
<string>/tmp/work.log</string>
</dict>
</plist>
- 这样设置后,每天 10 点会执行一次。
- 如果 10 点刚好 mac 在待机,则唤醒后会执行一次。
- 如果 10 点是关机的,则开机后不会执行。
- 还有个 StartInterval 的参数,每多少秒执行一次。这个参数因睡眠导致的错过在唤醒时不会执行的。
windows 开机自启动
编写startup.vbs
,放到这个文件夹下(可以使用 win+r
输入shell:Startup
):
%userprofile%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
startup.vbs
内容如下:
set forward=WScript.CreateObject("WScript.Shell")
forward.Run "taskkill /f /im forward.exe",0,True
forward.Run "C:\Users\arloor\go\bin\forward.exe -conf D:\bin\caddyfile -log E:\data\var\log\forward.log -socks5conf=D:\bin\socks5.yaml",0
- 先关闭 forward.exe,末尾的
0,True
表示不开启窗口,等待该命令结束再执行下一行 - 再启动 forward.exe,末尾的
0
表示不开启窗口
具体 Run 命令见www.vbsedit.com