前言
本节主要描述Linux开机自启的一些方式。
crontab
crontab
是 Linux 系统中用于设置周期性被执行的指令的命令。常用指令:
crontab -e # 执行文字编辑器来设定时程表
crontab -l # 列出当前的时程表
crontab -r # 删除当前的时程表
crontab -u username -l # 列出 username 用户的时程表,编辑、删除同理
crontab 格式:
# f1:表示分钟(0~59)、f2:表示小时(0~23)、f3:表示月份中的第几日(1~31)
# f4:表示月份(1~12)、f5:表示星期中的第几天(0~6)
f1 f2 f3 f4 f5 program # program 表示要执行的程序。
- 当 f1 为 * 时表示每分钟都要执行 program,f2 为 * 时表示每小时都要执行程序,后续同理;
- 当 f1 为 a-b 时表示从第 a 分钟到第 b 分钟这段时间内要执行,f2 为 a-b 时表示从第 a 到第 b 小时都要执行,后续同理;
- 当 f1 为 */n 时表示每 n 分钟个时间间隔执行一次,f2 为 */n 表示每 n 小时个时间间隔执行一次,后续同理;
- 当 f1 为 a, b, c,… 时表示第 a, b, c,… 分钟要执行,f2 为 a, b, c,… 时表示第 a, b, c…个小时要执行,后续同理;
示例:crontab -e
# 每分钟执行一次检查脚本,check.sh 实现监测某个程序的运行,如果异常退出或者中断了,继续执行。
* * * * * /home/nvidia/Desktop/check.sh >> /home/nvidia/Desktop/check.log 2>&1
systemd
systemd 是一个专用于 Linux 操作系统的系统与服务管理器。systemd 并不是一个命令,而是一组命令,涉及到系统管理的方方面面。
- systemctl:systemd 的主命令,用于管理系统。
- systemd-analyze:用于查看启动耗时。
- hostnamectl:查看当前主机的信息。
- localectl:查看本地化设置。
- timedatectl:查看当前时区设置。
- loginctl:查看当前登录的用户。
systemd 将各种系统启动和运行相关的对象, 表示为各种不同类型的单元(unit), 并提供了处理不同单元之间依赖关系的能力。单元文件是 ini
风格的纯文本文件,封装了各种不同的单元类型:
- service 单元。用于封装一个后台服务进程。
- socket 单元。 用于封装一个系统套接字(UNIX)或互联网套接字(INET/INET6)或FIFO管道。
- device 单元。用于封装一个设备文件,可用于基于设备的启动。
- mount 单元。 用于封装一个文件系统挂载点(也向后兼容传统的 /etc/fstab 文件)。
- automount 单元。用于封装一个文件系统自动挂载点,仅在挂载点确实被访问的情况下才进行挂载。
- target 单元。 用于将多个单元在逻辑上组合在一起。
- swap 单元。 用于封装一个交换分区或者交换文件。
- path 单元。 用于根据文件系统上特定对象的变化来启动其他服务。
- timer 单元。 用于封装一个基于时间触发的动作。
- slice 单元。 用于控制特定 CGroup 内(例如一组 service 与 scope 单元)所有进程的总体资源占用。
- scope 单元。它与 service 单元类似,但是由 systemd 根据 D-bus 接口接收到的信息自动创建, 可用于管理外部创建的进程。
单元文件通用配置
单元文件是 ini
风格的纯文本文件。通用小节包含 [Unit]
与 [Install]
。
单元文件中的 [Unit]
小节包含与此单元类型无关的通用信息,这些信息不依赖于你这个单元到底是 service、socket、mount 还是其他类型。一些常见选项如下:
Requires:必须依赖的单元。此单元启动时,会尝试启动这些依赖单元。如果依赖单元启动失败,并且设置了 After 依赖,则此单元不会启动。被依赖单元被停止时,此单元也会被连带停止。
Requisite:要求依赖的单元必须已成功启动,否则当前单元立即失败,不会尝试启动依赖单元。
Wants:建议依赖的单元。启动当前单元时会尝试启动这些单元,但即使失败也不影响当前单元。
BindsTo:被绑定的单元如果停止或崩溃,当前单元也会停止。
PartOf:当停止或重启被列出的单元时,当前单元也会被停止或重启。
Conflicts:与当前单元冲突的单元列表,不能共存。启动当前单元时会停止冲突单元,反之亦然。
StopWhenUnneeded:设置为
yes
表示当前单元不再被任何其他运行单元需要时会被自动停止。默认no
。Before:当前单元应当在列出的单元之前启动。
After:当前单元应当在列出的单元之后启动。
PropagatesReloadTo:reload 当前单元时,也 reload 所列出的单元。
ReloadPropagatedFrom:当所列单元被 reload 时,当前单元也随之 reload。
RefuseManualStart:拒绝手动启动,只能通过依赖方式被启动。默认
no
。RefuseManualStop:拒绝手动停止,只能通过依赖方式被停止。默认
no
。OnFailure:当前单元进入 failed 状态时,会自动启动列表中的其他单元。
OnFailureJobMode:配置 OnFailure 启动的单元的执行方式(并发、顺序等)。
IgnoreOnIsolate:执行
systemctl isolate ...
时是否忽略停止该单元。设为yes
则不会停止。默认no
。StopWhenUnneeded:与依赖无关的情况下自动停止。设为
yes
表示当不再被其他单元依赖时会自动停止。
[Install]
小节包含单元的启用信息。 事实上,systemd
在运行时并不使用此小节。 只有 systemctl
的 enable 与 disable 命令在启用/停用单元时才会使用此小节。
- Alias:启用时使用的别名,可以设一个空格分隔的别名列表。
- WantedBy、RequiredBy:表示在使用
systemctl enable
启用此单元时,将会在每个列表单元的.wants/
或.requires/
目录中创建一个指向该单元文件的软连接。 - Also:设置此单元的附属单元,可以设为一个空格分隔的单元列表。当使用
systemctl enable
启用或systemctl disable
停用此单元时,也同时自动的启用或停用附属单元。 - DefaultInstance:仅对模板单元有意义,用于指定默认的实例名称。
单元文件除了通用的 [Unit] 与 [Install] 小节之外,还有各自专属的小节。
服务单元配置
服务单元是以 “.service” 为后缀的单元文件,封装了一个被 systemd 监视与控制的进程。每个服务单元文件都必须包含一个 [Service]
小节。该小节相关指令如下:
Type:设置进程的启动类型。必须设为
simple, exec, forking, oneshot, dbus, notify, idle
之一。- 如果设为
simple
(当设置了ExecStart=
、但是没有设置Type=
与BusName=
时,这是默认值),那么ExecStart=
进程就是该服务的主进程,并且systemd
会认为在创建了该服务的主服务进程之后,该服务就已经启动完成。 exec
与simple
类似,不同之处在于,只有在该服务的主服务进程执行完成之后,systemd
才会认为该服务启动完成。- 如果设为
forking
,那么表示xecStart=
进程将会在启动过程中使用fork()
系统调用。 oneshot
与simple
类似,不同之处在于,只有在该服务的主服务进程退出之后,systemd
才会认为该服务启动完成,才会开始启动后继单元。此种类型的服务通常需要设置RemainAfterExit=
选项。当Type=
与ExecStart=
都没有设置时,Type=oneshot
就是默认值。dbus
与simple
类似,不同之处在于,该服务只有获得了BusName=
指定的D-Bus
名称之后,systemd
才会认为该服务启动完成,才会开始启动后继单元。notify
与exec
类似,不同之处在于,该服务将会在启动完成之后通过sd_notify
之类的接口发送一个通知消息。systemd
将会在启动后继单元之前,首先确保该进程已经成功的发送了这个消息。如果设为此类型,那么下文的NotifyAccess=
将只能设为非none
值。如果未设置NotifyAccess=
选项、或者已经被明确设为none
,那么将会被自动强制修改为main
。注意,目前Type=notify
尚不能与PrivateNetwork=yes
一起使用。idle
与simple
类似,不同之处在于,服务进程将会被延迟到所有活动任务都完成之后再执行。
- 如果设为
RemainAfterExit:当该服务的所有进程全部退出之后,是否依然将此服务视为活动(active)状态。默认值为 no。
GuessMainPID:在无法明确定位该服务主进程的情况下,systemd 是否应该猜测主进程的PID。
PIDFile:该服务PID文件的路径(一般位于 /run/ 目录下)。
BusName:设置与此服务通信 所使用的 D-Bus 名称,在 Type=dbus 的情况下 必须明确设置此选项。
ExecStart:在启动该服务时需要执行的命令行(命令+参数)。除非
Type=oneshot
,否则必须且只能设置一个命令行。仅在Type=oneshot
的情况下,才可以设置任意个命令行(包括零个),多个命令行既可以在同一个ExecStart=
中设置,也可以通过设置多个ExecStart=
来达到相同的效果。如果设为一个空字符串,那么先前设置的所有命令行都将被清空。如果不设置任何 ExecStart= 指令,那么必须确保设置了RemainAfterExit=yes
指令,并且至少设置一个ExecStop=
指令。同时缺少ExecStart=
与ExecStop=
的服务单元是非法的。ExecStartPre、ExecStartPost:设置在执行
ExecStart=
之前/后执行的命令行。语法规则与ExecStart=
相同。ExecReload:可选指令,用于设置当该服务被要求重新载入配置时所执行的命令行。语法规则与
ExecStart=
相同。ExecStop:可选指令,用于设置当该服务被要求停止时所执行的命令行。语法规则与
ExecStart=
相同。ExecStopPost:可选指令,用于设置在该服务停止之后所执行的命令行。语法规则与
ExecStart=
相同。RestartSec:设置在重启服务(
Restart=
)前暂停多长时间。默认值是100毫秒(100ms)。如果未指定时间单位,那么将视为以秒为单位。TimeoutStartSec:设置该服务允许的最大启动时长。如果守护进程未能在限定的时长内发出”启动完毕”的信号,那么该服务将被视为启动失败,并会被关闭。如果未指定时间单位,那么将视为以秒为单位。例如设为”20”等价于设为”20s”。设为 “
infinity
“ 则表示永不超时。当Type=oneshot
时,默认值为 “infinity
“ (永不超时),否则默认值等于DefaultTimeoutStartSec=
的值。TimeoutStopSec:
- 设置每个
ExecStop=
的超时时长。如果其中之一超时,那么所有后继的ExecStop=
都会被取消,并且该服务也会被SIGTERM
信号强制关闭。 - 设置该服务自身停止的超时时长。如果超时,那么该服务将会立即被
SIGTERM
信号强制关闭。
- 设置每个
TimeoutSec:同时设置
TimeoutStartSec=
与TimeoutStopSec=
的快捷方式。RuntimeMaxSec:允许服务持续运行的最大时长。如果服务持续运行超过了此处限制的时长,那么该服务将会被强制终止,同时将该服务变为失败(failed)状态。注意,此选项对
Type=oneshot
类型的服务无效,因为它们会在启动完成后立即终止。默认值为 “infinity
“ (不限时长)。WatchdogSec:设置该服务的
watchdog
的超时时长。watchdog
将在服务成功启动之后被启动。Restart:当服务进程正常退出、异常退出、被杀死、超时的时候,是否重新启动该服务。 所谓”服务进程” 是指
ExecStart=
,ExecStartPre=
,ExecStartPost=
,ExecStop=
,ExecStopPost=
,ExecReload=
中设置的进程。当进程是由于 systemd 的正常操作(例如 systemctl stop|restart)而被停止时,该服务不会被重新启动。该选项的值可以取no
,on-success
,on-failure
,on-abnormal
,on-watchdog
,on-abort
,always
之一。no
(默认值) 表示不会被重启。always
表示会被无条件的重启。on-success
表示仅在服务进程正常退出时重启,所谓”正常退出”是指:退出码为”0”,或者进程收到SIGHUP
,SIGINT
,SIGTERM
,SIGPIPE
信号之一,并且退出码符合SuccessExitStatus=
的设置。on-failure
表示仅在服务进程异常退出时重启, 所谓”异常退出” 是指: 退出码不为”0”, 或者进程被强制杀死(包括 “core dump”以及收到SIGHUP
,SIGINT
,SIGTERM
,SIGPIPE
之外的其他信号)。
SuccessExitStatus:额外定义其他的进程”正常退出”状态。在退出码”0”、以及表示”正常退出”的
SIGHUP
,SIGINT
,SIGTERM
,SIGPIPE
信号之外,再额外添加一组表示”正常退出”的退出码或信号。RestartPreventExitStatus:可以设为一系列以空格分隔的数字退出码或信号名称,当进程的退出码或收到的信号与此处的设置匹配时,无论
Restart=
选项 是如何设置的,该服务都将无条件的禁止重新启动。RestartForceExitStatus:可以设为一系列以空格分隔的数字退出码或信号名称,当进程的退出码或收到的信号与此处的设置匹配时,无论
Restart=
是如何设置的,该服务都将无条件的被自动重新启动。 默认值为空,表示完全遵守Restart=
的设置。RootDirectoryStartOnly:接受一个布尔值。设为 yes 表示根目录
RootDirectory=
选项仅对ExecStart=
中的程序有效,而对ExecStartPre=
,ExecStartPost=
,ExecReload=
,ExecStop=
,ExecStopPost=
中的程序无效。默认值 no 表示根目录对所有Exec*=
系列选项中的程序都有效。NonBlocking:是否为所有基于套接字启动传递的文件描述符设置非阻塞标记(
O_NONBLOCK
)。设为 yes 表示除了通过FileDescriptorStoreMax=
引入的文件描述符之外,所有 ≥3 的文件描述符(非 stdin, stdout, stderr 文件描述符)都将被设为非阻塞模式。该选项仅在与 socket 单元联用的时候才有意义。对于那些先前已经通过FileDescriptorStoreMax=
引入的文件描述符则毫无影响。默认值为 noNotifyAccess:设置通过
sd_notify
访问服务状态通知套接字的模式。Sockets:设置一个 socket 单元的名称,表示该服务在启动时应当从它继承套接字文件描述符。
FileDescriptorStoreMax:
允许在 systemd 中最多为该服务存储多少个使用 “
FDSTORE=1
“ 消息sd_pid_notify_with_fds
的文件描述符。默认值为”0”(不存储)。USBFunctionDescriptors:设为一个包含
USB FunctionFS
描述符的文件路径,以实现USB gadget
支持。仅与配置了ListenUSBFunction=
的 socket 单元一起使用。该文件的内容将被写入ep0
文件。USBFunctionStrings:设为一个包含
USB FunctionFS
字符串的文件路径。 其行为与上面的USBFunctionDescriptors=
类似。
定时器单元配置
定时器单元是以 “.timer” 为后缀的单元文件,封装了一个被 systemd 管理的定时器,以支持基于定时器的启动。
每个定时器单元都必须有一个与其匹配的单元,用于在特定的时间启动。匹配的单元可以通过 Unit=
选项明确指定。若未指定,则默认是与该单元名称相同的 .service
单元。例如 obu.timer
默认匹配 obu.service
单元。
每个定时器单元文件都必须包含一个 [Timer]
小节。该小节相关指令如下:
OnActiveSec、OnBootSec、OnStartupSec、OnUnitActiveSec、OnUnitInactiveSec:定义相对于特定时间点之后一段时间的单调定时器:
OnActiveSec=
相对于该单元自身被启动的时间点;OnBootSec=
相对于机器被启动的时间点, 也就是内核开始运行的时间点;OnStartupSec=
相对于 systemd 被首次启动的时间点,也就是内核启动init进程的时间点;OnUnitActiveSec=
相对于匹配单元最后一次被启动的时间点;OnUnitInactiveSec=
相对于匹配单元 最后一次被停止的时间点;
如果定时器单元在启动时已经超过了
OnBootSec=
或OnStartupSec=
指定的时间, 那么将会立即启动匹配的单元。 但是对于使用其他指令定义的定时器, 超过了就等于错过了,不会尝试去补救。所谓”单调时间”的意思是从开机那一刻(零点)起,只要系统正在运行,该时间就不断的单调均匀递增(但在系统休眠时此时间保持不变),永远不会往后退,并且与时区也没有关系。 即使在系统运行的过程中,用户向前/向后修改系统时间,也不会对”单调时间”产生任何影响。
注意,这些指令设置的定时器并不必然在所设置的精准时间点上启动匹配单元,因为它们还受到下文
AccuracySec=
选项的影响。OnCalendar:定义基于挂钟时间(wallclock)的日历定时器,值是一个日历事件表达式,是与传统cron任务类似的定时器。
AccuracySec:设置定时器的触发精度,默认值是一分钟。
RandomizedDelaySec:将此单元的定时器随机延迟一小段时间,这一小段时间的长度 介于零到该指令设置的时间长度之间,以均匀概率分布。默认值是零,表示不延迟。
Unit:该定时器单元的匹配单元,也就是要被该定时器启动的单元。参数是一个不以 “
.timer
“ 结尾的单元名。默认值是与此定时器单元同名的服务单元。Persistent:此选项仅对
OnCalendar=
指令定义的日历定时器有意义。若设为”yes”,则表示将匹配单元的上次触发时间永久保存在磁盘上。这样,当定时器单元再次被启动时,如果匹配单元本应该在定时器单元停止期间至少被启动一次,那么将立即启动匹配单元。这样就不会因为关机而错过必须执行的任务。默认值为no
WakeSystem:若设为”yes”,则表示当某个定时器到达触发时间点时,唤醒正在休眠的系统并阻止系统进入休眠状态。注意,此选项仅确保唤醒系统,而不关心任务执行完成之后是否需要再次休眠系统。默认值为
no
RemainAfterElapse:接受一个布尔值。若设为”yes”(默认值),则表示保持已过期定时器单元的已加载(loaded)状态,并且依然允许查询其状态。若设为”no”,则表示卸载已过期并且将来也不会被继续触发的定时器单元。
rc.local
rc.local
是在类 Unix 系统(如 Ubuntu、Debian、CentOS 等)中常见的一个启动脚本,用于在系统 启动完成后自动执行指定命令或脚本。通常用于运行后台服务、初始化硬件设备、挂载磁盘、启动脚本等。
路径: /etc/rc.local
作用: 作为系统启动完成后的最后一步执行,通常用于运行用户自定义的启动任务。
Ubuntu 16.04 之后默认 rc.local
已废弃,需手动恢复支持。如果 /etc/rc.local
不存在,可以:
# 1.创建文件
sudo nano /etc/rc.local
# 2.赋予执行权限
sudo chmod +x /etc/rc.local
# 3.创建 systemd 服务以启用 rc.local:
sudo nano /etc/systemd/system/rc-local.service
# 4.启用服务
sudo systemctl enable rc-local
sudo systemctl start rc-local
rc-local.service
服务内容如下:
[Unit]
Description=/etc/rc.local Compatibility
ConditionPathExists=/etc/rc.local
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99
[Install]
WantedBy=multi-user.target