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.
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.