简体   繁体   中英

After suspending child process with SIGTSTP, shell not responding

I'm coding a basic shell in C, and I'm working on suspending a child process right now.

I think my signal handler is correct, and my child process is suspending, but after that, the terminal should return to the parent process and that's not happening.

The child is suspended, but my shell isn't registering any input or output anymore. tcsetpgrp() doesn't seem to be helping.

Here's my signal handler in my shell code for 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);
}

Can anyone tell me what I'm doing wrong?

Am I suspending my shell along with the child, and do I need to return stdin and stdout to the shell somehow? How would I do this?

Thanks!

It's an old question but still I think I found an answer.
You didn't write your parent's code but I'm assuming its looks something like:

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
}

the problem is with the wait() or waitpid(), it's look like if you run your program on OS like Ubuntu after using Ctrl + Z your child process is getting the SIGTSTP but the wait() function in the parent process is still waiting!

The right way of doing that is to replace the wait() in the parent with pause(), and make another handler that catch SIGCHLD. For example:

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

In this case after the child process receive Ctrl + Z the parent process also receive SIGCHLD and the pause() return.

i used folk with signals for make process pause and resume with ctrl+c

video while is running : link

Code:

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

i hope that's be useful.

please comment me if there's any questions

tcsetpgrp is to specify what is the foreground job. When your shell spawns a job in foreground (without & ), it should create a new process group and make that the foreground job (of the controlling terminal, not whatever's on STDIN). Then, upon pressing CTRL-Z, that job will get the TSTP. It's the terminal that suspends the job, not your shell. Your shell shouldn't trap TSTP or send TSTP to anyone.

It should just wait() for the job it has spawned and detect when it has been stopped (and claim back the foreground group and mark the job as suspended internally). Your fg command would make the job's pgid the foreground process group again and send a SIGCONT to it and wait for it again, while bg would just send the SIGCONT

I might be late to answer the question here but this is what worked when I was stuck with the same problem. According to the man pages for tcsetpgrp()

The function tcsetpgrp() makes the process group with process group ID pgrp the foreground process group on the terminal associated to fd, which must be the controlling terminal of the calling process, and still be associated with its session. Moreover, pgrp must be a (nonempty) process group belonging to the same session as the calling process.

If tcsetpgrp() is called by a member of a background process group in its session, and the calling process is not blocking or ignoring SIGTTOU, a SIGTTOU signal is sent to all members of this background process group.

So, what worked for me was ignoring the signal SIGTTOU in the shell program, before I created the processes that would come to the foreground. If I do not ignore this signal, then the kernel will send this signal to my shell program and suspend it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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