惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
J
Java Code Geeks
雷峰网
雷峰网
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
腾讯CDC
博客园 - 司徒正美
B
Blog RSS Feed
博客园 - 三生石上(FineUI控件)
I
InfoQ
N
Netflix TechBlog - Medium
L
LangChain Blog
博客园_首页
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Tailwind CSS Blog
MyScale Blog
MyScale Blog
美团技术团队
The Cloudflare Blog
爱范儿
爱范儿
Stack Overflow Blog
Stack Overflow Blog
博客园 - 聂微东
H
Help Net Security
Martin Fowler
Martin Fowler
V
Visual Studio Blog

Pil0tXia

撒下种子,然后不期而遇 —— 在 CommunityOverCode Asia 2024 与热爱重逢 Apache EventMesh vs. AWS EventBridge: “产品化” 是开源中间件的出路吗? 《铃芽之旅》4K SDR madVR 渲染壁纸 成为 Apache Committer 只是我投身开源社区建设的开始 Apache 社区力量的汇聚 & 程序员奔现大会:CommunityOverCode Asia 2023 给我的成长 【开源贡献笔记】异步文件事件监听:协调稳健的 Webhook 配置缓存同步 【联想实习笔记】克服在流中使用 Lambda 表达式排序时编译器类型推断的弱点 【开源贡献笔记】前辈在单元测试里留下的 TODO 注释,你该信吗? 【开源之夏】DataSphereStudio 集成 GitLab 课题 Proposal(已中选) 【开源之夏】Apache EventMesh 整合 admin 模块课题 Proposal(已中选) 【开源贡献笔记】确保线程在阻塞状态被中断的 InterruptedException 得到处理 【联想实习笔记】如何优雅地处理数据表中一对多的重复记录 【联想实习笔记】分步查询真的一定比联表查询更好吗? 编译原理学习笔记 【联想实习笔记】软件开发的流程和规范 《你的名字。》 2K SDR 4K 原盘超采样壁纸 《铃芽之旅》是新海诚对粉丝的背叛吗?不,是他对自己的忠实。 www 域名前缀过时了吗?你需要知道的一切 基于不蒜子 2.3 自定义站点访问量 (site_pv, site_uv, page_pv)
Linux 后台程序的创建与管理
Pil0tXia · 2022-12-11 · via Pil0tXia

用 ssh 登录远程的 Linux 服务器,某些程序或脚本会在前台持续运行、占用 shell 窗口,且终端连接断开时任务也会中止。如何让运行命令提交后不受本地关闭终端窗口或网络断开连接的干扰呢?本文列举了一些实用的命令。

省流版

nohop <command> &

nohup

场景

如果只是临时有一个命令需要长时间运行,什么方法能最简便的保证它在后台稳定运行呢?

解决方法

我们知道,当用户注销 (logout) 或者网络断开时,终端会收到 HUP (hangup) 信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。

nohup 无疑是我们首先想到的办法。顾名思义,nohup 的用途就是让提交的命令忽略 hangup 信号。让我们先来看一下 nohup 的帮助信息:

1
2
3
4
5
6
7
8
9
10
11
NOHUP(1)                        User Commands                        NOHUP(1)
NAME
nohup - run a command immune to hangups, with output to a non-tty
SYNOPSIS
nohup COMMAND [ARG]...
nohup OPTION
DESCRIPTION
Run COMMAND, ignoring hangup signals.
--help display this help and exit
--version
output version information and exit

nohup 的使用只需在要处理的命令前加上 nohup 即可,标准输出和标准错误默认会被重定向到当前目录下的 nohup.out 文件中。此时,终端将不再打印输入输出,但终端依然被占用着。退出终端时,程序将继续运行。

一般我们可以在结尾加上 & 将命令放入后台运行,终端将不再被占用。

后台进程管理

jobs 管理作业

通过 jobs 命令可以看到由当前终端创建的后台作业的运行状况。编号为作业号(jobspec)。

  • bg %JOBSPEC 可以让后台 Stopped 的命令继续 Running

  • fg %JOBSPEC 可以让后台的作业来前台执行

  • Ctrl+Z 可以把前台执行的作业送进后台并挂起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
azure_root@UbuntuVM:~$ jobs
[1]- Running nohup ping www.ibm.com & (wd: ~/mirai-api-http/mcl-installer)
[2]+ Running nohup ping www.baidu.com &
azure_root@UbuntuVM:~$ fg %1
nohup ping www.ibm.com (wd: ~/mirai-api-http/mcl-installer)
^Cazure_root@UbuntuVM:~$ jobs
[2]+ Running nohup ping www.baidu.com &
azure_root@UbuntuVM:~$ fg %2
nohup ping www.baidu.com
^Z
[2]+ Stopped nohup ping www.baidu.com
azure_root@UbuntuVM:~$ bg %2
[2]+ nohup ping www.baidu.com &
azure_root@UbuntuVM:~$ jobs
[2]+ Running nohup ping www.baidu.com &

jobs 的缺点在于,一旦丢失 ssh 连接,将再也无法管理作业。

ps 搜索进程

Process Status 类似于 Windows 的任务管理器,可以显示系统的所有进程。编号为 PID。

1
2
3
4
5
6
7
azure_root@UbuntuVM:~$ nohup ping www.ibm.com &
[1] 1408536
azure_root@UbuntuVM:~$ nohup: ignoring input and appending output to 'nohup.out'
^C
azure_root@UbuntuVM:~$ ps -ef | grep 1408536
azure_r+ 1408536 1407733 0 19:01 pts/0 00:00:00 ping www.ibm.com
azure_r+ 1408544 1407733 0 19:01 pts/0 00:00:00 grep --color=auto 1408536

可以附加 grep -v grep 来忽略 grep 自身进程。

1
2
azure_root@UbuntuVM:~$ ps -ux | grep ping | grep -v grep
azure_r+ 1408536 0.0 0.2 8964 2700 pts/0 S 19:01 0:00 ping www.ibm.com

其中,ps -efps -ux 的区别在于输出风格和内容的不同,ps -uxps -aux 的区别在于进程所属用户不同。对我而言,ps -ux 获得的信息更直接。

killl 结束进程

1
kill -9 PID
1
kill %JOBSPEC

-9 是可选的,用于强制杀死进程。若要使用作业号,需要在 jobspec 前加上 %。纯数字会被认为是 PID。

1
2
3
4
5
6
7
azure_root@UbuntuVM:~$ kill %2
azure_root@UbuntuVM:~$ jobs
[2]+ Terminated nohup ping www.baidu.com
azure_root@UbuntuVM:~$ kill -9 %2
-bash: kill: %2: no such job
azure_root@UbuntuVM:~$ jobs
azure_root@UbuntuVM:~$

更改默认的 nohup.out

1
nohup python file.py > log.txt 2>&1 &

> 表示重定向标准输出(STDOUT),这里重定向到了当前目录下的 log.txt,不存在此文件时会自动创建。

若无 2>&1 则错误信息不会重定向。

setsid

nohup 无疑能通过忽略 HUP 信号来使我们的进程避免中途被中断。除此之外,如果我们的进程不属于接受 HUP 信号的终端的子进程,那么自然也就不会受到 HUP 信号的影响了。setsid 能帮助我们做到这一点。让我们先来看一下 setsid 的帮助信息:

1
2
3
4
5
6
7
SETSID(8)                 Linux Programmer’s Manual                 SETSID(8)
NAME
setsid - run a program in a new session
SYNOPSIS
setsid program [ arg ... ]
DESCRIPTION
setsid runs a program in a new session.

可见 setsid 的使用也是非常方便的,也是只需要在要处理的命令前加上 setsid 即可,不过 shell 依然会被占用,Ctrl+CCtrl+Z 也不会生效。

1
2
3
azure_root@UbuntuVM:~$ setsid ping www.google.com
azure_root@UbuntuVM:~$ ps -ef | grep www.google.com | grep -v grep
azure_r+ 1409166 1 0 19:43 ? 00:00:00 ping www.google.com

值得注意的是,此例中我们的 PID 为 31094,而它的 PPID(父 ID)为 1(即 init 进程 ID),并不是当前终端的进程 ID。这一点与 nohup 例不同。

&

这里还有一个关于 subshell 的小技巧。我们知道,将一个或多个命名包含在 () 中就能让这些命令在子 shell 中运行,从而扩展出很多有趣的功能,我们现在讨论的就是其中之一。

当我们将 & 也放入 () 内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过 jobs 来查看的,Ctrl+CCtrl+Z 同样不会生效。

1
2
3
azure_root@UbuntuVM:~$ (ping www.office.com &)
azure_root@UbuntuVM:~$ ps -ef | grep www.office.com | grep -v grep
azure_r+ 1409270 1 0 19:56 pts/0 00:00:00 ping www.office.com

disown

场景

我们已经知道,如果事先在命令前加上 nohup 或者 setsid 就可以避免 HUP 信号的影响。但是如果我们未加任何处理就已经提交了命令,该如何补救才能让它避免 HUP 信号的影响呢?

解决方法

此时再想用 nohup 或者 setsid 已经为时已晚,只能用作业调度和 disown 来解决了。让我们来看一下 disown 的帮助信息:

1
2
3
4
5
6
7
8
9
10
11
disown [-ar] [-h] [jobspec ...]
Without options, each jobspec is removed from the table of
active jobs. If the -h option is given, each jobspec is not
removed from the table, but is marked so that SIGHUP is not
sent to the job if the shell receives a SIGHUP. If no jobspec
is present, and neither the -a nor the -r option is supplied,
the current job is used. If no jobspec is supplied, the -a
option means to remove or mark all jobs; the -r option without
a jobspec argument restricts operation to running jobs. The
return value is 0 unless a jobspec does not specify a valid
job.
  • disown -h JOBSPEC 来使某个作业忽略 HUP 信号。
  • disown -ah 来使所有的作业都忽略 HUP 信号。
  • disown -rh 来使正在运行的作业忽略 HUP 信号。

需要注意的是,当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用 jobs 来查看它,但 ps 依然可以查找到它。

这种方法的操作对象是作业,如果需要通过 jobs 命令得到作业列表,在运行命令时应该在结尾添加 & 来使命令成为一个作业并在后台运行。

如果没有把命令作为作业来运行,我们需要按下 Ctrl+Z 将前台进程送入后台并挂起,然后才可以用 jobs 命令查询它的作业号,接着用 bg %JOBSPEC 让它继续运行。需要注意的是,如果挂起会影响当前进程的运行结果,请慎用此方法。