0

I have a parent process that created 16 child processes using fork in a loop. Eventually, every child sends a SIGUSR1 signal , that is handled by a handler function in the parent process.

My problem is this - some children send the signal while a signal from another child is handled. I read that the handler function then stops and handles the new signal, ignoring the current signal its working on.

I tried to fix that by sending: kill(0,SIGSTOP) at the start of the handler function, but looks like that stops the parent process as well. Is there a way to send this signal only to the children?

If its not possible, is my goal achievable using wait, waitpid and kill?

Added the code below, I left out stuff like checking the return value for read, open etc.

Handler function:

void my_signal_handler( int signum, siginfo_t* info, void* ptr)
{
    kill(0, SIGSTOP);
    int sonPid = info->si_pid;
    char* pipeName = malloc(14 + sizeof(int));//TODO ok?
    sprintf(pipeName, "//tmp//counter_%d" , (int) sonPid); //TODO double //?
    size_t fdPipe = open(pipeName, O_RDONLY);
    int cRead;
    int countRead = read(fdPipe,&cRead,sizeof(int));
    COUNT+= cRead;
    kill(0, SIGCONT);
    return;
}

Creating the child processes:

struct sigaction new_action;
memset(&new_action, 0, sizeof(new_action));
new_action.sa_handler = my_signal_handler;
new_action.sa_flags = SA_SIGINFO;
if( 0 != sigaction(SIGUSR1, &new_action, NULL) )
{
    printf("Signal handle registration failed. %s\n", strerror(errno));
    return -1;
}
for(int i=0; i<16; i++){
    pid_t cpid = fork();
    if(cpid == 0) // child
    {
        execv("./counter",argvv); // some arguments to the function
        printf("execv failed: %s\n", strerror(errno));
        return -1;
    }
    else{
        continue;
    }

The counter program of the children, in short it counts the appearances of the char counc in some part of the file then prints it to a pipe.:

int main(int argc, char** argv){
    int counter = 0;
    char counc = argv[1][0];
    char* filename = argv[2];
    off_t offset = atoll(argv[3]);
    ssize_t length = atoll(argv[4]); 
    int fd = open(filename, O_RDWR | O_CREAT);
    char* arr = (char*)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); 
    for(int i=0; i<length; i++){
        if(arr[i] == counc){
            counter++;
        }
    }
    pid_t proid = getpid();
    pid_t ppid = getppid();
    char* pipeName = malloc(14 + sizeof(pid_t));
    sprintf(pipeName, "//tmp//counter_%d" , (int) proid);
    size_t fdPipe = mkfifo(pipeName, 0777);
    int didopen = open(pipeName,O_WRONLY);
    size_t wrote = write(fdPipe,&counter , 1 );
    if(wrote < 0){
        printf(OP_ERR, strerror( errno ));
        return errno;
    }
    kill(ppid, SIGUSR1);
//close the pipe and unmap the array
return 1;
}
Bar
  • 196
  • 10
  • Please post your code as a proper [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) (read the page very carefully). – Arash May 23 '17 at 18:08
  • I thought an explanation would be OK in this case, but I'll add a code example shortly. – Bar May 23 '17 at 18:10
  • This is not true, the signal being handled is blocked until the handler is finished, so if they all send the same signal, you should be fine. Some code would help to identify your **real** problem. Btw, keep handlers short, only set flags and do complex handling outside the signal handler. –  May 23 '17 at 18:12
  • Read about signal-safe functions. Your handler calls *other* functions. Ideally, only set a `volatile sig_atomic_t` variable as a flag to your main code and do anything else outside your handler. –  May 23 '17 at 18:41
  • @FelixPalmen I tried, I came to the conclusion that I have to use sprintf inside the handler to get the name of the pipe. Unless the pipe is open in the handler, I don't think a child can write to it. – Bar May 23 '17 at 19:35
  • @Bar only async safe functions can be called from a signal handler. Look at [section 2 of Signal](http://man7.org/linux/man-pages/man2/signal.2.html). I would try to make the signal handler as simple as setting a flag. Then I check for the value of that flag inside the app (like a loop) and whenever it is set I do the functionality that currently you put into the signal handler. Or e.g., if this code is a part of a library, whenever you enter the library you check the flag... This way you do the work when you are outside the signal handler... – Arash May 24 '17 at 02:15
  • @Bar are you expecting the sending of the signal via `kill` to: add the signal to the set of signals that will be delivered to the process; or to queue up such that your handler is always called N times if sent N SIGUSR1 signals even while it's already handling one? I ask because for the latter you'd need something like linux's `sigqueue`. Also, what operating system is it that you're expecting this code to work on? I ask because some OS's don't provide reliable signal semantics which could also be the problem. – Louis Langholtz May 25 '17 at 21:40
  • @LouisLangholtz I work on Linux. I meant the latter, but since that was a homework assignment I wasn't allowed to use `sigqueue`. Had to solve it using `sleep` to ensure 2 children aren't sending a signal at the same time. Pretty stupid, but couldn't use anything other than `kill` and 'wait'. I'm still not sure `sigqueue` would fix the problem - because a child could still run and try to write to the pipe, while his signal was not handled yet, therefore the pipe was not opened in the parent process, and the write attempt will fail. – Bar May 25 '17 at 22:56
  • What I tried to do is to send `kill(0,SIGSTOP)` as soon an I entered the handler, to make sure its handled before the child processes run further. But as I wrote earlier, it kills the parent too – Bar May 25 '17 at 23:00

2 Answers2

0

If i understand your question correctly, you are having trouble the desired signal function in father and children separately. If this is not your case please correct me.

If your problem is this, you can simply create some if statements, to test the pid that fork() call returns and then only execute if you are the father. So...

if (pid == 0) 
          //ChildProcess();
          // do nothing!
     else 
          //ParentProcess();
          // do something!

Note that you have to define size_t pid as a global variable, to be visible in both main, and signal handler function you are implementing!

Sarriman
  • 382
  • 5
  • 22
  • Its not it - the problem is that the parent process has 16 children, and when he handles a signal from child 1, child 2 send a new signal, resulting in the signal of child 1 not being handled properly. – Bar May 23 '17 at 18:08
  • Then you simply have to predefine which PID refers to what children, and create the `if` statements like this. – Sarriman May 23 '17 at 18:11
0

Besides what others have already pointed out regarding the use of non-async-safe functions from within a signal handler, I'm gonna go out on a limb and guess that the problem is either of these two things:

  1. You're incorrectly assuming that signals sent via kill are queued up for the process they're sent to. They're not queued up (unless you use sigqueue on an operating system that supports it); the set of pending (yet un-handled) signals for the destination process is instead updated. See signal queuing in C. Or,
  2. You're running this code on an operating system that doesn't support "reliable signal semantics". See man 3 sysv_signal for insights (horrors) like:

However sysv_signal() provides the System V unreliable signal semantics, that is: a) the disposition of the signal is reset to the default when the handler is invoked; b) delivery of further instances of the signal is not blocked while the signal handler is executing;

Louis Langholtz
  • 2,913
  • 3
  • 17
  • 40