使用SIGTSTP挂起子进程后,shell没有响应

使用SIGTSTP挂起子进程后,shell没有响应,shell,process,c,signals,Shell,Process,C,Signals,我正在用C编写一个基本的shell,现在正在暂停一个子进程 我认为我的信号处理程序是正确的,我的子进程是挂起的,但是在那之后,终端应该返回到父进程,而这不会发生 孩子被挂起,但我的shell不再注册任何输入或输出。tcsetpgrp()似乎没有帮助 以下是我的SIGTSTP shell代码中的信号处理程序: void suspend(int sig) { pid_t pid; sigset_t mask; //mpid is the pgid of this shell.

我正在用C编写一个基本的shell,现在正在暂停一个子进程

我认为我的信号处理程序是正确的,我的子进程是挂起的,但是在那之后,终端应该返回到父进程,而这不会发生

孩子被挂起,但我的shell不再注册任何输入或输出。tcsetpgrp()似乎没有帮助

以下是我的SIGTSTP shell代码中的信号处理程序:

void suspend(int sig) {
    pid_t pid;
    sigset_t mask;
    //mpid is the pgid of this shell.
    tcsetpgrp(STDIN_FILENO, mpid);
    tcsetpgrp(STDOUT_FILENO, mpid);
    sigemptyset(&mask);
    sigaddset(&mask, SIGTSTP);
    sigprocmask(SIG_UNBLOCK, &mask, NULL);
    signal(SIGTSTP, SIG_DFL);
    //active.pid is the pid of the child currently in the fg.
    if (active.pid != 0) {
        kill(active.pid, SIGTSTP);
    }
    else{
        //if this code is being run in the child, child calls SIGTSTP on itself.
        pid = getpid();
        if (pid != 0 && pid != mpid){
            kill(pid, SIGTSTP);
        }
    }
    signal(SIGTSTP, suspend);
}
谁能告诉我我做错了什么

我是否将我的外壳与孩子一起悬挂,是否需要以某种方式将stdin和stdout返回外壳?我该怎么做


谢谢

tcsetpgrp
用于指定什么是前台作业。当您的shell在前台生成作业(没有
&
)时,它应该创建一个新的进程组,并将其作为前台作业(控制终端的作业,而不是STDIN上的任何作业)。然后,在按下CTRL-Z时,该作业将获得TSTP。是终端暂停了工作,而不是你的外壳。您的shell不应该捕获TSTP或将TSTP发送给任何人


它应该只
wait()
等待它生成的作业,并在停止时检测它(并收回前台组并在内部将作业标记为挂起)。您的
fg
命令将使作业的pgid再次成为前台进程组,并向其发送
SIGCONT
并再次等待,而
bg
将只发送
SIGCONT

tcsetpgrp
来指定什么是前台作业。当您的shell在前台生成作业(没有
&
)时,它应该创建一个新的进程组,并将其作为前台作业(控制终端的作业,而不是STDIN上的任何作业)。然后,在按下CTRL-Z时,该作业将获得TSTP。是终端暂停了工作,而不是你的外壳。您的shell不应该捕获TSTP或将TSTP发送给任何人


它应该只
wait()
等待它生成的作业,并在停止时检测它(并收回前台组并在内部将作业标记为挂起)。您的
fg
命令将使作业的pgid再次成为前台进程组,并向其发送
SIGCONT
并再次等待,而
bg
将只发送
SIGCONT

这是一个老问题,但我仍然认为我找到了答案。
您没有编写家长的代码,但我假设它看起来像:

int main(){ 
     pid_t pid = fork();
     if(pid == 0){ //child process
        //call some program
     else //parent process
        wait(&status); //or waitpid(pid, &status, 0)
        //continue with the program
}
问题在于wait()或waitpid(),如果您在使用Ctrl+Z后在类似Ubuntu的操作系统上运行程序,您的子进程将获得SIGTSTP,但父进程中的wait()函数仍在等待

正确的方法是将父级中的wait()替换为pause(),并生成另一个捕获SIGCHLD的处理程序。例如:

void sigHandler(int signum){
     switch(signum){
        case SIGCHLD:
             // note that the last argument is important for the wait to work
             waitpid(-1, &status, WNOHANG);
             break;
     }
}

在这种情况下,在子进程接收Ctrl+Z之后,父进程也会接收SIGCHLD和pause()返回。

这是一个老问题,但我仍然认为我找到了答案。
您没有编写家长的代码,但我假设它看起来像:

int main(){ 
     pid_t pid = fork();
     if(pid == 0){ //child process
        //call some program
     else //parent process
        wait(&status); //or waitpid(pid, &status, 0)
        //continue with the program
}
问题在于wait()或waitpid(),如果您在使用Ctrl+Z后在类似Ubuntu的操作系统上运行程序,您的子进程将获得SIGTSTP,但父进程中的wait()函数仍在等待

正确的方法是将父级中的wait()替换为pause(),并生成另一个捕获SIGCHLD的处理程序。例如:

void sigHandler(int signum){
     switch(signum){
        case SIGCHLD:
             // note that the last argument is important for the wait to work
             waitpid(-1, &status, WNOHANG);
             break;
     }
}

在这种情况下,在子进程接收到Ctrl+Z之后,父进程也会接收到SIGCHLD和pause()返回。

我在这里回答这个问题可能会迟到,但当我遇到同样的问题时,这就是有效的方法。根据tcsetpgrp()的手册页

函数tcsetpgrp()使进程组具有进程组ID pgrp与fd关联的终端上的前台进程组, 必须是呼叫过程的控制终端,以及 仍将与其会话关联。此外,pgrp必须是一个 (非空)与调用进程属于同一会话的进程组 过程

如果中后台进程组的成员调用了tcsetpgrp() 它的会话,并且调用进程没有阻塞或忽略 SIGTTOU,一个SIGTTOU信号被发送给这个背景的所有成员 进程组


因此,对我来说,在我创建将出现在前台的进程之前,忽略shell程序中的信号
sigttoo
。如果我不忽略这个信号,那么内核将把这个信号发送到我的shell程序并挂起它。

我在这里回答这个问题可能会迟到,但当我遇到同样的问题时,这就是有效的方法。根据tcsetpgrp()的手册页

函数tcsetpgrp()使进程组具有进程组ID pgrp与fd关联的终端上的前台进程组, 必须是呼叫过程的控制终端,以及 仍将与其会话关联。此外,pgrp必须是一个 (非空)与调用进程属于同一会话的进程组 过程

如果中后台进程组的成员调用了tcsetpgrp() 它的会话,并且调用进程没有阻塞或忽略 SIGTTOU,一个SIGTTOU信号被发送给这个背景的所有成员 进程组


因此,对我来说,在我创建将出现在前台的进程之前,忽略shell程序中的信号
sigttoo
。如果我不忽略这个信号,那么内核将把这个信号发送到我的shell程序并挂起它。

我使用folk with signals使进程暂停,并用ctrl+c恢复

正在运行时的视频:

代码:

#包括
#包括
#包括
无效反向处理器(int sig);
_Bool-isPause=0;
_Bool-isRunning=1;
int main()