12

我正在写一个迷你外壳(不,不是为了学校: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它永远不会返回,即使我传递了WUNTRACED标志,据我所知,它应该waitpid在该过程也停止了。
那么我可能做错了什么?还是我错误地理解了waitpid的行为?

注意:
-findForegroundProcessID() 返回正确的 pid;我仔细检查了那个。
- 我在我之后更改每个进程的组 - 处理工作正常- 如果我在我的外壳挂起后使用另一个终端发送 SIGCONT,子进程将恢复其工作,外壳最终会获得它fork
。 -我正在捕获 SIGTSTP,据我阅读(和测试)可以捕获它。- 我尝试使用 waitid 而不是 waitpid 以防万一,问题仍然存在。 编辑:Ctrl+C

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 处理程序。

4

2 回答 2

1

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

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

于 2012-12-26T00:55:17.620 回答
1

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

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

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

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

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

于 2012-12-26T09:45:56.107 回答