【问题标题】:waitpid blocking when it shouldn'twaitpid 在不应该阻塞时阻塞
【发布时间】:2012-12-11 16:12:52
【问题描述】:

我正在编写一个 mini-shell(不,不是为了学校 :P;为了我自己的乐趣)并且大部分基本功能现在已经完成,但是在尝试处理 SIGTSTP 时我被卡住了。

假设当用户按下Ctrl+Z时,如果存在SIGTSTP应该发送到shell的Foreground进程,并且Shell应该正常继续。

创建每个进程后(如果是前台进程),下面的代码等待:

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,即使我通过了@,它也永远不会返回987654327@ 标志,据我了解,它也应该在进程停止时使waitpid 返回。
那么我可能做错了什么?还是我错误地理解了waitpid的行为?

注意事项:
-findForegroundProcessID() 返回正确的 pid;我仔细检查了。
-我在fork 之后立即更改每个进程的组
-处理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 处理程序。

【问题讨论】:

  • IIUC,您正在尝试捕获 SIGSTP。您无法捕获 SIGSTP 或 SIGKILL。
  • 我不确定 SIGSTOP 是否与 SIGSTP 相同,但根据一本书,SIGSTOP 无法被捕获,但 SIGTSTP 可以,事实上我确信我抓住了它,因为我确实打印了一些东西作为测试
  • 糟糕,我的错。如果我没看错的话,SIGTSTP 是故意创建的,以允许它被捕获,以允许进程组/终端组在其成员之间传播它。如果你所指的书是APUE,那你应该可以正确地做……
  • 您的 waitpid() 调用看起来不错,因此请测试您的其他假设。当在 waitpid() 中被阻止时,如果你从另一个终端“杀死 -TSTP”孩子会发生什么?如果你从另一个终端“杀死 -KILL”孩子会发生什么?
  • @fizzer 'kill -TSTP' 挂起 shell,再次发送 kill -CONT 让它继续正常工作(如,孩子恢复并且 shell 继续等待)。发送 kill -KILL 终止子进程,等待返回,shell 正常继续

标签: c linux shell signals wait


【解决方案1】:

SIGCHLD 是为停止的儿童提供的。信号处理程序中的waitpid() 调用 - 没有指定WUNTRACED - 永远阻塞。

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

【讨论】:

  • 我应该阅读更多关于 SIGCHLD 的内容,确实发现它是导致等待阻塞的原因,它不是 processCreation 中的等待,而是信号处理程序中的等待。非常感谢 =)
【解决方案2】:

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

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

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

现在,如果您的 Ctrl-Z 信号正在停止整个 shell,您需要执行以下操作。一旦你 fork 一个进程,在子进程中: - 使用 setpgid 在自己的组中设置进程。 - 通过阻止 SIGTTOU 给它一个健全的终端,然后使用 tcsetpgrp 给它一个终端。

在父级中: - 还使用 setpgid 设置其子进程。这是因为您不知道是孩子还是父母先执行,所以这避免了竞争条件。设置两次也无妨。

【讨论】:

  • 感谢您的回复,我正在设置一个 sigchld 处理程序(因此为什么处理 Ctrl+C 工作得很好)。这听起来有点奇怪,你是说我应该处理的唯一信号是 SIGCHLD?但是当孩子收到 SIGTSTP 信号时我没有得到 SIGCHLD 但我想我可能已经理解你的意思了。这个想法是让一个新进程拥有自己的终端并使其成为前台,因此当 Ctrl+Z 运行时,它只命中前台进程而不是我的 shell,而不完全处理 Ctrl+Z?
  • 我正在设置我的信号处理程序,正如我上面已经说过的,我现在确认子进程正在接收信号并且正在停止但等待仍然没有返回。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-12
  • 2013-10-07
  • 2013-06-03
  • 2014-05-25
  • 2023-01-10
  • 2016-07-04
  • 1970-01-01
相关资源
最近更新 更多