Understanding SIGCHLD when the child process terminates - c

I am not able to understand the output for the following program. I observed that after the child process returns, parent process is not sleeping for 3 sec before wait(). If SIGCHLD is set to default handler, then it sleeping for 3 sec, calling wait and returning as expected. What is exactly happening here ??
# include <unistd.h>
# include <sys/types.h>
# include <stdio.h>
# include <sys/wait.h>
# include <signal.h>
void handler(int sig) {
printf("Iam in handler ...\n");
}
main() {
int status;
pid_t pid;
struct sigaction act;
//act.sa_flags=SA_NOCLDSTOP;
act.sa_handler=handler;
sigaction(SIGCHLD,&act,NULL);
if(!fork()) {
printf("child process id is %d\n",getpid());
return 1;
}
printf("xxx ...\n");
sleep(3);
pid = wait(&status);
printf("process terminated is %d\n",pid);
}
output::
xxx ...
child process id is 2445
Iam in handler ...
process terminated is 2445

From the man for sleep():
sleep() makes the calling thread sleep until seconds seconds have elapsed or a signal arrives which is not ignored.
Your child terminating causes a signal to wake you up.
The return value from sleep():
Zero if the requested time has elapsed, or the number of seconds left to sleep, if the call was interrupted by a signal handler.
Can be used if you'd like to help you "finish" the sleep.
unsigned sleep_time = 3;
...
while((sleep_time = sleep(sleep_time)) > 0) {}
pid = wait(&status);
...

When the child process dies a SIGCHLD is sent to the parent. In your case it interrupts the sleep and it looks as if the process doesn't sleep.
The gist of the issue: sleep isn't restarted when interrupted by a signal.

Related

How can a parent-process find out if the child-process was terminated?

I want to write a program in which the parent creates exactly 1 child process. The child process should print its pid to the standard output and then finish. The parent process should waits until it is sure that the child has terminated. The parent terminates after it has waited for the child process.
Thats what I got so far:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int child;
child = fork();
if (child == 0)
{
printf("Child process has PID: %d. \n", getpid());
exit(0);
}
// how can the parent process find out it the child process was terminated successful?
printf("Child process terminated successfully\n");
return EXIT_SUCCESS;
}
How can the parent-process find out if the child process was terminated? I can't use wait() or waitpid() in this programm.
Thanks for your help!
When a child process terminates a SIGCHLD signal will be sent to the parent, by default the parent will ignore the SIGCHLD, however you can register a signal handler that will catch it.
You need to be careful what you do in the signal handler - quite a few standard function aren't safe to use.
The SIGCHLD approach turns up in code when the parent has it's own work to do and can't just wait for the child. If the parent just spawns children and then waits for them to finish the wait() and waitpid() are the best solution.
Finally if you don't call wait() or waitpid() you risk creating a zombie process, the child process expects it's parent to receive it's exit status through a call to one of these functions.
As I said in remark use the signal SIGCHLD, for instance :
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void handler(int sig)
{
pid_t chpid = wait(NULL);
/* WARNING : to show the call of the handler, do not do that
in a 'real' code, we are in a handler of a signal */
printf("Child pid %d ended (signal %s)\n", chpid, sig);
/* does 'something' to allow the parent to know chpid
terminated in a way compatible with parent requirement */
}
int main(void)
{
signal(SIGCHLD, handler);
if (!fork())
{
printf("Child pid is %d\n", getpid());
sleep(1);
return 0;
}
printf("Parent pid is %d\n", getpid());
getchar();
return 0;
}
Note when the signal arrive you have to call wait(NULL) but because by definition the child terminated the function returns immediately
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -pedantic -Wall -Wextra s.c
pi#raspberrypi:/tmp $ ./a.out
Parent pid is 21743
Child pid is 21744
Child pid 21744 ended (signal 17)
<enter>
pi#raspberrypi:/tmp $
Of course the signal 17 is SIGCHLD because it is the only one the program catches

How to set the paused process to background?

I am new in C. I am trying to make a shell - like program. I am currently making a signal handler, which means, when the process is running and somebody pressed ctrl + Z the process should pause and go to background while shell has to continue. The problem here is: parent process is making wait(NULL), but child is not ending the program so basically parent waits the child which is not ending the program yet. How to make it so that parent continues to work foreground. (you can see my code How to redirect signal to child process from parent process? here)
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.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
printf("PARENT: %d\n", getpid());
waitpid(pid, NULL, WUNTRACED);
printf("Parent out of wait, i think this is what you are expecting\n");
} else {
struct sigaction act = {{0}};
act.sa_handler = send_signal;
act.sa_flags = SA_RESETHAND;
sigaction(SIGTSTP, &act, NULL);
sigaction(SIGINT, &act, NULL);
perror("sigaction ");
printf("CHILD: %d\n", getpid());
// Child Process
while(1){
usleep(300000);
}
}
return 0;
}
I think above code can serve your purpose. Let me explain it.
In your code [How to redirect signal to child process from parent process? you have handled signal and from hander context sending same signal.When you pressed Ctrl + c or Ctrl + z both parent and child receives signal. Now as per the handler code
void send_signal(int signum) {
kill(pid, signum);
}
when handler will execute in parent's context pid will be equal to child's pid so it will send signal to child but when handler runs in child context pid value will be 0, so it sends signal to whole process group i.e. parent as well as child. this make you code to run handler recursively for infinite times. Due to this you are not getting desired result.
I have modified two things to get desired result.
child context
In child context restore the signal action to the default upon entry to the signal handler so that when child receives signal for second time signal default action can be performed.
parent context
use waitpid() instead of wait().
pid_t waitpid(pid_t pid, int *status, int options);
The waitpid() system call suspends execution of the calling process until a child specified by pid argument has changed state. By default, waitpid() waits only for terminated children, but this behavior is modifiable via the options argument.
`WUNTRACED` also return if a child has stopped
Due to WUNTRACED parent process will return when child will be stopped or terminated.
I hope it will serve you purpose ask me if it don't.

Child process still does not run after receiving signal

The code is one of the answers to this question.
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void
sigusr1( int pidno )
{
fprintf(stderr, "Caught\n");
}
int
main()
{
pid_t pid;
signal( SIGINT, sigusr1 );
if( (pid = fork()) == 0 ){
pause();
fprintf(stderr, "Child\n");
}
else
{
fprintf(stderr, "Parent\n");
kill( pid , SIGINT ); //parent sends signal to child
}
pause();
return 0;
}
The child waits for a signal, and after receiving it continues execution.
Running it I get
Parent
Caught
It seems that the child does not runs after receiving the signal. Pressing Ctrl+c:
Parent
Caught
^CCaught
Caught
Child
Can somebody explain me what is happening here?
pause() gets invoked after the signal has been received and handled by the child. Unfortunately, pause() easily introduces this kind of race condition:
time you parent child
---- --- ------ -----
0 signal(SIGINT,handler)
1 fork() (hello)
2 print("Parent") (waking up)
3 kill(..., SIGINT) <<SIGINT>> # RACE kill v pause
4 handler: print("Caught")
5 pause() pause()
6 ^C <<SIGINT>> <<SIGINT>>
7 handler: print("Caught") handler: print("Caught")
8 exit pause()
9 (still running)
To check what exactly goes on, try adding %i, getpid() to printfs, and maybe also one more printf() before the pause() call in the child branch.
sigsuspend() and explicit signal masking might be a better choice here.

pthreads and signal handling C ending early

This program is supposed to
The parent simply waits indefinitely for any child to return (hint, waitpid).
b. The child sets up two signal handlers (hint, signal) and goes to sleep for 5 minutes.
i. The first signal handler listens for the USR1 signal, and upon receiving it:
1. Creates a thread (hint, pthread_create).
a. Basically, all that the thread needs to do is “say hello” and sleep for 60
seconds.
ii. The second signal handler listens for the USR2 signal, and upon receiving it:
1. Destroys the thread (hint, pthread_cancel).
When this program receives the first signal to create the thread, it outputs
"[thread] sleeping for 1 m[thread] sleeping for 1 minute"
and then ends, it never waits for the 2nd signal, what am i doing wrong?
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
pthread_t thread;
void* temp()
{
printf("[thread] hello professor\n");
printf("[thread] sleeping for 1 minute\n");
sleep(60);
}
void handle_USR1(int x)
{
int s;
printf("[signal] creating the thread\n");
s = pthread_create(&thread, NULL, &temp, NULL);
}
void handle_USR2(int x)
{
int s;
printf("[signal] destroying the thread\n");
s = pthread_cancel(thread);
}
int main(void)
{
int status = 0;
if(fork() != 0)
{
printf("[parent] waiting.....\n");
waitpid(-1, &status, 0);
}
else
{
printf("[child] to create the thread: kill -USR1 %d\n", getpid());
printf("[child] to end the thread: kill -USR2 %d\n", getpid());
printf("[child] setting up signal handlers\n");
signal(SIGUSR1, handle_USR1);
signal(SIGUSR2, handle_USR2);
printf("[child] waiting for signals\n");
sleep(300);
}
return (0);
}
As Charlie Burns pointed out, both processes eventually exit as a consequence of the signal, but for different reasons.
Child
During its sleep, the child is blocked in a system call (the actual system call is nanosleep, used to implement the sleep() function). When a process receives a signal while in a system call, the corresponding signal handler is executed and the system call returns an error, EINTR, which means it has been interrupted and couldn't fulfill its duty. You can then decide if you want to restart the system call or not. Upon receiving SIGUSR1, the nanosleep system call executed by the child is interrupted, the handler is executed and sleep() returns immediately. Notice what man 3 sleep says about the return value of sleep():
Zero if the requested time has elapsed, or the number of seconds left to sleep, if the call was interrupted by a signal handler.
The correct way would be for the child to check for the return value of sleep (number of seconds left to sleep), and sleep again for that duration.
Parent
Unlike what Charlie Burns pointed out, waitpid() in the parent does not return because of the child receiving a signal. It returns because of the child exiting. It would return because of the child IF the child did not handle the signal, and thus was killed by it (an unhandled signal causes the process to die). You can (and should) check that using the WIFEXITED macro and its companions as described in man 2 waitpid. The example at the bottom of this man page is very good:
do {
w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
Basically, what this code does is wait on the child until it has exited normally or has exited because of an unhandled signal. In your case, it would be a good idea for the parent to check the status variable to make sure that waitpid returned because of the event it expects (a child exiting) and not something else.
Place a pthread_join after your pthread_create.
Ok, I see what is going on.
When you send a signal, without otherwise directing it to a particular thread by masking, any thread within a process can get it. When SIGUSR1 gets delivered main in the child gets blown out of the sleep and the main thread terminates killing the thread created in the handler.
There are plenty of questions here covering how to direct signals to a single thread and/or using sigaction to restart a system call if that is also a direction you want to go in to resolve it.

sigsuspend() does not react against signal

My target is to intercommunicate main process and its "fork" children.
Communication is done by signal delivery.
My problem appears when first child gets stuck waiting when waiting for SIGUSR1 signal.
I have no real idea why it gets stuck on that point. Evenmore if I sent signals by console, that child process seems not paying attention.
Could anybody help me?
Here comes the code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
int N = 5;
int _pipe[2];
pid_t children[5];
void main(){
pid_t parent_pid;
pid_t pid;
int i = 0;
sigset_t set;
sigfillset(&set);
parent_pid = getpid();
fprintf(stderr,"I am main process, here comes my pid %u\n",getpid());
if (0>pipe(_pipe)) fprintf(stderr,"Error when creating pipe");
//Start creating child processes
while (i < N){
pid = fork();
if (pid == 0){
close(_pipe[1]);
break;
}
else{
fprintf(stderr,"Created child with pid %u\n",pid);
children[i] = pid;
write(_pipe[1],&pid,sizeof(pid_t));
}
i = i+1;
}
i = 0;
// What main process does
if (pid>0){
close(_pipe[0]);
close(_pipe[1]);
sigdelset(&set,SIGUSR2);
sigdelset(&set,SIGTERM);
sigdelset(&set,SIGKILL);
// Main process sends signal to each child
while(i < N){
kill(children[i],SIGUSR1);
fprintf(stderr,"Sent SIGUSR1 to child %u\n",children[i]);
// .. Now just wait for SIGUSR2 arrival
sigsuspend(&set);
i = i+1;
}
}
// What children do
else{
// Wait for main process SIGUSR1 delivery
sigdelset(&set,SIGUSR1);
sigsuspend(&set);
fprintf(stderr, "SIGUSR1 arrived child %u from its father",getpid());
// Once SIGUSR1 has arrived, pipe is read N times
while((i < N) && (read(_pipe[0],&pid,sizeof(pid_t))>0)){
children[i] = pid;
i = i+1;
}
close(_pipe[0]);
// After reading pipe, a reply is sent to parent process
kill(parent_pid,SIGUSR2);
}
}
The problem most likely has to-do with the fact that the parent is sending the signals to the child processes immediately after it has forked them, and the child processes aren't blocking the signal. Thus by the time you call sigsuspend() in the child process, the signal has already been delivered to the child, and now it just sits there waiting for a signal that's never coming. You can quickly test this theory by placing a call to sleep() in the main process for a second or two before it starts sending signals. Keep in mind that as your code is structured right now, sigsuspend() won't work right without signal handlers for the signals you're waiting on ... so I suggest the following when working with signals like this:
In the parent process, block all the signals that you're planning on using for communication between the parent and child processes. You'll need to call sigprocmask() for this.
Have the parent fork the child processes
In the child processes simply call sigwait() using a signal set containing the blocked signals being used for communication ... you don't need sigsuspend() for what you're doing here.
After the parent process has sent the signals to the children, it too can call sigwait() to wait for the child process replies.
Here is an example of your code that does work: http://ideone.com/TRcqga

Resources