I am trying to understand processes in C. I currently want to create shell-like structure which - after pressing a shortcut like Ctrl+C or Ctrl+Z will kill all its subprocesses but will stay alive. My code looks like this:
#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>
pid_t pid;
void send_signal(int signum){
kill(pid, signum);
}
void init_signals(){
signal(SIGINT, send_signal);
signal(SIGTSTP, send_signal);
}
int main(){
init_signals();
pid = fork();
if(pid > 0){
//Parent Process
wait(NULL);
} else {
// Child Process
while(1){
usleep(300000);
}
}
return 0;
}
Problem here is that, when I press Ctrl+C, parent redirects it to child and kills it but when I press Ctrl+Z (even though child process is stopped) parent still hangs on wait(NULL)
. Any suggestions on how to fix this?
You can check here how to use wait in C . Long story short:
The wait system-call puts the process to sleep and waits for a child-process to end. It then fills in the argument with the exit code of the child-process (if the argument is not NULL).
wait
doesn't get signaled until the child process ends, so just by sending the child to sleep there is no reason for the main process to continue. If you want any setup where the main process still works while the child does as well (including when it sleeps!) you can't wait on the child.
Wouldn't make sense for a shell either - it's always active in the background. Instead you need a better handler on main - like waiting on a condition. That way, when sending a child to sleep, you can signal the condition and keep going.
Apart from the solution at https://stackoverflow.com/a/49346549/5694959 I would like to suggest one more solution as to handle signals for parent process only.This way parent will execute signal handler and default action will be performed for child process. Use waitpid()
to get the status of child.
waitpid(pid, NULL, WUNTRACED);
Now parent will resume its execution when child process changes its state i.e. either terminated or stopped.
Update your code as follows:
#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>
pid_t pid;
void send_signal(int signum){
kill(pid, signum);
}
void init_signals(){
signal(SIGINT, send_signal);
signal(SIGTSTP, send_signal);
}
int main(){
pid = fork();
if(pid > 0){
//Parent Process
init_signals();
waitpid(pid, NULL, WUNTRACED);
printf("I think this is what you are expecting...\n");
} else {
// Child Process
while(1){
usleep(300000);
}
}
return 0;
}
Just one thing to keep in mind that please make sure that parent process
has handled signal before you press ctrl + c or ctrl + z otherwise, default action of signal will be performed for parent as well.