我只想在这里回答字面问题:进程背景本身,而不是 fork 本身,是否可以继续在子进程中执行并退出,以便等待它的进程可以继续执行这里已经被其他人覆盖的进程。
这里首先要注意术语。
后台处理通常是指交互式 shell 中的作业控制。
就像您运行带有&附加的命令一样。或者按Ctrl+Z然后运行bg。
那里的作业不是进程,它们是进程组。当你运行时:
$ ps -ej | grep -w "$$" & echo "$!"
35131
19152 19152 19152 pts/0 00:00:00 zsh
35130 35130 19152 pts/0 00:00:00 ps
35131 35130 19152 pts/0 00:00:00 grep
Run Code Online (Sandbox Code Playgroud)
这是放在后台的ps | grep 工作(这里是通过 35130 进程组实现的,其领导者是正在运行的进程,ps但也包含正在运行的进程grep)。
这里的背景意味着:
^C/ ^Z/ ^\, 等等。现在后台措辞有时在终端作业控制之外使用。
当你这样做时:
cmd1 | cmd2 & pid=$!
somecommand
wait
Run Code Online (Sandbox Code Playgroud)
在脚本中,没有作业控制。如果该脚本是通过交互式 shell 在终端中启动的,它本身将被置于前台或后台,具体取决于脚本的启动方式,就像任何其他命令一样。
但是cmd1 | cmd2脚本中的内容不会被解释脚本的外壳程序置于后台,因为它不是交互式外壳程序。
如果按^ C,cmd1并且cmd2将刚杀同旁边运行该脚本的外壳,并暂停当你按下^ Z。与其说cmd1 | cmd2是在后台启动,不如说它们是异步运行的。
与在交互式shell 中在后台启动的作业相比,只完成了1 个。没有创建进程组来运行该管道。
现在,随着这一点的澄清,流程可以将自己置于后台吗?
由于是作业而不是进程置于后台,因此该进程的问题是:
如果我们可以对所有这些都回答是,也就是说,如果我们从终端中的交互式 shell 作为简单命令(而不是管道或复合命令的一部分)被调用,那么我们需要(1)告诉等待shell 停止等待我们,(2) 告诉终端它的进程组不再是前台进程组,(3) 告诉 shell 更新其作业表以记录我们现在处于后台的事实。
除了终止或挂起之外,您无法真正告诉另一个进程停止等待您,在这种情况下,该进程将收到SIGCHLD信号或wait*()当前正在执行的调用将返回。
但是,您可以通过向自己发送 SIGTSTP 信号(按 时发送的相同^Z)或 SIGSTOP(无法拦截)来暂停自己,在这种情况下,所有 (1)、(2) 和 (3) 都会自动发生,除了作业状态将被暂停而不是在后台运行。
现在,由于您被暂停,您将不再跑步,也无法自行恢复。
但是,您可以分叉一个子进程,该进程将在暂停自己之前的一段时间内恢复自己(通过将 SIGCONT 发送到您的 pid)。
当您恢复执行时,您的 shell 将再次收到一个 SIGCHLD,并且(3)当它开始处理该信号时,shell 将意识到您现在正在后台运行。
举个例子,在sh:
$ sh -c 'echo running in foreground; sleep 1
(sleep 1; echo resuming my parent; kill -s CONT "$$") &
echo stopping; kill -s STOP "$$"
echo resumed
sleep 30
echo finished'; echo "$?"
running in foreground
stopping
147
zsh: suspended (signal) sh -c
$ resuming my parent
resumed
$ jobs
[1] + running sh -c
$ finished
[1] + done sh -c
Run Code Online (Sandbox Code Playgroud)
也可以使用kill(0, SIGSTOP)( kill -s STOP 0in sh)暂停整个工作,但是进程这样做是否正确,以影响它尚未启动且不知道的进程的运行流程?
sh -c 'echo running in foreground
perl -MPOSIX -le "setpgid 0,0; # leave the process group before it is suspended
sleep 2;
print q(resuming the process group of my parent);
kill q(CONT), - shift@ARGV
" "$(ps -o pgid= -p "$$")" &
sleep 1
echo stopping my process group; kill -s STOP 0
echo process group resumed
sleep 30
echo finished' | cat
Run Code Online (Sandbox Code Playgroud)
只是有时。
Ángel 回答中的建议已过时且糟糕。所谓的“守护进程”在登录会话中并不真正起作用。系统为了建立登录会话而通过的单向活板门太多了;Ángel 的回答不仅忽略了 OpenBSD 的setlogin()、AIX 的受保护环境(请参阅 参考资料setsenv)以及 Linux 的安全上下文和控制组等内容,而且daemon()几乎所有 C 库中的旧库函数也是如此。
它自 1980 年代以来就没有用过;因为 1990 年代引入了许多这种单向活板门。“恶魔化”是一个谬论,不幸的是,这些年来,人们仍然通过接受的智慧和民间传说推广。
这甚至不是守护进程应该做的。1980 年代后期(例如 AT&T Unix 服务访问设施)和 1990 年代(例如 IBM 的系统资源控制器)以后的服务管理子系统调用已经在守护进程上下文中的守护进程。它们首先不是在登录会话上下文中启动的。
此外,fork-and-exit-parent 与服务管理子系统在 30 多年来一直用于守护进程的控制语义发生冲突;关闭文件描述符与服务管理子系统为守护进程设置的日志记录机制冲突。
/etc/rc或/etc/rc.localshell 脚本,甚至从超级用户登录会话中交互地启动守护进程,并破坏了服务管理子系统使用它从fork()ing获得的进程 ID 的事实跟踪和控制服务的服务流程。(正如您从进一步阅读中看到的,IBM 几乎在其系统资源控制器存在的时候就一直建议不要这样做。)服务管理子系统在已经没有控制终端的会话中调用守护进程。此外,“守护进程”并不是在登录会话的后台执行的操作。“作业控制”shell 通过调用tcsetpgrp()函数来更改控制终端的当前前台进程组 ID值,从而将登录会话中的内容放入前台和后台。这实际上与fork(),chdir()关闭文件描述符或(内核)会话没有任何关系。
shell 的子进程可以调用tcsetpgrp(),但请注意,(a)SIGTTOU如果它已经在后台,它将被发送一个信号,并且 (b) 找到另一个进程组 ID 来切换到,作为 shell 是很重要的在所有情况下可能不是直接父进程。
&机制来执行此操作。tcsetpgrp. 系统接口。单一 UNIX 规范。IEEE 1003.1。2018. 开放组。service。小吃页。JdeBP 的软件。