在 lxterminal 内运行的交互式 bash shell 中,如果我运行后台作业
$ evince &
然后关闭shell,后台作业将被杀死。
如果我将后台命令放入脚本中,并在交互式 bash shell 中运行该脚本:
$ cat test.sh
!# /bin/bash
evince &
$ ./test.sh
$
脚本终止后,后台作业仍在运行。即使在我终止交互式 bash shell 后,后台作业仍然继续运行。
我想知道为什么将后台作业放入脚本中并运行该脚本可以使作业在脚本终止和脚本调用者终止时继续存在?将作业包装到脚本中是否是使作业在脚本终止和调用脚本的 shell 终止后继续存在的好方法?
谢谢。
答案1
我认为“关闭外壳”是指关闭窗口
lxterminal
。交互lxterminal
和交互bash
通过伪 TTY 设备相互连接,这是一个内核级构造。bash
如果在其交互式子进程仍在运行时执行此操作,该lxterminal
进程将关闭其伪 TTY 一侧。这会导致内核向交互发送 SIGHUP 信号bash
。这信号的章节
man bash
说道:默认情况下,shell 在收到 SIGHUP 后退出。在退出之前,交互式 shell 会向所有正在运行或已停止的作业重新发送 SIGHUP。停止的作业将被发送 SIGCONT 以确保它们收到 SIGHUP。
为了防止 shell 将信号发送到特定作业,应使用
disown
内置命令将其从作业表中删除(请参阅下面的 shell 内置命令)或使用 标记为不接收 SIGHUPdisown -h
。SIGHUP 信号的预期含义是“与用户终端的连接丢失;如有必要,请保存所有未保存的工作,然后以有序方式退出”。 (对于守护进程,这不适用,因此对于它们来说,信号通常用于表示“重新读取配置文件”和/或“关闭并重新打开日志文件以进行日志轮换”。)
正如手册页所述,交互
bash
将重新向其所有子级发送 SIGHUP 信号(除非使用 另有说明disown
)。这就是导致您的后台evince
退出的原因。但当你使用脚本时,您的交互式 shell
fork()
会在新进程中运行一个 shell 来运行该脚本,然后运行该脚本的新 shellfork()
会再次运行evince
。之后,脚本结束,因此运行脚本的 shell 退出。此时,该
evince
进程将不再有父进程。但是当你看一个ps -ef
列表时,永远不会有 PPID 字段为空的进程:进程不可能没有父进程。因此,为了处理这种情况,内核将孤立evince
进程 PPID 分配为 1,使其成为该进程的收养子进程init
。现在,原始交互bash
不知道bash
运行脚本的实例确实启动了另一个进程:这种知识随着运行脚本的 shell 实例一起消失。因此,交互
bash
根本不知道有一个evince
进程可能需要在会话结束时向其发送 SIGHUP。您使用该脚本所取得的成果实际上是有意使用的技术的基本版本守护进程 过程,称为双叉。 (守护进程=使其完全独立于启动它的进程和会话。)
正如您所看到的,实际上,这足以使该
evince
过程与交互过程分开,bash
但仍将其保留为 GUI 登录会话的一部分。但如果你想要一个完整的守护进程,在第二个分支之前你应该执行一些其他步骤:- 确保标准输入、标准输出和标准错误被重定向到
/dev/null
- 如果任何其他文件描述符打开,请关闭它们
cd /
这样您的进程就不会意外地干扰系统管理员安装/卸载文件系统,除非该进程实际上需要访问特定文件系统中的文件- 明确设置
umask
为您想要/需要的值 - 如果
setsid
命令可用,您应该使用它来断开生成的进程与会话进程组的连接,并使其完全独立(即,evince &
您可以使用 ,而不是在脚本末尾exec setsid evince
)。
有关 bash shell 中守护进程的更详细说明,请参阅此处:http://blog.n01se.net/blog-n01se-net-p-145.html
- 确保标准输入、标准输出和标准错误被重定向到
答案2
杀死免费程序表明它可能在您的注销配置文件中。
使用脚本是有效的,因为运行后台任务的 shell 已经完成,您可以通过键入获得相同的效果。
( evince & )
这将启动一个子 shell,子 shell 背景显示然后退出。 evince 现在是一个守护进程。