C: Fork and signals - c

What I want to do is create a parent process that lasts for 5 seconds. I also want it to send a SIGUSR1 signal every second. On this signal I want the child to do something.
The code that I put together so far is:
void alarmHandler(int sig) {
printf("\nalarm called\n");
exit(0);
}
void childHandler(int sig) {
printf("child called");
signal(SIGUSR1, childHandler);
}
int main() {
pid_t val;
if((val = fork())) { //parinte
signal(SIGALRM, alarmHandler);
printf("parent");
alarm(5);
while(1) {
kill(val, SIGUSR1);
sleep(1);
}
}else {
signal(SIGUSR1, childHandler);
printf("child");
}
return 0;
}
What I get is:
child
parent
alarm called
What I want:
child
parent
child called
child called
child called
child called
child called
alarm called

Your parent has the while loop. The child does the following:
signal(SIGUSR1, childHandler);
printf("child");
And then exits.
If it does receive SIGUSR before the exit, this will also be executed
printf("child called");
signal(SIGUSR1, childHandler);
Therefore you have a race condition as the number of child called is printed.
Just put a while (1) {} after the printf("child");

Base on the original code, add two blocks:
flush the printf: setbuf(stdout, NULL);
keep the child running: while (1) pause();
The code list as follow:
#include "stdio.h"
#include "stdlib.h"
#include <signal.h>
/* For a real-world program, printing from a signal handler is not very safe.
* A signal handler should do as little as it can, preferably only setting a flag here or there.
* And the flag should be declared `volatile`.
* Real-world example:
* I once worked on a system that used an Access database as a back end,
* and under certain circumstances a printf() call in a signal handler would write to the .mdb file instead of stdout,
* hosing the database beyond repair.
*/
void alarmHandler(int sig)
{
printf("\nparent signal alarm handler: times up\n");
exit(0);
}
void childHandler(int sig)
{
printf("\nchild signal handler\n");
// The old style should install the handler again.
signal(SIGUSR1, childHandler);
}
int main()
{
pid_t val;
signal(SIGALRM, alarmHandler);
// If not set this, we cann't the child's output.
// The stdout stream is buffered, so will only display what's in the buffer after it reaches a newline (or when it's told to).
setbuf(stdout, NULL);
if ((val = fork())) { //parinte
printf("\nparent\n");
// #Note that there is only one alarm clock per process.
// the alarm() function will return a value if another alarm has been previously set.
//
// sets a timer that generates the SIGALARM signal when times up.
// If we ignore or don’t catch this signal, the process is terminated.
alarm(5);
while(1) {
sleep(1);
kill(val, SIGUSR1);
}
printf("\nparent exit\n");
} else {
signal(SIGUSR1, childHandler);
printf("\nchild\n");
while (1)
pause(); // The pause() function suspends the process until a signal is caught.
printf("\nchild exit\n");
}
return 0;
}
And the output:
parent
child
child signal handler
child signal handler
child signal handler
child signal handler
parent signal alarm handler: times up

Related

Ubuntu C How to stop child process using SIGTSTP then resume it using SIGCONT?

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int pid = 0;
// some very time-consuming function
void childLabor() {
for (long long i=1;i<=10000000000;i++) {
//printf("i'm printing\n");
fflush(stdout);
}
}
// stop the process who calls this
void stopYourself() {
// TODO
}
void childReceiveStop() {
signal(SIGTSTP, childReceiveStop);
printf("I have important things to do first before stopping\n");
fflush(stdout);
// do important things
printf("I stop myself now\n");
fflush(stdout);
stopYourself();
}
void childReceiveContinue() {
signal(SIGCONT, childReceiveContinue);
}
int main()
{
pid = fork();
if (pid==0) {
signal(SIGTSTP, childReceiveStop);
signal(SIGCONT, childReceiveContinue);
stopYourself(); // wait until parent sends SIGCONT
childLabor();
}
else {
// start/stop child every 2 second
kill(pid,SIGCONT);
for (int i=1; i<=100; i++) {
printf("sending signal stop\n");
fflush(stdout);
kill(pid, SIGTSTP);
sleep(3);
printf("sending signal start\n");
kill(pid, SIGCONT);
sleep(1);
}
}
return 0;
}
Basically what I want to do in this example is let the child print for 3 seconds, then stop it, then let it print again, ... When the child receives SIGTSTP, it should stop. And when it receives SIGCONT, it should continue.
However, with or without handler, when the child process receives the SIGTSTP signal, it does not stop at all.
How can I fix this problem? Thank you.
SIGTSTP for sending stop signal from the terminal to a process. In your case, you ant to send stop signal from parent process. So you'd need SIGSTOP instead of SIGTSTP. So replace SIGTSTP with SIGSTOP.
Also SIGSTOP can't caught. So you don't need to have handlers for SIGSTOP.

C Can't signal a child process to continue after `sigsuspend`

I'm trying to create a program where a process forks, creating a child process, and the parent must always finish printing to the screen before the child is finished printing to the screen, no matter what. I also wish to accomplish this using signals instead of pipelining.
It is a similar problem to the question asked here: Explanation of sigsuspend needed
I understand that kill(pid,signal); is to send a signal to that pid and tell it to finish executing and terminate.
The problem is, when it executes, the child doesn't print after the suspend. Heres the code:
int main(void){
pid_t pid;
int i;
pid = fork();
if(pid==0){
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask,SIGUSR1);
printf("This is the child Process id = %d \n",getpid());
sigsuspend(&mask);
printf("The child is now complete \n");
}
else{
printf("This is the parentProcess id = %d \n",getpid());
printf("The parentProcess is complete\n");
sleep(1);
int j = kill(pid,SIGUSR1);
if (j!=0)
{
perror(NULL);
}
exit(0);
}
}
I have managed to accomplish my task (printing the parent before the child) by using a global variable int x = 0; and a signal handler method void handle1(int s){x = 1;}before the main. In the main I added signal(SIGUSR1,handle1); In the child I removed all the sigset and sigsuspend lines and instead wrote while(x==0){/*do_nothing*/} 1 line before the printf statement. So when the parent executes kill(pid,SIGUSR1) the signal handler which is inherited by the child process also gets executed and sets x=1. So the child now leaves the while loop and can print it's statement.
However I believe it would be helpful to know how to accomplish this task using sigmask_t and sigsuspend() but i cant get it to work that way.
There are 3 problems in your code:
SIGUSR1 is the signal you want to deliver to the child. You can't use sigaddset(&mask,SIGUSR1);, it does exactly the opposite of your intention.
According to POSIX standard sigsuspend() below, you should install a signal handler for SIGUSR1 to make sigsuspend() continue the following code, since the default behavior of SIGUSR1 is termination.
The sigsuspend() function shall replace the current signal mask of the calling thread with the set of signals pointed to by sigmask and then suspend the thread until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process.
It would be better if you collect the child from the parent, otherwise there is a race condition.
The code below will work:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void handler(int sig) {}
int main(void){
pid_t pid;
int i;
pid = fork();
signal(SIGUSR1, handler);
if(pid==0){
sigset_t mask;
sigemptyset(&mask);
//sigaddset(&mask,SIGUSR1);
printf("This is the child Process id = %d \n",getpid());
sigsuspend(&mask);
printf("The child is now complete \n");
}
else{
printf("This is the parentProcess id = %d \n",getpid());
printf("The parentProcess is complete\n");
sleep(1);
int j = kill(pid,SIGUSR1);
if (j!=0)
{
perror(NULL);
}
wait(NULL);
exit(0);
}
}
You have a few issues.
Your parent process should wait for the child to complete. This allows for diagnostics (such as properly waiting for the child to print), but is otherwise a bookkeeping task that is a good habit even when the waiting process will just exit:
printf("This is the parentProcess id = %d \n",getpid());
printf("The parentProcess is complete\n");
sleep(1);
int j = kill(pid,SIGUSR1);
if (j!=0)
{
perror(NULL);
exit(0);
}
waitpid(pid, NULL, 0);
exit(0);
Now, you have set SIGUSR1 in your mask to sigsuspend(), which causes the signal to be ignored. This is now more obvious once the parent is made to wait, because the parent will never exit. So, remove the line of code that sets SIGUSR1.
Finally, the default handler for SIGUSR1 will simply cause the process to exit, and so the printf will not get a chance to print. If you want it to print, you should add a signal handler for SIGUSR1. It doesn't have to do anything.
void h (int s) {}
...
sigset_t mask;
sigemptyset(&mask);
//sigaddset(&mask,SIGUSR1);
printf("This is the child Process id = %d \n",getpid());
struct sigaction sa = { .sa_handler = h };
sigaction(SIGUSR1, &sa, NULL);
sigsuspend(&mask);
printf("The child is now complete \n");

How to wait for the children processes to send signals?

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
void handler(int signumber)
{
return;
}
int main()
{
int i, pid;
int children_count = 5;
int arr_childprocesses[5];
int parent_pid = getpid();
for(i=0;i<children_count;i++)
{
pid = fork();
if(pid == -1)
{
perror("Err");
exit(EXIT_FAILURE);
}
if(pid == 0) break;
arr_childprocesses[i] = pid;
}
if (pid == 0) // children
{
kill(parent_pid,SIGUSR1);
printf("Child(%d) sig sent. Waiting 5s...\n",getpid());
sleep(5);
printf("Child(%d) terminated.\n",getpid());
}
else // parent
{
signal(SIGUSR1,handler);
for(i=0;i<children_count;++i)
{
waitpid(arr_childprocesses[i],NULL,0);
printf("Parent: Signal received.\n");
}
printf("Parent(%d) signals received. Waiting 3s...\n",getpid());
sleep(3);
printf("Parent(%d) terminated.\n",getpid());
}
exit(EXIT_SUCCESS);
}
I want to wait until all the children send me a signal. Then do some work with the children and with the parent too. But the program stops until all the children terminate. How should I do this?
Result:
Update 1: full code plus result included
You are probably facing a race here.
The parent receives the SIGUSR1 before its handler for this signal had been set up. As the default behaviour on receiving a SIGUSR1 is to end, the parent dies.
You want to setup the signal handler inside the parent before forking off the child.
(If from the programs design it is unacceptbale for the child to have SIGUSR1 signal handler set up, just call signal(SIGUSR1, SIG_DFL) as the 1st statement inside the child to deinstall this handler.)
To prove this theory you might like to temporarily add a sleep(1); inside the child just before the call to kill().
As a hint to fulfill your assignment:
Have a look at sigaction() as it provides a much more powerful interface to signalling then the function signal() does. Especially read about the SA_SIGINFO flag as it enables passing a siginfo_t typed variable to your signal handler. This latter variable carries info on who (identified by PID) sent the signal, which is the key to your solution.

Hanging loop with sleep()

I'm trying to learn how to handle signals. In my program I have an array of pids of earlier created subprocesess. No I want to every couple seconds send a sigtstp signal to one of them. He just have to send sigchld to parent process and exit. Parent process should print an exit code of exited process and create next one in the place of exit one. Everything works fine in first loop but it hangs in second. So on output get:
loop
slept
forking
in to array
loop
Zakonczyl sie potomek 3934 z kodem 0.
So it's seems that sleep works in first loop but not in second. Or just main process didn't get back control after handling signal but this should't happen. So I have no idea whats may be wrong here.
while(1) {
printf("loop\n");
sleep(5);
printf("slept\n");
int r = rand() % n;
if(kill(process_tab[r],SIGTSTP) < 0) {
printf("Error while sending sigtstp signal.\n");
} else {
printf("forking\n");
if((child = fork()) < 0) {
printf("Fork failed.\n");
} else if(child == 0) {//to sie dzieje w procesie
if(signal(SIGTSTP,&catch_sigtstp)) {
printf("Error while setting signal handler.\n");
_exit(EXIT_FAILURE);
}
while(1) {
}
} else { //to sie dzieje w parencie
process_tab[r] = child;
printf("in to array\n");
}
}
}
And here are handlers.
void catch_sigtstp(int signal) {
kill(ppid,SIGCHLD);
_exit(EXIT_SUCCESS);
}
void catch_sigchld (int signal) {
int status;
pid_t child = wait(&status);
printf("Zakonczyl sie potomek %d z kodem %d.\n",child,status);
}
Add fflush after printf.
printf("Something\n");
fflush(stdout);
Otherwise you may not get the output as stdio is buffered by default.
Edit: Issues of handler
It is pretty unsafe to use printf function in signal handler, as it is not reentrant. Also, the catch_sigchild function can be modified:
void catch_sigchld (int signal) {
int status;
pid_t child;
while ((child = waitpid(-1, &status, WNOHANG)) > 0)
{
// may be something else?
// ...printf("Zakonczyl sie potomek %d z kodem %d.\n",child,status);
}
}
The reason is that one signal can be delivered for multiple dead children.
Edit: blocking signal when printing.
To avoid deadlock inside stdio, you should block the signal:
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGCHILD);
...
sigprocmask(SIG_BLOCK, &set, NULL);
printf("my output");
sigprocmask(SIG_UNBLOCK, &set, NULL);
...
Edit: as #Barmar has pointed, you parent process will receive SIGCHILD signal twice: once from your child'd signal handler, and one from OS.
To fix, it might be sufficient to remove your own signal source:
void catch_sigtstp(int signal) {
// kill(ppid,SIGCHLD); //< This one causes two signals per one child
_exit(EXIT_SUCCESS);
}

kill signal example

I'm trying this example that I took from: http://www.cs.cf.ac.uk/Dave/C/node24.html:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sighup(); /* routines child will call upon sigtrap */
void sigint();
void sigquit();
main() {
int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
printf("\nI am the new child!\n\n");
signal(SIGHUP,sighup); /* set function calls */
signal(SIGINT,sigint);
signal(SIGQUIT, sigquit);
printf("\nChild going to loop...\n\n");
for(;;); /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid,SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid,SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid,SIGQUIT);
sleep(3);
}
}
void sighup() {
signal(SIGHUP,sighup); /* reset signal */
printf("CHILD: I have received a SIGHUP\n");
}
void sigint() {
signal(SIGINT,sigint); /* reset signal */
printf("CHILD: I have received a SIGINT\n");
}
void sigquit() {
printf("My DADDY has Killed me!!!\n");
exit(0);
}
But I do not see any output from the child process.
Is it the expected behaviour? If so; why?
Thank you very much!
Your code has a major race condition. You do not ensure that the child has finished calling signal before the parent sends the signals. You either need to use some kind of synchronization primitive to make the parent wait for the child to install the handlers, or you need to install the signal handlers before forking so the child inherits them.
Here's the easiest way I know to synchronize processes like this:
Before forking, call pipe(p) to create a pipe.
fork().
In the parent, close(p[1]); (the writing end) and read(p[0], &dummy, 1);
In the child, close(p[0]); and close(p[1]); after installing the signal handlers.
When the parent returns from read, you can be sure the child has setup its signal handlers. You can also close(p[0]); in the parent at this point.
Edit 2: Perhaps a better and easier approach:
Before forking, call sigprocmask to block all signals and save the old signal mask.
In the parent, call sigprocmask again right after forking to restore the original signal mask.
In the child, call sigprocmask right after installing the signal handlers to restore the original signal mask.
You should use getpid() instead of pid().

Resources