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

jdg4fx2g  于 2023-02-09  发布在  Shell
关注(0)|答案(4)|浏览(334)

我正在用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);
}

有人能告诉我我做错了什么吗?
我是否将shell与子进程一起挂起,是否需要以某种方式将stdin和stdout返回到shell?我将如何做到这一点?
谢谢!

py49o6xq

py49o6xq1#

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

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()上,看起来像是如果你在Ubuntu这样的操作系统上运行你的程序,在使用Ctrl+Z之后,你的子进程正在获得SIGTSTP,但是父进程中的wait()函数仍然在等待!
正确的方法是用pause()替换父进程中的wait(),并创建另一个捕捉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()返回。

rn0zuynd

rn0zuynd2#

tcsetpgrp是指定什么是前台作业。(没有&),它应该创建一个新的进程组,并使其成为前台作业(控制终端的,而不是STDIN上的任何东西)。然后,按下CTRL-Z,该作业将获得TSTP。它是挂起作业的终端,而不是您的shell。您的shell不应该捕获TSTP或将TSTP发送给任何人。
它应该只为它所派生的作业提供wait(),并检测它何时停止(收回前台组并将作业标记为内部挂起)。您的fg命令将使作业的pgid再次成为前台进程组,并向它发送SIGCONT,然后再次等待它,而bg将只发送SIGCONT

k2fxgqgv

k2fxgqgv3#

我用folk与信号为使进程暂停和恢复与ctrl + c
运行时的视频:link
代码:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void reverse_handler(int sig);
_Bool isPause=0;
_Bool isRunning=1;

int main()
{
    int ppid;
    int counter=0;
    //make parent respond for ctrl+c (pause,resume).
    signal(SIGINT,reverse_handler);
    while(isRunning){
        while(isPause==0)
        {
            /*code exec while process is resuming */
            printf("\nc:%d",counter++);
            fflush(stdout);
            sleep(1);
        }
        //close parent after child is alive.
        if((ppid=fork())==0){   exit(0);    }
        //make child respond for ctrl+c (pause,resume).
        signal(SIGINT,reverse_handler);
        //keep child alive and listening.
        while(isPause==1){ /*code exec while process is pausing */  sleep(1);   }
    }
    return 0;
}
//if process is pause made it resume and vice versa.
void reverse_handler(int sig){
    if(isPause==0){
        printf("\nPaused");
        fflush(stdout);
        isPause=1;
    }
    else if(isPause==1){
        printf("\nresuming");
        fflush(stdout);
        isPause=0;
    }
}

我希望这有用。
如果有任何问题请告诉我

owfi6suc

owfi6suc4#

在这里回答这个问题可能会晚一些,但是当我遇到同样的问题时,这是行之有效的方法。
函数tcsetpgrp()使进程组ID为pgrp的进程组成为fd关联的终端上的前台进程组,fd必须是调用进程的控制端,并且还与其会话关联,并且pgrp必须是与调用进程属于同一会话的(非空)进程组。
如果tcsetpgrp()被后台进程组的成员在其会话中调用,并且调用进程没有阻塞或忽略SIGTTOU,则SIGTTOU信号将发送到此后台进程组的所有成员。
所以,在我创建进程之前,忽略shell程序中的SIGTTOU信号,如果我不忽略这个信号,内核就会发送这个信号到shell程序并挂起它。

相关问题