你做的事情完全错误。
您不要将 SIGTSTP 发送给子进程,tty 会直接将 SIGTSTP 发送给子进程。
尝试
$ stty -a
speed 38400 baud; rows 55; columns 204; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
注意:
susp = ^Z;
告诉 tty 如何处理“CTRL-Z”,当 tty 获得 ^Z 时,它会向当前前台进程组中的所有进程发送 SIGTSTP 信号
如何处理进程组
在shell中启动新进程时,before execvX
,将新进程放入新进程组,然后调用tcsetpgrp
设置新进程组前台。所以任何未来的信号都会直接发送到子进程。如果孩子分叉新进程,他们将在同一个进程组中;所以当 ^Z 被按下时,整个进程组将被暂停。
pid = fork()
if (pid) {
// parent
setpgid(pid, pid); // put child into a new process group
int status;
wait(pid, &status, 0);
} else {
// child
pid = getpid();
setpgid(pid, pid);
if (isatty(0)) tcsetpgrp(0, pid);
if (isatty(1)) tcsetpgrp(1, pid);
if (isatty(2)) tcsetpgrp(2, pid);
execvX ...
}
一旦来自 tty 的任何信号导致子进程停止/终止/退出,您的 shell 将从中返回wait
,检查状态以了解子进程发生了什么。
防止你的shell停止
你的 shell 应该屏蔽 SIGTSTP 信号,因为 shell 不会挂起。您在开始时执行此操作,当您启动 shell 时。但别忘了 fork 会派生 sigmask,所以你需要在子进程中 fork 之后启用 SIGTSTP。