Search code examples
csignalssigaction

SIGINFO's si_pid sets itself to 0 after a few calls of the same function


I'm working on a simple project that makes 2 processes communicate to each other using signals. More specifically, I'm using sigaction with the flag SA_SIGINFO so that each process can identify who sent it a signal and reply. Thing is, after they call each other a few times (it can vary a lot, sometimes it happens after 3 exchanges, other times, after 700), siginfo returns a si_pid that is equal to 0. Here are both codes I'm using to make them communicate. First, the "server"

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void    ft_respond(int sig, siginfo_t *info, void *context)
{
    static int  i = 0;
    (void)context;
    if (sig == SIGUSR1)
    {
        i++;
        printf("received - %d PID: %d\n", i, info->si_pid);
        if (info ->si_pid != 0)
            kill(info->si_pid, SIGUSR1);
        if (i == 5000)
            exit(EXIT_SUCCESS);
    }
}

int main(void)
{
    struct sigaction    reaction;
    sigset_t            mask;

    reaction.sa_flags = SA_SIGINFO;
    reaction.sa_sigaction = ft_respond;
    sigaction(SIGUSR1, &reaction, NULL);
    printf("PID = %d\n", getpid());
    while (1)
        pause();
    return(0);
}

And second, the "client"

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void    ft_send_signal(int pid)
{
    kill(pid, SIGUSR1);
    printf("sent\n");
}

void    ft_signal_handler(int sig, siginfo_t *info, void *context)
{
    static int  i = 0;

    (void)context;
    if (sig == SIGUSR1)
    {
        printf("recieved - %d PID: %d\n", i, info->si_pid);
        i++;
        if (info->si_pid != 0)
            kill(info->si_pid, SIGUSR1);
        if (i == 5000)
            exit(EXIT_SUCCESS);
    }
}

int main(int ac, char **av)
{
    struct sigaction    action;
    sigset_t            set;
    
    if (ac != 2)
        exit (EXIT_FAILURE);
    sigaddset(&set, SIGUSR1);
    action.sa_flags = SA_SIGINFO;
    action.sa_sigaction = ft_signal_handler;
    action.sa_mask = set;
    sigaction(SIGUSR1, &action, NULL);
    ft_send_signal(atoi(av[1]));
    while(1)
        pause();
    return (0);
}

Notes:

  • If I remove the line if (info ->si_pid != 0), the process not receiving a signal pauses forever (duh!) and interestingly enough, the other one keeps on going like it is receiving signals forever (not duh!).

  • I have looked around to understand how I could use sigfillset or sigaddset in order to prevent any incoming signal while my handler is still working, nothing seems to disrupt this behaviour.

  • I am running this program on a MacBook, if you're wondering.

  • I have also run the programs on Linux (Ubuntu-based distro) and I get no "bug" there. Which seems odd to me.

  • If you want to test the code, it is sort of straightforward: compile each program with a distinct name (e.g. gcc -o server server.c && gcc -o client client.c), run the server first, then run the client with the server's PID as a parameter.

  • Before I get flamed for using printf with signals, I know it is not recommended in case of signal interruption during printf execution (see How to avoid using printf() in a signal handler?), but theoretically, handler is done when sending out its signal so it should work well. I have tried using write function instead, it has the same behaviour.

If you have ANY lead I can follow for this to work without a hic, I'll be really grateful.


Solution

  • So, after playing around, I stumbled on a clean solution. Since the programs are randomly losing track of the info->si_pid, I stored its value into a static int id and removed the condition if (info->si_pid != 0). Since now, if info->si_pid == 0, my id still has the pid in store. Here's how it looks. I pushed the exchanges to 50000 and it works like a charm, each time.

    Server:

    #include <signal.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    void    ft_respond(int sig, siginfo_t *info, void *context)
    {
        static int  i = 0;
        static int  id = 0;
    
        if (info->si_pid != 0)
            id = info->si_pid;
        (void)context;
        if (sig == SIGUSR1)
        {
            i++;
            printf("received - %d PID: %d\n", i, id);
            kill(id, SIGUSR1);
            if (i == 50000)
                exit(EXIT_SUCCESS);
        }
        return ;
    }
    
    int main(void)
    {
        struct sigaction    reaction;
    
        reaction.sa_flags = SA_SIGINFO;
        sigemptyset(&reaction.sa_mask);
        reaction.sa_sigaction = ft_respond;
        sigaction(SIGUSR1, &reaction, NULL);
        printf("PID = %d\n", getpid());
        while (1)
            pause();
        return(0);
    }
    

    Client:

    #include <signal.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    void    ft_send_signal(int pid)
    {
        kill(pid, SIGUSR1);
        printf("sent\n");
    }
    
    void    ft_signal_handler(int sig, siginfo_t *info, void *context)
    {
        static int  i = 0;
        static int  id = 0;
    
        if (info->si_pid != 0)
            id = info->si_pid;
        (void)context;
        if (sig == SIGUSR1)
        {
            printf("recieved - %d PID: %d\n", i, id);
            i++;
            kill(id, SIGUSR1);
            if (i == 50000)
                exit(EXIT_SUCCESS);
        }
        return ;
    }
    
    int main(int ac, char **av)
    {
        struct sigaction    action;
        
        if (ac != 2)
            exit (EXIT_FAILURE);
        sigemptyset(&action.sa_mask);
        action.sa_flags = SA_SIGINFO;
        action.sa_sigaction = ft_signal_handler;
        sigaction(SIGUSR1, &action, NULL);
        ft_send_signal(atoi(av[1]));
        usleep(100);
        while(1)
            pause();
        return (0);
    }
    

    Now it appears that no matter what, the processes keep on sending signals to each other.