menu Chancel's blog
rss_feed
Chancel's blog
有善始者实繁,能克终者盖寡。

Linux后台运行服务的几种方式

作者:Chancel Yang, 创建:2023-02-28, 字数:5320, 已阅:343, 最后更新:2023-02-28

这篇文章更新于 359 天前,文中部分信息可能失效,请自行甄别无效内容。

Linux后台运行程序的方法有以下常见的几种

  • ctrl+z
  • screen
  • nohup
  • supervisor
  • systemd

以下实践基于Ubuntu2204系统,理论上其他Linux发行版也适用

1. 区分使用场景

如果命令执行时间稍长且不需要查看输出结果,使用&作为后台运行标志或在运行之后使用ctrl+z挂起进程到后台,结合jobs以及fg来切换当前会话的后台任务

如果希望命令在会话结束之后保持运行,则需要考虑使用screennohupscreen适合运行一些短时任务(如脚本),nohup则适合运行一些长时任务(如代理程序)

如果对日志记录/权限控制方面有需求,screennohup就显得不太够用了,这个时候适合使用supervisorsystemd等daemon程序创建后台守护进程(Web后台)

2. 使用例子

2.1. ctrl-z

ping为例,向114.114.114.114发送4个icmp包并记录结果

ping -c 4 114.114.114.114 > 114-ping.txt &

&符号的效果与运行之后ctrl-z是类似的,如果不希望记录结果,要随时方便切换回来查看结果,则可以考虑使用ctrl+z挂起进程到后台

ping 114.114.114.114 # 按ctrl+z挂起

挂起后,使用jobs -l查看被挂起的后台进程

jobs -l
[1]  - 241975 suspended  ping 114.114.114.114

使用指令fg %num可以将后台任务进程重新唤起,可以看到进程重新执行了

fg %1
64 bytes from 114.114.114.114: icmp_seq=3 ttl=117 time=6.19 ms
64 bytes from 114.114.114.114: icmp_seq=4 ttl=117 time=6.37 ms

以上的方法只在当前会话有效,如果会话结束,则程序会结束运行

2.2. screen

如果希望程序在会话结束之后继续运行,则可以考虑使用screen,部分Linux发行版需自行安装该程序

sudo apt install screen

使用screen创建一个ping的会话

screen -R ping

退出会话之后,重新创建会话并再次使用创建ping会话的指令

screen -R ping

可以看到命令依旧在运行

2.3. nohup

screen相较于nohup而言,更适合执行一些脚本,nohup则适合运行长时任务,nohup全称是no hang up,即不挂起地运行程序,无视系统的Hang Up信号

在网络上搜索nohup的使用方法,比较常见的运行方法如下

nohup command > command.log 2>&1 &

这里面比较费解的是2>&1的含义

在Linux中定义了三种输入输出,分别是

  • 0:标准输入
  • 1:标准输出
  • 2:错误输出

标准输出1在运行时可以不写,所以执行command > command.log等价于command 1 > command.log,在理解到这点后,2>&1的含义就比较清晰了,表示将错误输出2重定向到标准输出变量&1中,再结合>一起输出到日志中的意思

把命令写全了等价于

nohup command 1> command.log 2>&1 &

注意:nohup command 2>&1 > command.log & 是不正确的写法(感谢leafee98指正),错误输出(2)被重定向到标准输出(1),随后标准输出(1)被重定向到文件,所以错误输出失效了

nc程序扫描端口为例,用nuhup执行如下

nohup nc -ztv -w 2 114.114.114.114 1-100 > /tmp/nc.log 2>&1 &

检查/tmp/nc.log文件,可以看到正在执行

➜  ~ tail -f /tmp/nc.log 
nc: connect to 114.114.114.114 port 1 (tcp) timed out: Operation now in progress
nc: connect to 114.114.114.114 port 2 (tcp) timed out: Operation now in progress
...

nohup用于执行一些简易后台任务效果还是非常不错的,但如果对输出日志切割、运行用户、开机自启等有要求,则需要考虑daemon类程序,如supervisorsystemd

2.4. supervisor

supervisor是与systemd类似的轻量级进程控制系统,用于长时间运行程序,常用于web后台程序、代理程序等,在大部分Linux发行版上需要自行安装,如在ubuntu2204中安装并启动服务

sudo apt install supervisor
sudo systemctl enable supervisor
sudo systemctl start supervisor

supervisor相较于nohup,支持非常丰富的参数设定,环境设定以及守护进程,安装supervisor后,以同步软件syncthing为例

创建/etc/supervisor/conf.d/syncthing.conf文件,具体如下

[program:syncthing]
directory=/home/apps/syncthing/syncthing-linux-amd64-v1.19.0
command=/home/apps/syncthing/syncthing-linux-amd64-v1.19.0/syncthing
autostart=true
autorestart=true
startsecs=10
stdout_logfile=/var/log/supervisor/%(program_name)s-stdout.log
stdout_logfile_maxbytes=5MB
stdout_capture_maxbytes=5MB
stdout_logfile_backups=5
stderr_logfile=/var/log/supervisor/%(program_name)s-stderr.log
stderr_logfile_maxbytes=5MB
stderr_capture_maxbytes=5MB
stderr_logfile_backups=5
user = apps
environment = HOME="/home/apps/syncthing/syncthing-linux-amd64-v1.19.0"

大部分参数非常简洁易懂,其中

  • autorestart:开机自启
  • stdout_*:标准输出日志
  • strerr_*:异常输出日志
  • user:运行程序的用户
  • environment:设定运行的环境参数

使新创建的服务生效

sudo supervisorctl update

supervisorctl支持多个操作,如

  • update:表示更新服务列表,对新建/修改的服务重新运行
  • status:查看所有服务运行状态
  • start/stop/restart:启动/停止/重启服务

2.5. systemd

在2015年后,systemd是大部分发行版自带用于取代SysV init的系统套件,在引导过程中提供可靠并行性以及对进程、守护进程、服务和挂载点进行集中管理的程序

如果对现代Linux操作系统熟悉,对systemd大概率是不会感到陌生的,systemd是比较重量级的系统套件,除非有明确使用需求,否则一般不考虑作为小应用的后台运行方式,supervisor足够应付绝大部分应用场景了

systemd常见用户单元配置目录如下(区别于系统单元配置)

  • $HOME/.config/systemd/user
  • /usr/lib/systemd/user
  • $HOME/.local/share/systemd/user
  • /etc/systemd/user

还是以同步软件syncthing为例,添加一个配置文件$HOME/.config/systemd/user/syncthing.service,内容如下

[Unit]description=Sync Files
Documentation=https://syncthing.net
After=network.target

[Service]
Type=simple
Environment=HOME="/home/apps/syncthing/syncthing-linux-amd64-v1.19.0"
ExecStart=/home/apps/syncthing/syncthing-linux-amd64-v1.19.0/syncthing
# 日志记录需systemd版本 > 240
StandardOutput=append:/home/apps/syncthing/syncthing-linux-amd64-v1.19.0/logs/standard-output.log
StandardError=append:/home/apps/syncthing/syncthing-linux-amd64-v1.19.0/logs/standard-error.log

[Install]
WantedBy=multi-user.target

启动服务

systemctl start syncthing --user

如修改配置单,需要运行刷新systemd

systemctl daemon-reload --user

配置单具体怎么写?可以参考 Systemd入门教程:实战篇 - 阮一峰,以下是对配置文件的重点部分进行说明

[Unit]description= # 程序描述
Documentation= # 参考文档
After= # 服务启动顺序,如network.target表示在network服务启动之后再启动该服务
Before= # 同上,表示在某服务启动之前
Wants= # 服务弱依赖,在启动服务前确保wants所指服务已正常启动
Requires= # 强依赖关系,如果Requires所指服务已停止运行,则本服务也会结束

[Service]
Type= # 服务类型,值类型可为simple/fork/oneshot/dbus/notify/idle
Environment= # 环境变量在此添加
EnvironmentFile= # 环境变量文件(如.bashrc)
ExecStart= # 启动进程时执行的命令
ExecReload= # 重启服务时执行的命令
ExecStop= # 停止服务时执行的命令
ExeStartPre= # 启动进程之前执行的命令
ExeStartPost= # 启动进程之后执行的命令
ExeStopPost= # 停止进程之后执行的命令
Restart= # 定义退出后是否要重启服务,值可为no/on-success/on-failure/on-abnormal/on-abort/onwatchdog/always
RestartSec= # 进程退出之后重启的时间间隔

[Install]
WantedBy= # 表示归属的target,这部分比较复杂,常用的是multi-user.target与graphical.target

[[replyMessage== null?"发表评论":"发表评论 @ " + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

还没有可以显示的留言...
[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]