I am writing a simple program that generates signals (SIGUSR1 or SIGUSR2, but currently I am only worried about SIGUSR1) until the user terminates the program, these signals are then sent to a thread to catch and log them.
The child process is in charge of creating the thread to receive the signals.
I believe I install the handler correctly, as well as the signal mask.
Originally, the signals would generate but the thread wouldn't receive and catch them. I was using kill()
to send the signal. I switched to using the pthread library version of pthread_kill()
, which now results in a segmentation fault.
Just a little lost on where I am going wrong and would appreciate any input!
Here's what I got:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
pthread_t reporter_thread;
struct sigaction sa;
sigset_t new_sig_set; // set up mask
void handle_SIGUSR1(int sig)
{
write(STDOUT_FILENO, "SIGUSR1 received", 20);
}
void * reporter(void *arg)
{
printf("Entered reporter thread\n");
//empty mask
pthread_sigmask(SIG_UNBLOCK, &new_sig_set, NULL);
while(1){
sigaction(SIGUSR1, &sa, NULL);
}
}
int main(int argc, char **argv)
{
/* set up the signal masks */
sigemptyset(&new_sig_set); // empty the mask
sigaddset(&new_sig_set, SIGUSR1); // add SIGUSR1 to the mask
pthread_sigmask(SIG_BLOCK, &new_sig_set, NULL); // block SIGUSR1
/* set up the signal handler */
sa.sa_handler = handle_SIGUSR1;
sa.sa_flags = 0;
// randomly generate signals every 5 seconds.
int pid = fork();
if (pid == 0)
{
//will need to exec() sigcatcher.c here
pthread_create(&reporter_thread, NULL, reporter, NULL);
printf("Created reporter thread\n");
}
else
{
wait(NULL);
pthread_sigmask(SIG_BLOCK, &new_sig_set, NULL); // block SIGUSR1
while (1)
{
srand((unsigned)time(NULL));
printf("Generating signal\n");
//generate a random number between 1 and 2
int random = rand()%((2 1)-1) 1;
if (random == 1)
{
printf("Sending signal: SIGUSR1\n");
pthread_kill(reporter_thread, SIGUSR1);
}
else
{
printf("Something else\n");
}
sleep(2);
}
return 0;
}
}
CodePudding user response:
So, several bugs here.
As pilcrow says,
pthread_kill
is for sending a signal to a thread in the same process. For signaling another process, you did wantkill()
all along. (And moreover, thereporter_thread
object was initialized only in the child process, which has its own memory; it never got initialized at all in the parent.) So why didn'tkill()
work before? Read on.The main thread of your child process exits immediately after starting the reporter thread: it falls out of the
if (pid == 0)
block and does the next thing in the function, which is nothing: it merely returns frommain
. This causes all the threads of that process to terminate, including the reporter thread. Threads do not get to survive the death of the process. Seeman pthread_create
:The new thread terminates in one of the following ways: [...]
- Any of the threads in the process calls exit(3), or the main thread performs a return from main(). This causes the termination of all threads in the process.
Moreover, the parent process then calls
wait()
to reap the child, so by the time you get around tokill()
, the child process isn't even a zombie anymore. If you had checked the return value anderrno
ofkill()
(hint hint), you should have seenESRCH
, the target process does not exist.So you need to make the child process's main thread keep running after starting the reporter thread. For instance you could
pthread_join
the reporter thread (which may or may not ever return). Likewise, the parent needs to not callwait()
; that would never complete since the child process never exits.The reporter thread has a race. It needs to install the signal handler before unblocking the signal; otherwise the signal is likely to arrive before the handler is installed, which would then be fatal.
The infinite loop of
sigaction
also serves no purpose except to waste CPU. The default behavior ofsigaction
, unless you specify theSA_RESETHAND
flag, is that the handler remains installed after the signal is delivered. Have the reporter thread callsigaction
only once, thenpthread_sigmask
once, then enter awhile(1) pause();
loop or similar.The
write()
in the signal handler writes 20 bytes of a string that is only 17 bytes long. gcc issues a warning about this. Enable all compiler warnings and heed them.It's unnecessary to call
pthread_sigmask
a second time in the parent. The signal is already blocked, and nobody is going to be sending signals to the parent anyway. The only reason to call it in the parent at all is to avoid a race by ensuring the child process starts with the signal already blocked.Unrelated, but never call
srand()
in a loop; it is pointless and usually counterproductive. Just call it once before the first call torand()
.
With these fixed, the program works for me. Try on godbolt.