waitpid阻止它不应该

时间:2012-12-26 00:13:57

标签: c linux shell signals wait

我正在写一个迷你shell(不,不是为了学校:P;为了我自己的乐趣),大部分基本功能现在已经完成,但我在尝试处理SIGTSTP时遇到困难。

据说,当用户按下Ctrl+Z时,SIGTSTP应该发送到shell的Foreground进程(如果存在),Shell应该正常继续。

创建每个进程后(如果它是Foreground进程),以下代码等待:

if(waitpid(pid, &processReturnStatus, WUNTRACED)>0){//wait stopped too
    if(WIFEXITED(processReturnStatus) || WIFSIGNALED(processReturnStatus))
        removeFromJobList(pid);
}

我正在按如下方式处理信号:

void sigtstpHandler(int signum)
{
    signum++;//Just to remove gcc's warning
    pid_t pid = findForegroundProcessID();
    if(pid > -1){
        kill(-pid, SIGTSTP);//Sending to the whole group
    }
}

当我按Ctrl+Z时,子进程确实被挂起(使用ps -all来查看进程的状态)但我的shell挂起waitpid它从未发生过即使我通过了WUNTRACED标志也返回,据我所知,当进程停止时,该标志应该使waitpid返回。
那么我可能做错了什么呢?还是我理解waitpid的行为不正确?

注意:
-findForegroundProcessID()返回正确的pid;我仔细检查过。
- 我在fork之后立即改变了每个进程的组 -Handling Ctrl+C工作正常 - 如果我在shell挂起后使用另一个终端发送SIGCONT,子进程将恢复其工作,并且shell最终会重新获得它。
- 我正在捕捉SIGTSTP,只要我读到(并测试过)就可以捕获。 - 我尝试使用waitid而不是waitpid以防万一,问题仍然存在。 修改

void sigchldHandler(int signum)
{
    signum++;//Just to remove the warning
    pid_t pid;
    while((pid = waitpid(-1, &processReturnStatus, 0)) > 0){    
        removeFromJobList(pid);
    }
    if(errno != ECHILD)
        unixError("kill error");
}

我的SIGCHLD处理程序。

2 个答案:

答案 0 :(得分:1)

SIGCHLD是为已停止的孩子提供的。信号处理程序中的waitpid()调用 - 未指定WUNTRACED - 永远阻止。

您可能不应该在两个不同的地方进行removeFromJobList()处理。如果我不得不猜测,它听起来像触及全局数据结构,并且不属于信号处理程序。

答案 1 :(得分:1)

Waitpid没有返回,因为您没有设置sigchld处理程序(我之前发送给您)。你有孩子的过程没有收获。此外,waitpid需要在while循环中,而不是if(也是发送给你)。

您应该捕获的唯一信号是SIGCHLD。原因是如果您的进程正确分叉,内核会将该信号发送到前台进程,它将终止它或停止它或做任何正确的信号。

如果未正确设置进程组,信号将被发送到错误的进程。测试的一种方法是运行前台进程并按Ctrl-Z。如果整个shell都存在,那么Ctrl-Z信号将被发送到整个shell。这意味着您没有在新进程组中设置新进程并为其提供终端。

现在,如果您的Ctrl-Z信号停止整个shell,则需要执行此操作。在子进程中分叉进程后: - 使用setpgid在自己的组中设置进程。 - 通过阻止SIGTTOU然后使用tcsetpgrp给它终端,给它一个理智的终端。

在父母: - 还使用setpgid设置其子进程。这是因为你不知道孩子或父母是否会先执行,所以这可以避免竞争条件。设置两次并没有什么坏处。