After pausing a child process using SIGTSTP, the shell does not respond

I am coding a base shell in C and am currently working on pausing a child process.

I think that my signal handler is correct, and my child process is paused, but after that the terminal should return to the parent process and this will not happen.

The child is paused, but my shell no longer registers any inputs or outputs. tcsetpgrp () doesn't seem to help.

Here is 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 someone tell me what I am doing wrong?

Am I pausing my shell along with a child, and do I need to somehow return stdin and stdout to the shell? How can I do it?

Thanks!

+6
source share
4 answers

This is an old question, but still I think I found the answer.
You did not specify your parent code, but I assume that it looks something like this:

 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 wait () or waitpid (), it seems that if you run your program on an OS, for example, Ubuntu after using Ctrl + Z , your child process receives SIGTSTP, but the wait () function in the parent process is still waiting!

The proper way to do this is to replace wait () in the parent with pause () and make another handler that will catch SIGCHLD. For instance:

 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 receiving the child process Ctrl + Z, the parent process will also receive SIGCHLD and return pause ().

+4
source

tcsetpgrp - indicate what is the foreground job. When your shell launches a task in the foreground (without & ), it should create a new group of processes and do this front task (of the control terminal, not STDIN). Then, by pressing CTRL-Z, this job will get TSTP. This is a terminal that pauses, not your shell. Your shell should not catch TSTP or send TSTP to anyone.

It should just wait() to complete the task that it generated and determine when it was stopped (and return the foreground group and mark the task as suspended inside). Your fg team will run pgid again for the foreground process group and send it SIGCONT and wait for it again, and bg just send SIGCONT

0
source

I may be late to answer this question, but this is what worked when I came across the same problem. According to the man pages for tcsetpgrp ()

The tcsetpgrp () function makes the process group with the process group identifier pgrp the foreground process group on the terminal associated with fd, which must be the control terminal of the calling process and still associated with its session. In addition, 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 does not block or ignore SIGTTOU, the SIGTTOU signal is sent to all participants in this background process group.

So, what worked for me is to ignore the SIGTTOU signal in the shell program before I create processes that come to the fore. If I do not ignore this signal, the kernel will send this signal to my shell program and pause it.

0
source

I used a folk with signals to stop the process and resume using Ctrl + C

the video is still working: link

The 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 this will be helpful.

please comment me if you have any questions

0
source

Source: https://habr.com/ru/post/926641/


All Articles