1

I am implementing my own shell, which requests pressing CTRL-Z to suspend foreground process. In order to not suspend my main process but only the child process, I have to catch the SIGTSTP by a handler in my main process and redirect it to my child process(which needed stop). But the kill function never return.

#include <ctype.h> #include <errno.h> #include <stdbool.h> #include <stdio.h> #include <readline/readline.h> #include <readline/history.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <signal.h> #include <sys/wait.h> #include <termios.h> #include <unistd.h> #include <dirent.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/signal.h> #include <sys/mman.h> void handler(){ pid_t pid; int status; while((pid = waitpid(-1, &status, 0))>0){ printf("pid: %d has been reaped\n", pid); } } void send_signal(int signum){ kill(pid, signum); } void init_signals(){ //signal(SIGINT, send_signal); signal(SIGCHLD, handler); signal(SIGTSTP, send_signal); } void start_foreground_job(char** argv) { if( (fork()) ==0) { signal(SIGTSTP, SIG_DFL); if((execve(argv[0], argv, NULL))<0)//envp { fprintf(stderr, "%s: Command not found.\n", argv[0]); exit(0); } } pause(); return; } int main(){ init_signals(); char* argv[] ={"/bin/sleep", "10", NULL}; start_foreground_job(argv); printf("This line is what I expected after pressing CTRL-Z\n"); return 0; } 
3
  • printf isn't async-signal-safe, so you can't call it from a signal handler. Commented Apr 22, 2020 at 0:06
  • You should show the code you do for signal/sigaction. Since you have to do one in the calling program's parent process before you fork, after the fork [in the child] before doing execvp, you may want to reset the signal to its default action (e.g.) signal(SIGTSTP,SIG_DFL); Commented Apr 22, 2020 at 0:36
  • @Craig Estey It still doesn't work in the way you said, can you take a look my code? Commented Apr 22, 2020 at 1:25

1 Answer 1

1

When I try your code on my environment, the result is the same with you. I check son process's state after Ctrl+Z,

Before Ctrl + Z

ps -aux | grep sleep 16586 0.0 0.0 2016 412 pts/1 S+ 10:42 0:00 /bin/sleep 10 

After Ctrl + Z

ps -aux | grep sleep 16586 0.0 0.0 2016 412 pts/1 T+ 10:42 0:00 /bin/sleep 10 

Son process's state has changed from S -> T. It means father process has sent signal to son process successfully .

But son process doesnot exit, so your waitpid(-1, &status, 0) cannot return.

Option1: send kill signal to son process, and waitpid will get one return.

void send_signal(int signum) { if (pid) { printf("kill %d, signum %d\n", pid, signum); //kill(pid, signum); kill(pid, SIGKILL ); } } 

Option2: waitpid api has one option which can wait for stop state:

void handler() { pid_t pid; int status; while ((pid = waitpid(-1, &status, WSTOPPED)) > 0) { printf("pid: %d has been reaped\n", pid); break; } } 

Please check below sw version, it works on my workstation.

int pid = 0; void handler() { pid_t pid; int status; while ((pid = waitpid(-1, &status, WSTOPPED)) > 0) { printf("pid: %d has been reaped\n", pid); break; } } void send_signal(int signum) { if (pid) { printf("kill %d, signum %d\n", pid, signum); kill(pid, signum); //kill(pid, SIGKILL ); } } void init_signals() { signal(SIGCHLD, handler); signal(SIGTSTP, send_signal); } void start_foreground_job(char** argv) { pid = fork(); if (pid == 0) { signal(SIGTSTP, SIG_DFL); if ((execve(argv[0], argv, NULL)) < 0) { //envp fprintf(stderr, "%s: Command not found.\n", argv[0]); exit(0); } } printf("son process pid is %d\n", pid); pause(); return; } int main() { init_signals(); char* argv[] = {"/bin/sleep", "10", NULL}; start_foreground_job(argv); printf("This line is what I expected after pressing CTRL-Z\n"); return 0; } 

Below is my test result:

./temp son process pid is 29249 ^Zkill 29249, signum 20 pid: 29249 has been reaped This line is what I expected after pressing CTRL-Z 
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks! I replacing WUNTRACED to WSTOPPED in my code, it works for me. what the difference between these two options?
I ran your code on my machine, but it seems there still a little be different, this is my output
son process pid is 47780 ^Zkill 47780, signum 18 This line is what I expected after pressing CTRL-Z
I check your raw source code, you call "waitpid(-1, &status, 0)". In my Centos workstation, both WUNTRACED and WSTOPPED can work well. In linux kernel 4.4, '#define WSTOPPED WUNTRACED', I think both should be OK. If WUNTRACED is not Ok on your work station, I suggest to re-edit your question for this. I could check again.
printf should be in signal handle. What I provide you is just one temporary version. I think maybe the difference comes from gcc build system. For this case, two key point: 1. father process can send signle to son process. 2. Father process can check son process's state change.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.