Reputation: 195
I'm trying to call a function everytime my child process dies. This is done by catching the SIGCHLD sent by execve(). My problem here is that my handler function is not always called a second time. Sometimes it is though so I'm a bit confused.
My code is this one:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h> // sigaction()
#include <time.h>
#include <errno.h>
#include <unistd.h>
#define MAX_LENGTH 1000
#define BILLION 1E9
struct timespec start,stop;
float totalTime = 0;
void runCommand(int signo)
{
printf("Run command called\n");
float processTime = 0;
//Get the time when the process ended
if(clock_gettime(CLOCK_MONOTONIC,&stop) == -1){
perror(NULL);
}
processTime = stop.tv_sec - start.tv_sec + ( stop.tv_nsec - start.tv_nsec ) / BILLION;
totalTime += processTime;
}
int main(int argc, char const *argv[]) {
int counter = 0;
int pid = 0;
int status;
char *args[4];
char line[MAX_LENGTH];
//Setup behaviour for SIGCHLD
struct sigaction psa;
psa.sa_handler = runCommand;
sigaction(SIGCHLD, &psa, NULL);
FILE *fp = fopen("script_file.txt","r");
if(!fp)
{
return 0;
}
//Setup argsgcc
args[0] = "/bin/sh";
args[1] = "-c";
args[3] = NULL;
while(fgets(line, MAX_LENGTH, fp))
{
printf("The command number %d is %s \n", counter, line);
//Get the time when the process started
if(clock_gettime(CLOCK_MONOTONIC,&start) == -1){
perror(NULL);
}
//Create a new process
pid = fork();
//Child process
if(pid == 0)
{
args[2] = line;
execve("/bin/sh", args, NULL);
}
//Parent process
else
{
wait(&status);
}
counter++;
}
printf("The overall time was %f seconds.\n", totalTime);
fclose(fp);
return 0;
}
When I try to run it sometimes my result is:
The command number 0 is mkdir test/
Run command called
The command number 1 is sleep 0.5
Run command called
The command number 2 is sleep 1
Run command called
The command number 3 is rmdir test/
Run command called
The overall time was 1.544909 seconds.
Which seems correct. But in other instances my result is:
The command number 0 is mkdir test/
Run command called
The command number 1 is sleep 0.5
The command number 2 is sleep 1
The command number 3 is rmdir test/
The overall time was 0.009810 seconds.
This makes me believe that sigaction is not always receiving my SIGCHLD signal. When I run valgrind I get the following error:
==5407== All heap blocks were freed -- no leaks are possible
==5407==
==5407== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==5407==
==5407== 1 errors in context 1 of 2:
==5407== Syscall param rt_sigaction(act->sa_flags) points to uninitialised byte(s)
==5407== at 0x4E6F5AE: __libc_sigaction (sigaction.c:62)
==5407== by 0x40098B: main (iv.c:40)
==5407== Address 0xffefff498 is on thread 1's stack
==5407== Uninitialised value was created by a stack allocation
==5407== at 0x400931: main (iv.c:29)
==5407==
==5407==
==5407== 1 errors in context 2 of 2:
==5407== Syscall param rt_sigaction(act->sa_mask) points to uninitialised byte(s)
==5407== at 0x4E6F5AE: __libc_sigaction (sigaction.c:62)
==5407== by 0x40098B: main (iv.c:40)
==5407== Address 0xffefff4a8 is on thread 1's stack
==5407== Uninitialised value was created by a stack allocation
==5407== at 0x400931: main (iv.c:29)
==5407==
==5407== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
But I'm not sure what this means.
Upvotes: 0
Views: 1394
Reputation: 121347
The simplest way is to initialise psa
is to zero initialise it:
struct sigaction psa = {0};
However, there are subtleties. You can't call async-signal-unsafe functions, such as printf
, from a signal handler. See signal safty
for the list of allowed functions. You could replace the printf
with a write(2)
call to avoid this:
write(STDOUT_FILENO, "Run command called\n", sizeof "Run command called\n" - 1);
Also you're performing wait
on each of the child process before creating another. Thus there's always only one child running at any time. If you modify your program to allow multiple child processes to run simultaneously and the child processes die around the same, then not all SIGCHLD
signals can be caught. For example, if a SIGCHLD arrives while the signal handler is processing is previous SIGCHLD, then it'll be lost. Because pending signals will be discarded unless they're different signals, except for real-time signals. For example, if SIGUSR1
arrives while SIGCHLD
is being processed (by signal handler), it'll be blocked; but another SIGCHLD
will be ignored.
Upvotes: 3