我有以下两个简单的程序。
家长:
package main
import (
"fmt"
"syscall"
)
func main() {
attr := &syscall.ProcAttr{
Files: []uintptr{0, 1, 2},
Sys: &syscall.SysProcAttr{ // child in its own group
Setpgid: true,
Pgid: 0,
},
}
_, err := syscall.ForkExec("./child/child", []string{"child"}, attr)
if err != nil {
fmt.Println("Error:", err)
return
}
}
孩子:
package main
import (
"fmt"
"time"
)
func main() {
for {
fmt.Println("hi from child")
time.Sleep(time.Second * 5)
}
}
输出ps
:
yakog@yakog-computer:~/goprojects/parent$ ps -o pid,ppid,pgid,uid,wchan,stat,tt,command -t /dev/pts/19
PID PPID PGID UID WCHAN STAT TT COMMAND
1867701 1867320 1867701 1000 do_sel Ss+ pts/19 bash
1870508 2118 1870508 1000 ep_pol Sl pts/19 child
当我按下CTRL-Z
或 时CTRL-C
,它没有任何效果。这正是我所期望的,因为进程 1870508 不是前台作业的一部分,并且CTRL-Z
/CTRL-C
调用kill -SIGTSTP -1867701
/ kill -SIGINT -1867701
。因此,1870508 不会收到这些信号。
另外,当我调用kill -SIGINT 1870508
或 时kill -SIGSTOP 1870508
,进程被终止/暂停。我仍然可以理解。虽然 1870508 不是前台作业的一部分,但使用kill
命令我们“直接”将信号发送给进程。
但是,为什么不起作用kill -SIGTSTP 1870508
?启动./parent
进程并调用kill -SIGTSTP 1870508
命令后,实际上什么都没有发生(1870508 仍然具有状态R
/S
并继续打印到终端)。我不明白为什么它没有暂停进程(将其移动到)。它应该与和T
相同(我们“直接”向进程发送 -SIGTSTP),但是,在这种情况下它没有效果。-SIGINT
-SIGSTOP
奇怪的是,如果我们改变父代码(下面的代码)并使其在子进程创建后继续执行,那么它就kill -SIGTSTP 1870508
可以正常工作(子进程被暂停)。
package main
import (
"fmt"
"os/signal"
"syscall"
"time"
)
func main() {
attr := &syscall.ProcAttr{
Files: []uintptr{0, 1, 2},
Sys: &syscall.SysProcAttr{ // child in its own group
Setpgid: true,
Pgid: 0,
},
}
_, err := syscall.ForkExec("./child/child", []string{"child"}, attr)
signal.Ignore(syscall.SIGTSTP)
if err != nil {
fmt.Println("Error:", err)
return
}
for {
fmt.Println("hi from parent")
time.Sleep(time.Second * 7)
}
}
此外,当我调用kill -SIGSTOP 1870508
(将进程移动到T
状态)然后调用kill -SIGINT 1870508
它时,它不会终止进程......为什么?
看
info libc 'Job Control Signals'
:或者POSIX 规范:
孤立进程组定义为:
并且您的 ID 为 1870508 的进程组符合条件,因为其唯一进程(1870508)以 2118 作为其父进程,可能是其父进程死亡后重新附加到的子收割机。
所有进程都会一直接收信号。它们不关心自己是在前台、后台还是已停止。
按键
ctrl-z
并向ctrl-c
前台进程发送信号。这并不意味着进程必须在前台才能接收信号,但这意味着如果进程不在前台,ctrl-c
则将信号发送给其他进程。因此行为并没有什么不同,是不同的进程获得了行为。前台总是有一些ctrl-c
进程。因此,如果您的目标进程在后台,并且您按下了ctrl-c,则 shell 会处理或忽略它,但不会退出。如果进程处于暂停
T
状态,它会收到信号。但是,进程可以捕获 SIGINT 并进行进一步处理,但如果进程处于暂停状态,信号将被放入其待处理队列中,当进程不再处于暂停状态时,它将进行该处理(然后可能退出)。在尝试向挂起的进程发送信号时,我注意到有些信号会使它们立即死亡,而有些信号则要求在信号生效之前恢复。进程中可能存在某些东西(例如库?)为这些信号设置了信号处理程序,以便在进程退出之前进行一些清理(例如刷新缓冲区)。