simple synchronization with signals - c

The program is intended to signal transaction permanently. SIGUSR1 is caught by the parent and SIGUSR2 caught by the child. They play with only the flag when they catch their own signals. I let first the parent to run, that is, at first the parent sends signal. The child waits by pause() its process until it runs its catcher on the fly. I thought I apply a simple synchronization, but seemingly not. However, if I comment in the usleep(1000), the code works. Like
initial value, flag = -99
child process, flag = 0
parent process, flag = 1
child process, flag = 0
parent process, flag = 1
child process, flag = 0
.
.
.
child process, flag = 0
parent process, flag = 1
child process, flag = 0
parent process, flag = 1
child process, flag = 0
.
.
.
but without sleep, I can't get what I want. I want to get my intend without sleep. Wrong output is,
initial value, flag = -99
parent process, flag = -99
waits forever..................
How can it be run as intended? However, what's the reason of the behaviour? By the way, I have to apply the synchronization with only signals without semaphores, mutex etc. All posix signal features, except for sleep, nanosleep or pause and busy waiting, can be used like sigaction, sigsuspend etc.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
volatile sig_atomic_t flag = -99; // child = 0, parent = 1;
void catcher(int sig) {
switch (sig) {
case SIGUSR1 : flag = 1; break;
case SIGUSR2 : flag = 0; break;
}
}
int safeBlockParent(int signum) {
sigset_t maskall, maskmost, maskold;
sigfillset(&maskall);
sigfillset(&maskmost);
sigdelset(&maskmost, signum);
sigprocmask(SIG_SETMASK, &maskall, &maskold);
if (flag == 0)
sigsuspend(&maskmost);
sigprocmask(SIG_SETMASK, &maskold, NULL);
}
int safeBlockChild(int signum) {
sigset_t maskall, maskmost, maskold;
sigfillset(&maskall);
sigfillset(&maskmost);
sigdelset(&maskmost, signum);
sigprocmask(SIG_SETMASK, &maskall, &maskold);
if (flag == 1)
sigsuspend(&maskmost);
sigprocmask(SIG_SETMASK, &maskold, NULL);
}
void ChildProcess() {
while(1) {
safeBlockChild(SIGUSR2);
fprintf(stderr, "child process, flag = %d\n", flag);
kill( getppid(), SIGUSR1 );
}
}
void ParentProcess(pid_t childPid) {
flag = 1;
while(1) {
//usleep(1000);
fprintf(stderr, "parent process, flag = %d\n", flag);
kill( childPid, SIGUSR2 );
safeBlockParent(SIGUSR1);
}
}
int main() {
pid_t pid;
struct sigaction sact = { 0 };
fprintf(stderr, "initial value, flag = %d\n", flag);
sigemptyset( &sact.sa_mask );
sact.sa_flags = 0;
sact.sa_handler = catcher;
if (sigaction (SIGUSR1, &sact, NULL) < 0) {
perror("sigaction sigusr1 error");
exit(1);
}
if (sigaction (SIGUSR2, &sact, NULL) < 0) {
perror("sigaction sigusr2 error");
exit(2);
}
pid = fork();
if (pid < 0) { perror("fork problem"); exit(3); }
if (pid == 0) {
//kill(getppid(), SIGUSR1);
ChildProcess();
}
else {
ParentProcess(pid);
//wait(NULL);
}
return 0;
}
The code stucks sometimes, sometimes runs.

You have two race conditions:
The parent process could send a signal before the child has had a chance to register a signal handler for SIGUSR2.
One process could send a signal while the other is outside pause.
The latter can happen the first time round, when the child process has yet to reach pause, but the parent has sent SIGUSR2 anyway. This causes the effect you're seeing.

Related

How to send a signal around specific processes?

I'm trying to send a signal around circle of processes for a certain amount of times. my first argument represents the number of processes I wish to create. my second one is just a place holder I am currently initiating to be 0. My third is the number of time I want to pass this signal around. I have designed the processes to have a relationship as such: Parent->child1,
child1->child2, child2->child3.... and so on. I'm just figuring out C and I'm confused to why my code is stopping midway. It runs for an iteration or two and then stalls out. Can someone explain why?
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int startProcess;
int N;
int numOfCycles;
sigset_t killSet;
void myHandler1 () {
if(N >= 2 && numOfCycles > 0) {
printf("N=%d, numOfCycles=%d, Signal caught. PID = %d\n",N,numOfCycles,getpid());
numOfCycles--;
kill((getpid()+1),SIGUSR1);
}
else if(N >= 2 && numOfCycles == 0) {
exit(1);
}
else if(N == 1 && numOfCycles > 0) {
printf("N=%d, numOfCycles=%d, Signal caught. PID = %d\n",N,numOfCycles,getpid());
numOfCycles--;
kill(startProcess,SIGUSR1);
}
else if(N == 1 && numOfCycles == 0) {
exit(1);
}
else {
printf("Cycle Complete\n");
exit(1);
}
}
void main(int arg, char ** argv) {
struct sigaction temp, vitas, arctic;
sigemptyset(&killSet);
sigaddset(&killSet,SIGUSR1);
N = atoi(argv[1]);
pid_t process1;
startProcess = atoi(argv[2]);
numOfCycles = atoi(argv[3]);
temp.sa_handler = myHandler1;
temp.sa_flags = SA_RESTART;
// vitas.sa_handler = myHandler2;
// vitas.sa_flags = SA_NODEFER;
//
// arctic.sa_handler = myHandler3;
// arctic.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &temp, NULL);
if (N > 1 ) {
process1 = fork();
if(process1 == 0) {
if(N > 2) {
printf("I am a child with PID=%d, PPID=%d, N =%d\n",getpid(),getppid(),N);
}
N--;
char narg = N+'0';
char *pnarg = &narg;
if(startProcess == 0) {
char nstartProcess[6];
startProcess=getppid();
sprintf(nstartProcess,"%d",startProcess);
char *pstartProcess = &nstartProcess[0];
execl("circle",argv[0],pnarg,pstartProcess,argv[3],NULL);
}
else{
if(N == 1){
printf("I am the final child with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
printf("\nSignal Passing start\n\n");
kill(startProcess, SIGUSR1);
while(1) {
sigsuspend(&killSet);
}
}
else {
execl("circle",argv[0],pnarg,argv[2],argv[3],NULL);
}
}
}
else {
printf("I am a parent with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
wait(NULL);
while(1) {
sigsuspend(&killSet);
}
}
}
}
stalls out. Can someone explain why?
The final child is not woken up from sigsuspend(&killSet) because you did sigaddset(&killSet,SIGUSR1); - you seem to have thought you have to add the signal to be waited for to the set, but on the contrary the signals in the given set are blocked from delivery. So just drop the sigaddset call.
To add to #Armali and as the sigset was not the only point to consider, it's seems more simple to provide an implementation with a far more simple handler and which avoid fork+exec, you'll found explanations in the comments. I'm not sur i'have understood what is your intent, from the comments i guess that you are looking to launch N processus with the last one sending cycles time SIGUSR1 to the parent, each child processus sending SIGUSR1 to their parent on receipt. Correct me if i'm wrong.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int startProcess;
int N;
int numOfCycles;
sigset_t killSet;
// Two counters for the signal handler
int numSignals=0;
int numSignalsTotal=0;
// A simple handler that do nothing else that incrementing values in the process.
// To use a SIGINFO handler : void myHandler1 (int signal, siginfo_t *info, void *ucontext) {
void myHandler1 (int signal) {
numSignals++;
numSignalsTotal++;
}
void main(int arg, char ** argv) {
struct sigaction temp;
// As Armali pointed that, here you should not add SIGUSR1 to the sigset a you want
// to suspend your process and waiting for it.
sigemptyset(&killSet);
N = atoi(argv[1]);
pid_t process1 = 0;
startProcess = atoi(argv[2]);
numOfCycles = atoi(argv[3]);
// A more complete initialization here, you need to include SIGUSR1 here to associate it
// to your handler. It seems you doesn't need specific flag like SA_RESTART for it here,
// as the handler code doesn't require that.
temp.sa_handler = myHandler1;
sigemptyset(&temp.sa_mask);
sigaddset(&temp.sa_mask,SIGUSR1);
temp.sa_flags = 0;
temp.sa_restorer = NULL;
// Always check that your function calls return OK. Always.
if (sigaction(SIGUSR1, &temp, NULL) != 0) {
perror("ERR: Failed to set handler ...");
exit(1);
}
// Instead of a mix of fork + exec, it is more simple to keep one process and fork it for each new process.
// Here, we fork at least once, the parent process will alaways be "PARENT" and have a 0 startProcess,
// the child processes will fork themselves, the parent process is their and the new forked one will be
// the next child.
while (startProcess == 0 || N > 1 ) {
process1 = fork();
// To follow the forks : printf(" Fork pid=%d %s %s %s %s\n", process1, argv[0],argv[1],argv[2],argv[3]);
if(process1 != 0 && startProcess == 0) {
// If fork() gives us a pid and we have startProcess 0, then we are the "PARENT" process.
// The only thing we have to do is waiting for signals from our childs, if there is childs.
printf(" I am a parent with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
// If there's no child, we simply quit the loop.
if (N == 1) break;
// For reference, "PARENT" output what is its first child.
printf(" -> child = %d\n" , process1);
// Here, while we have cycles, we wait for signals
while(numOfCycles > 0) {
// If you use a handler, you need to operate synchronously with it, so here we block SIGUSR1 as we will wait for it.
if (sigprocmask(SIG_BLOCK, &temp.sa_mask, NULL) != 0) {
perror("ERR: Failed to block usr1...");
exit(1);
}
// We wait for a SIGUSR1.
sigsuspend(&killSet);
// We have a SIGUSR1, we unblock SIGUSR1 as we will modify numSignals and don't want handler doing that at the same time.
if (sigprocmask(SIG_UNBLOCK, &temp.sa_mask, NULL) != 0) {
perror("ERR: Failed to unblock usr1...");
exit(1);
}
// We output on STDOUT that "PARENT" caught a SIGUSR1, we should have the number of signals received in numSignals
printf(" ===> PARENT : N=%d, numOfCycles=%d, Signal caught. PID = %d, count = %d\n",N,numOfCycles,getpid(), numSignals);
// We substract that from our cycles and reset numSignals
numOfCycles-=numSignals; numSignals=0;
}
// If we are here, no more cycles, so we output on STDOUT a summary of "PARENT" processing
printf(" PARENT : N=%d, PID = %d, total = %d\n",N,getpid(), numSignalsTotal);
// Before leaving, "PARENT" should wait for its potential remaining childs to exit
printf(" PARENT waiting for child to terminate.\n");
if (wait(NULL) == -1) {
perror("ERR: PARENt failed to wait for childs termination...");
exit(1);
}
// We quit the loop, so we exit
break;
} else {
// If only one process (N=1) was asked, nothing more to do for the fork, we quit the loop then exit
if (N == 1 && startProcess == 0) break;
// We get the parent process pid in startProcess, as we are a child process
startProcess = getppid();
// If we are the forked process, we decrement N, it will "our" N as child
if (N > 1 && process1 == 0) {
N--;
}
if(process1 == 0) {
// If we are the forked process, we simply state our identity on STDOUT, the final child should be the one with N=1
if (N > 1) {
printf(" I am a child with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
} else {
printf(" I am the final child with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
}
if (N == 1) {
// If we are the final child, we simply have to send SIGUSR1 signals to our parent process for the number of cycles,
// after that we simply quit the loop and exit, no more forks are required.
while(numOfCycles > 0) {
printf("\n Cycle %d, first Signal start to %d\n\n", numOfCycles--, startProcess);
if (kill(startProcess, SIGUSR1) != 0) {
perror("ERR: LAST CHILD : Failed to send USR1...");
exit(1);
}
}
break;
}
// Note that if we are NOT the final child, we have to go through the loop to fork a new process for the next child.
} else {
if (N > 1) {
// If we are here, we are the forked process for a child and we are not the final child.
// So we do our payload, waiting for SIGUSR1 signals from our child and sending SIGUSR1 to our parent for the
// required number of cycles.
while(numOfCycles > 0) {
// If you use a handle, you need to operate synchronously with it, so here we block SIGUSR1 as we will wait for it.
if (sigprocmask(SIG_BLOCK, &temp.sa_mask, NULL) != 0) {
perror("ERR: Failed to block usr1...");
exit(1);
}
// We wait for a SIGUSR1.
sigsuspend(&killSet);
// We have a SIGUSR1, we unblock SIGUSR1 as we will modify numSignals and don't want handler doing that at the same time.
if (sigprocmask(SIG_UNBLOCK, &temp.sa_mask, NULL) != 0) {
perror("ERR: Failed to unblock usr1...");
exit(1);
}
// We output on STDOUT that this child caught a SIGUSR1, we should have the number of signals received in numSignals
printf(" ===> CHILD: N=%d, numOfCycles=%d, Signal caught. PID = %d, count = %d\n",N,numOfCycles,getpid(), numSignals);
// For each receveid signals from our child, we send one to our parent
while (numSignals > 0) {
printf("\n Signal Passing start to %d\n\n", startProcess);
if (kill(startProcess, SIGUSR1) != 0) {
perror("ERR: CHILD : Failed to send USR1...");
exit(1);
}
// We decrement accordingly our number of cycles and signals
numOfCycles--;
numSignals--;
}
}
// If we are here, no more cycles, so we output on STDOUT a summary of the child processing
printf(" CHILD : N=%d, PID = %d, total = %d\n",N,getpid(), numSignalsTotal);
// We quit the loop, so we exit, then terminate our child
break;
}
}
}
}
// We should always restore signal handler to default when we exit.
temp.sa_handler = SIG_DFL;
sigemptyset(&temp.sa_mask);
sigaddset(&temp.sa_mask,SIGUSR1);
temp.sa_flags = 0;
temp.sa_restorer = NULL;
// Always check that your function calls return OK. Always.
if (sigaction(SIGUSR1, &temp, NULL) != 0) {
perror("ERR: Failed to restore SIGUSR1 ...");
exit(1);
}
// We output on STDOUT the related PID at each process termination.
printf(" End of PID %d\n",getpid());
}

Wait for all child processes avoiding the suspended processes

I'm trying to write a shell and I came across this problem: after I run the fork() and execute the commands, in the main process I wait for all child processes like this:
while (wait(NULL) > 0);
But when I try to suspend a child process, the main process won't go past this loop.
So how do I wait only for non suspended processes?
I could try to save the pid_t of all started sub processes then check if they are suspended but I thought maybe there is a better way.
To wait for any child, either exited (aka ended, terminated) or stopped (aka suspended) use the waitpid() instead.
int wstatus;
{
pid_t result;
while (result = waitpid(-1, &wstatus, WUNTRACED)) /* Use WUNTRACED|WCONTINUED
to return on continued children as well. */
{
if ((pid_t) -1 = result)
{
if (EINTR = errno)
{
continue;
}
if (ECHILD == errno)
{
exit(EXIT_SUCCESS); /* no children */
}
perror("waitpid() failed");
exit(EXIT_FAILURE);
}
}
}
if (WEXITED(wstatus))
{
/* child exited normally with exit code rc = ... */
int rc = WEXITSTATUS(wstatus);
...
}
else if (WIFSIGNALED(wstatus)
{
/* child exited by signal sig = ... */
int sig = WTERMSIG(wstatus);
...
}
else if (WSTOPPED(wstatus))
{
/* child stopped by signal sig = ... */
int sig = WSTOPSIG(wstatus);
...
}
else if (WCONTINUED(wstatus))
{
/* child continued (occurs only if WCONTINUED was passed to waitpid()) */
}

Sending signal frequently with alarm() and pause()

My task is:
Write a program in which the parent process creates exactly 1 child. After creating the child, the behaviour of the parent process is as follows: it sends the signal SIGUSR1 to the child every 5 seconds. To implement this behaviour the parent process most use the following system calls: alarm() and pause(). After sending the signal SIGUSR1 three times, the fourth time the signal SIGUSR2 is sent to the child. After this, the parent waits for the child to finish.
The behaviour of the child is as follows: it waits until it is interrupted by any signal. If the signal received is SIGUSR1 it prints a message to the standard output. If the signal received is SIGUSR2 then it finishes. In addition, during the first 5 seconds of its execution, the signal SIGUSR2 should be blocked. Students are expected to check the behaviour of alarm() and pause() in the man pages.
My solution looks like this. I am trying to ignore the alarm() in the parent and by the alarm in the child I set the flag on true and change unblock SIGUSR2.
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#include <sys/wait.h>
bool flag = false;
void alert_ignore(int signum) {
printf("catched alarm\n");
return;
}
void alert_setting_flag(int signum) {
flag = true;
return;
}
void sigusr1_handler(int signum) {
printf("Recieved SIGUSR1\n");
return;
}
void sigusr2_handler(int signum) {
printf("Recieved SIGUSR2\n");
exit(0);
}
int main(int argc, char *argv[]) {
sigset_t set;
if (sigemptyset(&set) == -1) {
perror("sigemptyset");
exit(1);
}
if (sigaddset(&set, SIGUSR2) == -1) {
perror("sigaddset");
exit(1);
}
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
perror("sigprocmask");
exit(1);
}
struct sigaction alert_action;
alert_action.sa_handler = alert_ignore;
alert_action.sa_mask = set;
if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
perror("sigaction");
exit(1);
}
struct sigaction sigusr1_action;
sigusr1_action.sa_handler = sigusr1_handler;
sigusr1_action.sa_mask = set;
if (sigaction(SIGUSR1, &sigusr1_action, NULL) == -1) {
perror("sigaction");
exit(1);
}
struct sigaction sigusr2_action;
sigusr2_action.sa_handler = sigusr2_handler;
if (sigaction(SIGUSR2, &sigusr2_action, NULL) == -1) {
perror("sigaction");
exit(1);
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
/* Child process duties */
/* Setting alert handler to turn flag form false to true */
alert_action.sa_handler = alert_setting_flag;
if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
perror("sigaction");
exit(1);
}
alarm(5);
int count = 0;
while (true) {
pause();
if (flag == true && !count) {
if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1) {
perror("sigprocmask");
exit(1);
}
printf("SIGUSR2 unblocked\n");
count++;
}
}
}
/* Parent Process duties */
for (int i = 0; i < 3; ++i) {
alarm(5);
pause();
kill(pid, SIGUSR1);
}
kill(pid, SIGUSR2);
wait(NULL);
return 0;
}
This works, but only sometimes and I get much random behavior, like the following, and I don't know why.
Case(0) desired behavior
catched alarm // after 5 seconds
SIGUSR2 unblocked
Recieved SIGUSR1
catched alarm // after 10 seconds
Recieved SIGUSR1
catched alarm // after 15 seconds
Recieved SIGUSR1
Recieved SIGUSR2
Case 1 (a lot):
catched alarm // after 5 seconds
Recieved SIGUSR1
SIGUSR2 unblocked
Alarm clock // after 10 seconds
Case 2 (very rare) needed to terminate it:
catched alarm // after 5 seconds
catched alarm // after 10 seconds
catched alarm // after 15 seconds
^Z
Case 3 (double printing SIGUSR2 unblocked):
catched alarm // after 5 seconds
SIGUSR2 unblocked
SIGUSR2 unblocked
Recieved SIGUSR1
catched alarm // after 10 seconds
Recieved SIGUSR1
catched alarm // after 15 seconds
Recieved SIGUSR1
Recieved SIGUSR2
Case 4:
catched alarm // after 5 seconds
Alarm clock
What is the reason for this behavior? (Most important part for me is, why the SIGALRM is not ignored as desired, I know that there are problems that I am not setting the flag in an atomic way, but that shouldn't influence my parent process, no?)
You have:
struct sigaction alert_action;
alert_action.sa_handler = alert_ignore;
alert_action.sa_mask = set;
if (sigaction(SIGALRM, &alert_action, NULL) == -1) {
Here, alert_action.sa_flags isn't initialized nor assigned to, so it will be a random integer. A number of bad things could happen. If the SA_RESETHAND flag is on, then the signal action will be reset to the default after the first SIGALRM is received, and the next SIGALRM will kill the process. That's why you're sometimes seeing the shell print out Alarm clock.
To fix this, either initialize the structure or have assignment statements for all of the necessary fields. For example:
struct sigaction alert_action = {
.sa_handler = alert_ignore,
.sa_mask = set,
.sa_flags = 0
};
You should do this for all your struct sigaction variables.

Inform parent process about child get signal after signal handler in child process is served

Hi currently I am collecting backtrace of child process in signal handler of child process . Then planning to send collected backtrace to parent process using message queue .
My problem is when child process get any signal. child signal handler runs but informs parent process that child exited normally instead of child get signal.
below is my code
void childProcess()
{
int h =0 ;
for(h=0;h<10;h++)
{
printf("child for loop running %d\n",h);
//sleep(1);
int q = 1/0; // generate floating point exception
}
exit(0);
}
void signalhandler(int signum, siginfo_t *si, void *arg)
{
printf("signal received %s\n",strsignal(signum));
printf("%d\n",signum);
void *array[100];
int size = 100;
int addrLen = backtrace(&array,size);
char ** sym = backtrace_symbols(&array,addrLen);
int j = 0;
printf("Test crashed due to %s\n",strsignal(signum));
for(j=0;j<addrLen;j++)
{
printf("%u : %s\n",array[j],sym[j]);
}
raise(signum);
exit(signum);
}
void registerSignals()
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = signalhandler;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGFPE, &sa, NULL);
}
int main()
{
//while(1)
{
pid_t pid;
pid = fork();
if(pid == 0)
{
// child
printf("child process id is %d\n",getpid());
registerSignals();
childProcess();
}
else
{
printf("parent process id is %d\n",getpid());
// parent
int iStatus;
pid_t childPID = waitpid(pid,&iStatus,0);
printf("iStatus is %d\n",WIFEXITED(iStatus));
if(childPID == -1)
{
printf("wait pid failed\n");
}
else if(WIFEXITED(iStatus)==1)
{
printf("child exited normally!\n");
}
else if (WIFSIGNALED(iStatus)==1)
{
printf("child process terminated abnormally !!\n");
int iSignalnumber = 0;
// to fetch the signal number
iSignalnumber = WTERMSIG(iStatus);
printf("child process terminated due to %s\n",strsignal(iSignalnumber));
// to check core file is generated or not
if(WCOREDUMP(iStatus)==1)
{
printf("core file is generated \n");
}
else
{
printf("core file is not generated \n");
}
}
int h ;
for(h = 0; h<10;h++)
{
printf("parent executing : %d\n",h);
}
}
printf("while loop executing with pid : %d \n", getpid());
sleep(1);
}
}
My requirement is after signal handler is served in child process the
parent should print "child process terminated abnormally !!" but I am
getting "child exited normally!" message
From wait()'s Linux docs:
WIFEXITED(wstatus)
returns true if the child terminated normally, that is, by
calling exit(3) or _exit(2), or by returning from main().
The child signal handler ends the process using exit(), so everything works a specified.
Remove the call to exit() from the signal handler to get the expected result.
The call to raise() inside the signal handler most likely leads to recursive calls, so remove is as well.

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);
}

Resources