4

我正在尝试写一个shell。当前台进程运行时,分叉的进程管道会被赋予其自己的进程组 ID。然后将终端交给这个进程组 id(使用 tcsetpgrp),shell 等待它终止,然后再给自己终端控制权。这工作得很好。

出现的问题是当我尝试运行后台进程时。同样,我给管道中的所有进程一个进程组 ID,但这次我不给这个组终端控制。运行时,给定后台命令的输出将输出到终端(在执行完成之前),终端同时向用户返回提示。应该发生的是,尝试写入终端的子进程应该得到一个 SIGTTOU 并且它应该停止,但这显然不会发生。我验证了分叉的进程都具有相同的进程组 id,并且这个 id 与 shell 的不同。

在退出 shell(通过 ctrl-c)并返回到运行它的标准 bash shell 后,因为我没有在 shell 终止时获得后台进程,所以后台进程继续运行(除外)。奇怪的是,即使它不是前台进程,这个进程也会继续将输出写入 bash shell。这使我得出结论,要么此后台进程由于 POSIX 错误(不太可能)而没有获得任何 SIGTTOU,它正在处理它们(导致忽略停止的默认操作),或者后台进程正在忽略 SIGTTOU。

有没有办法在执行分叉进程之前确保它在收到 SIGTTOU 时停止(假设 exec 二进制文件没有改变任何东西)?

4

2 回答 2

3

SIGTTOU仅当为该终端设置了 termios 标志时,才会将其发送到尝试写入终端的后台进程TOSTOP。默认情况下一般不设置,这样后台进程就可以愉快的写入终端了。(该TOSTOP标志不影响读取权限。如果进程尝试读取,则会发送一个SIGTTIN.)

所以,是的,前台进程可以做一些事情:用于tcsetattr设置TOSTOP

于 2013-10-12T05:22:47.343 回答
1

解决方案是让分叉的进程在调用 exec 之前执行以下操作:

struct termios term; 
if (tcgetattr(STDIN_FILENO, &term) < 0)
        printf("ERROR\n");
    term.c_lflag = TOSTOP;
    if (tcsetattr(STDIN_FILENO,TCSANOW,&term)<0)
        printf("ERROR\n");
于 2013-10-12T05:50:23.357 回答