Skip to content

程序后台执行

nohup 和 &

nohup(n ohang up)的意思是不挂起、永久执行。 nohup运行命令可以使运行的命令永久的执行下去,和用户终端没有关系,可以在你退出帐户/关闭终端之后继续运行相应的进程。例如我们断开SSH连接并不会影响他的运行(注意:nohup没有后台运行的意思,&才是后台运行)。

&是指在后台运行,当用户退出(挂起)、关闭终端的时候,后台运行的这条命令也会退出。

所以,想要后台运行,而且会话退出后程序也能正常运行,可以把 nohup 和 & 结合使用。

先看一下一条实际的命令:

nohup python server.py &

# 日志输出到log文件
nohup python server.py > python.log 2>&1 &

# 输出到 /dev/null,即忽略输出
nohup python3 script.py >/dev/null 2>&1 &

Linux系统预留可三个文件描述符:0、1和2,他们的意义如下所示:
0——标准输入(stdin)
1——标准输出(stdout)
2——标准错误(stderr)

/dev/null 是一个特殊的设备文件,这个文件接收到任何数据都会被丢弃。因此,null这个设备通常也被称为位桶(bit bucket)或黑洞。

nohup python3 script.py >/dev/null 2>&1 & 中: - >/dev/null 相当于 1>/dev/null 表示标准输出重定向到null,就是不输出任何东西; - 2>&1 2重定向到地址1,即标准错误输出重定向到标准输出,由于前面标准输出重定向到null,所以标准错误也不输出任何东西; - & 最后的&表示后台执行这条命令。

总结, nohup python3 script.py >/dev/null 2>&1 & 意思是后台执行脚本,不会看到输出,并且挂起,当关闭终端/退出账户的时候,脚本也能继续执行。

systemd

systemd service,系统和服务管理器。

unit的文件位置一般主要有三个目录:

Table 1.  Load path when running in system mode (--system).
┌────────────────────────┬─────────────────────────────┐
│Path                    │ Description                 │
├────────────────────────┼─────────────────────────────┤
│/etc/systemd/system     │ Local configuration         │
├────────────────────────┼─────────────────────────────┤
│/run/systemd/system     │ Runtime units               │
├────────────────────────┼─────────────────────────────┤
│/lib/systemd/system     │ Units of installed packages │
└────────────────────────┴─────────────────────────────┘

这三个目录的配置文件优先级依次从高到低,如果同一选项三个地方都配置了,优先级高的会覆盖优先级低的。其中 /usr/lib/systemd/system 其实是的软链接.

systemd service 配置文件均以 .service 为后缀,服务配置文件统一放在 /lib/systemd/system,同时暴露一个可供用户存放的服务配置文件的目录 /etc/systemd/system,如果用户需要,新建的服务配置就放在这个目录下。

以下是一个 unit 例子:

[Unit]
Description=Python Server
After=network.target
[Service]
WorkingDirectory=/path
User=root
Type=idle
ExecStart=/usr/bin/python3 /path/server.py
Restart=always
[Install]
WantedBy=multi-user.target

# 添加或修改后须重新加载systemd
systemctl daemon-reload
# 启动服务
systemctl start {service-name}
# 服务状态
systemctl status {service-name}
# 停止服务
systemctl stop {service-name}
# 重启服务
systemctl restart {service-name}

# 设置开机启动
systemctl enable {service-name}
# 设置取消开机启动
systemctl disable {service-name}

# 查看服务日志。-u 指定unit,-f 跟随输出
journalctl -u {service-name} -f

参考: https://systemd-book.junmajinlong.com/service_1.html http://www.jinbuguo.com/systemd/systemd.service.html#%E9%80%89%E9%A1%B9 https://www.cnblogs.com/TonvyLeeBlogs/articles/13762400.html

tmux

terminal multiplexer(终端复用器),可以保持 tmux 的会话以达到后台运行的效果。

Tmux终端管理工具

screen

screen是一款由GNU计划开发的用于命令行终端切换的自由软件。

# 创建会话窗口
screen -S {screen-name}

# 窗口列表
screen -ls

# 恢复会话窗口
screen -r {screen-name || screen-id}

先按 Ctrl-A 再按 Ctrl-D 暂时断开 (detach)会话。这也是判断是普通窗口和 screen 的一种方法,普通窗口会直接关闭。

如果在会话中直接按 Ctrl-D 或输入 exit 会直接删除会话,不可恢复。

总结: - 在Attached状态下,按Ctrl+D 或输入exit,都会直接删除创建的会话,不能在激活! - 在Attached状态下,直接叉掉终端,此时会话状态变成Detacted,表示挂起虚拟终端,如果此时有运行的程序也就变成在后端运行了,可以通过screen -r ID/session_name 重新打开会话! - 在Attached状态下,按Ctral+a,再按d,此时会退出并挂起虚拟终端(保存会话,后台运行该虚拟终端),此时会话状态变成Detacted - 在Attached状态下,按Ctral+a,再按k(kill),此时会关闭对话,等同输入:exit - 在Attached状态下,按Ctral+a,再按c(create):新建一个虚拟终端 - 在Attached状态下,按Ctral+a,再按?:显示所有绑定键盘

在进入会话时,按Ctrl+D 或 输入exit 回车就会退出,并删除会话,此时使用screen -ls就看不到该会话了!

supervisor

Supervisor 是使用python开发的一个进程管工具,一般

安装:

sudo apt-get install supervisor

配置文件路径 /etc/supervisor/supervisord.conf

子进程配置文件 /etc/supervisor/conf.d/*.conf

写一个测试脚本 echo_time.sh,间隔两秒输出时间:

#/bin/bash

while true; do
    echo `date +%Y-%m-%d,%H:%M:%S`
    sleep 2
done

/etc/supervisor/conf.d/新增子进程配置文件 echo_time.conf

[program:echo_time]
command=sh /tmp/echo_time.sh
priority=999                ; the relative start priority (default 999)
autostart=true              ; start at supervisord start (default: true)
autorestart=true            ; retstart at unexpected quit (default: true)
startsecs=10                ; number of secs prog must stay running (def. 10)
startretries=3              ; max # of serial start failures (default 3)
exitcodes=0,2               ; 'expected' exit codes for process (default 0,2)
stopsignal=QUIT             ; signal used to kill process (default TERM)
stopwaitsecs=10             ; max num secs to wait before SIGKILL (default 10)
user=root                 ; setuid to this UNIX account to run the program
log_stdout=true
log_stderr=true             ; if true, log program stderr (def false)
logfile=/tmp/echo_time.log
logfile_maxbytes=1MB        ; max # logfile bytes b4 rotation (default 50MB)
logfile_backups=10          ; # of logfile backups (default 10)
stdout_logfile_maxbytes=20MB  ; stdout 日志文件大小,默认 50MB
stdout_logfile_backups=20     ; stdout 日志文件备份数
stdout_logfile=/tmp/echo_time.stdout.log

重新读取配置、更新子进程组,启动程序:

sudo supervisorctl reread
sudo supervisorctl update

看下日志:

$ tail -f /tmp/echo_time.stdout.log
2023-05-14,23:42:46
2023-05-14,23:42:48
2023-05-14,23:42:50

supervisorctl 查看子进程运行:

$ sudo supervisorctl
supervisor> help

default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail   
avail  fg        pid   remove  shutdown  status  update 
clear  maintail  quit  reread  signal    stop    version

supervisor> status
echo_time                        RUNNING   pid 254384, uptime 0:00:42
supervisor>stop all
echo_time: stopped
supervisor> status
echo_time                        STOPPED   May 14 11:43 PM
supervisor> start echo_time
echo_time: started
supervisor> tail -f echo_time                                                                                                                                                                 
==> Press Ctrl-C to exit <==                                                                                                                                                                 
2023-05-14,23:42:46                                                                                                                                                                           
2023-05-14,23:42:48                                                                                                                                                                           
2023-05-14,23:42:50 

pstree 可以看到 supervisord 是 systemd 的子进程,同时后面的 sh sleep 是 supervisord 子进程 echo_time

systemd─┬─ModemManager───2*[{ModemManager}]
        ├─NetworkManager───2*[{NetworkManager}]
        ├─supervisord───sh───sleep

参考 https://juejin.cn/post/6844903745587773448