繁体   English   中英

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

[英]After suspending child process with SIGTSTP, shell not responding

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

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

这个孩子被暂停了,但是我的外壳不再注册任何输入或输出了。 tcsetpgrp()似乎没有帮助。

这是我的SIGTSTP外壳代码中的信号处理程序:

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? 我该怎么做?

谢谢!

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

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()。

我使用带有信号的民谣使进程暂停并使用ctrl + c恢复

正在运行的视频: 链接

码:

#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;
    }
}

我希望这会有所帮助。

如有任何疑问,请评论我

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

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

我在这里回答这个问题可能太迟了,但是当我遇到同样的问题时,这才起作用。 根据tcsetpgrp()的手册页

函数tcsetpgrp()使具有进程组ID pgrp的进程组成为与fd关联的终端上的前台进程组,fd必须是调用进程的控制终端,并且仍与其会话关联。 而且,pgrp必须是与调用进程属于同一会话的(非空)进程组。

如果tcsetpgrp()在其会话中由后台进程组的成员调用,并且该调用进程没有阻塞或忽略SIGTTOU,则SIGTTOU信号将发送到该后台进程组的所有成员。

因此,在我创建将要出现的进程之前,对我SIGTTOU是忽略shell程序中的信号SIGTTOU 如果我不忽略此信号,则内核会将此信号发送到我的Shell程序并将其挂起。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM