sigsuspend() with SIGINT and SIGCHLD - c

I am studying this piece of code from the CSAPP book:
#include "csapp.h"
volatile sig_atomic_t pid;
void sigchld_handler(int s)
{
int olderrno = errno;
pid = Waitpid(-1, NULL, 0);
errno = olderrno;
}
void sigint_handler(int s)
{
}
#define N 10
int main(int argc, char **argv)
{
sigset_t mask, prev;
int n = N; /* 10 */
Signal(SIGCHLD, sigchld_handler);
Signal(SIGINT, sigint_handler);
Sigemptyset(&mask);
Sigaddset(&mask, SIGCHLD);
while (n--) {
Sigprocmask(SIG_BLOCK, &mask, &prev); /* Block SIGCHLD */
if (Fork() == 0) /* Child */
exit(0);
/* Wait for SIGCHLD to be received */
pid = 0;
while (!pid)
Sigsuspend(&prev);
/* Optionally unblock SIGCHLD */
Sigprocmask(SIG_SETMASK, &prev, NULL);
/* Do some work after receiving SIGCHLD */
printf(".");
}
printf("\n");
exit(0);
}
While I understand a large part of it such as how sigsuspend uses mask and interacts with SIGCHLD, I don't understand this piece from the book If the parent caught a SIGINT, then the loop test succeeds and the next iteration calls sigsuspend again. If the parent caught a SIGCHLD, then the loop test fails and we exit the loop. At this point, SIGCHLD is blocked, and so we can optionally unblock SIGCHLD.
How does SIGINT interact with sigsuspend()? How will loop test succeed if SIGINT gets caught by parent? I guess the behavior of SIGINT is overwritten by the call to Signal(), which gets overwritten to doing nothing?
Also, how is SIGCHLD still blocked after loop test fails(by sigchld_handler())?
Much thanks in advance!
Other than this, can someone please do a walkthrough of this whole example? I don't have a solid understanding after reading it multiple times.

Related

Linux signal parent and child [c]

im new to Linux and im still learning my code job is simple it receives a signal from the parent and the child have to ignore the signal and print the number of the signal like [1,3,4,9,11], but my problem is the child does not print anything after the signal plus I want the child to ignore the signals especially like[sigquit] here is my code.
// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// function declaration
void sighup();
void sigint();
void sigquit();
void sigsegv();
// driver code
void main()
{
int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
signal(SIGSEGV, sigsegv);
for (;;)
; /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid, SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid, SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid, SIGQUIT);
sleep(3);
}
}
// sighup() function definition
void sighup()
{
signal(SIGHUP, sighup); /* reset signal */
printf("CHILD: 1 [sighub]\n");
}
// sigint() function definition
void sigint()
{
signal(SIGINT, sigint); /* reset signal */
printf("CHILD: 2 [sigint]\n");
}
// sigsegv() function definition
void sigsegv()
{
signal(SIGSEGV, sigsegv); /* reset signal */
printf("CHILD: 11 [sigsegv]\n");
}
// sigquit() function definition
void sigquit()
{
signal(SIGINT, sigquit); /* reset signal */
printf("3 [sigquit]\n");
}
Check signal.h in /usr/bin/include, signal handler
/* Type of a signal handler. */
typedef void (*__sighandler_t) (int);
so need to change both the forward declaration and function definition to match this prototype as
// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// function declaration
void sighup(int);
void sigint(int);
void sigquit(int );
void sigsegv(int );
// driver code
int main()
{
int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
signal(SIGSEGV, sigsegv);
for (;;)
; /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid, SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid, SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid, SIGQUIT);
sleep(3);
}
return 0 ;
}
// sighup() function definition
void sighup(int signo)
{
signal(SIGHUP, sighup); /* reset signal */
printf("CHILD: 1 [sighub]\n");
}
// sigint() function definition
void sigint(int signo)
{
signal(SIGINT, sigint); /* reset signal */
printf("CHILD: 2 [sigint]\n");
}
// sigsegv() function definition
void sigsegv(int signo)
{
signal(SIGSEGV, sigsegv); /* reset signal */
printf("CHILD: 11 [sigsegv]\n");
}
// sigquit() function definition
void sigquit(int signo)
{
signal(SIGINT, sigquit); /* reset signal */
printf("3 [sigquit]\n");
}
As mentioned in comments, stdio functions like printf() aren't safe to use in signal handlers. On Linux, you should also use sigaction() instead of signal() to install signal handlers, as that avoids some issues with an imprecise definition of how handlers work in the latter function (Which should only be used when targeting bare bones standard C, not POSIX, where what signal handlers can do is even more restricted than in POSIX).
However, when targeting Linux or Unix platforms, you don't need signal handlers at all for this task! Each process has a signal mask, which controls which signals are blocked from having the normal execution of a handler or default action go off. If a process blocking signal X gets that signal, it's considered pending, and there are other ways to receive it. One such way in Linux is to use a signalfd, a special file descriptor that can be read from to get information about pending signals. An example using it:
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/signalfd.h>
#include <sys/types.h>
#include <sys/wait.h>
int child_main(const sigset_t *, int);
void send_signals(pid_t, int *);
int main(void) {
// The signals we want to catch
int signals[] = {SIGHUP, SIGINT, SIGQUIT, SIGSEGV, -1};
// Set up the signal mask
sigset_t sigs, oldmask;
sigemptyset(&sigs);
for (int i = 0; signals[i] >= 0; i++) {
sigaddset(&sigs, signals[i]);
}
// To avoid a race condition where the parent starts sending signals
// before the child is ready for them, block the signals before
// forking the child
if (sigprocmask(SIG_BLOCK, &sigs, &oldmask) < 0) {
perror("sigprocmask");
return EXIT_FAILURE;
}
pid_t child = fork();
if (child < 0) {
perror("fork");
return EXIT_FAILURE;
} else if (child == 0) {
// In the child process
return child_main(&sigs, (sizeof signals / sizeof signals[0]) - 1);
} else {
// Parent process. Restore the original signal mask and send child signals
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
perror("parent sigprocmask");
kill(child, SIGKILL);
return EXIT_FAILURE;
}
send_signals(child, signals);
// Wait for the child to finish
if (waitpid(child, NULL, 0) < 0) {
perror("parent waitpid");
return EXIT_FAILURE;
}
}
return 0;
}
void send_signals(pid_t proc, int *signals) {
for (int i = 0; signals[i] >= 0; i++) {
printf("Sending process %d signal %s (%d)\n", (int)proc,
strsignal(signals[i]), signals[i]);
if (kill(proc, signals[i]) < 0) {
printf("Failed: %s\n", strerror(errno));
}
}
}
int child_main(const sigset_t *sigs, int nsigs) {
// Create a signalfd that monitors the given signals
int fd = signalfd(-1, sigs, 0);
if (fd < 0) {
perror("child signalfd");
return EXIT_FAILURE;
}
struct signalfd_siginfo s;
// Loop up to nsigs times reading from the signal fd
int count = 0;
while (++count <= nsigs && read(fd, &s, sizeof s) == sizeof s) {
printf("Child received signal %s (%d)\n", strsignal(s.ssi_signo),
s.ssi_signo);
}
if (count <= nsigs && errno != EINTR) {
perror("child read");
close(fd);
return EXIT_FAILURE;
}
close(fd);
return 0;
}
Example output:
Sending process 17248 signal Hangup (1)
Sending process 17248 signal Interrupt (2)
Sending process 17248 signal Quit (3)
Sending process 17248 signal Segmentation fault (11)
Child received signal Hangup (1)
Child received signal Segmentation fault (11)
Child received signal Interrupt (2)
Child received signal Quit (3)
Your program is running perfectly in my machine, so I don't understand why do you say the child doesn't print anything.
Anyway, you have to be careful, as the child is printing the messages of the received signals, but running forever, this means that when the parent is done and exit()s you leave a child running... forever? (and you are running the child without stopping or blocking, so consuming all the available cpu)
You must devise a method to kill such a process, because if you don't you will end with lots of processes running in your behalf, doing nothing but ignoring the kill()s you do to them.
For that, there are some signals that are not ignorable, the SIGKILL is one, you cannot install a signal handler to it, for security reasons... :)
Also, when using signal(2) system call, the handler is executed on the reception of the first signal, and switches to the default behaviour (which depends on the signal) when the signal handler is done.
If you want it to be permanent, you need to reinstall the signal handler (this has a race condition, in case you receive a second signal while you are executing the signal handler and have not had time to install it again) or call sigaction(2) instead, that allows you to permanently (while the process is running, or up to the next call you do to sigaction) install a signal handler that doesn't need to be renewed. Look at the manual page of sigaction, as you need to learn how to use it.

Ignore SIGQUIT once

I am trying to write a function to install a signal handler on a process to ignore a SIGQUIT signal the first time it is called and then exit on the second SIGQUIT signal. I am intend to use a global variable for the counter to count the number of times SIGQUIT signal is sent.
However I am having issues implementing this. I have found SIG_IGN which would ignore the first SIGQUIT signal, but how would I then detect the first signal to increment the counter (and maybe change the signal() to call the signal handler)?
My code is as below:
// global var counter
int signal_counter = 0;
int main (int argc, const char * argv[]) {
pid_t child_pid = 0;
int child_status = 0;
/* fork a child process... */
child_pid = fork();
if(child_pid < 0 ) { /* fork() and check if there were errors */
perror("fork"); /* print a system-defined error message */
exit(EXIT_FAILURE);
}
else if(child_pid == 0) { /* Child code */
install_handler();
for(; ;sleep(1)); /*loop forever*/
}
else { /* Parent code */
printf("Parent processing starts\n");
printf("\nPARENT: sending first SIGQUIT\n\n");
kill(child_pid,SIGQUIT);
printf("\nPARENT: doing something\n\n");
sleep(3);
printf("\nPARENT: sending SIGQUIT again\n\n");
kill(child_pid,SIGQUIT);
}
return EXIT_SUCCESS;
}
void sigquit() {
printf("SIGQUIT\n");
sleep(2);
exit(0);
}
void install_handler(){
if (signal_counter == 0){
signal(SIGQUIT, sigquit);
signal_counter+=1;
}
else{
signal(SIGQUIT, sigquit);
}
}
Any help would be appreciated.
A way to solve it is to use the posix standard. According to it, a signal installed is one shot.
The first time that the signal will arrive, it will start the method specified. Therefore, if another signal arrives, then it will be executed normally (i.e. it will quit your application for a sigquit).

sending signal from parent to child

I am using this tutorial from website http://www.code2learn.com/2011/01/signal-program-using-parent-child.html and trying to understand why signal is not recieved by child?
here is the code:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void sighup(); /* routines child will call upon sigtrap */
void sigint();
void sigquit();
void main()
{ int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0)
{ /* child */
signal(SIGHUP,sighup); /* set function calls */
signal(SIGINT,sigint);
signal(SIGQUIT, sigquit);
for(;;); /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid,SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid,SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid,SIGQUIT);
sleep(3);
}
}
void sighup()
{ signal(SIGHUP,sighup); /* reset signal */
printf("CHILD: I have received a SIGHUP\n");
}
void sigint()
{ signal(SIGINT,sigint); /* reset signal */
printf("CHILD: I have received a SIGINT\n");
}
void sigquit()
{ printf("My DADDY has Killed me!!!\n");
exit(0);
}
output:
It's a race condition. Your code assumes that the child runs first and is not preempted by the parent until it installed all signal handlers and starts looping forever.
When that is not the case, the parent may send a signal to the child before the child had the chance to catch the signal. As such, the child process is killed, since the default action for SIGHUP, SIGINT and SIGQUIT is to terminate.
In your specific case, you never see any output from the child. This means that the parent sent SIGHUP to the child, and SIGHUP was delivered before the child changed the default behavior. So the child was killed.
Actually, if you did some error checking on the returning value of kill(2) - which you should - you would see ESRCH in the parent upon trying to send SIGINT and SIGQUIT, because the child is already gone (assuming no other process in the system was started and got assigned the same PID in the meantime).
So, how do you fix it? Either use some form of synchronization to force the child to run first and only let the parent execute after all signal handlers are installed, or set up the signal handlers before forking, and then unset them in the parent. The code below uses the latter approach:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void sighup(int); /* routines child will call upon sigtrap */
void sigint(int);
void sigquit(int);
int main(void) {
int pid;
signal(SIGHUP,sighup); /* set function calls */
signal(SIGINT,sigint);
signal(SIGQUIT, sigquit);
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
/* child */
for(;;); /* loop for ever */
} else {
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
/* parent */
/* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid,SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid,SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid,SIGQUIT);
sleep(3);
}
return 0;
}
void sighup(int signo) {
signal(SIGHUP,sighup); /* reset signal */
printf("CHILD: I have received a SIGHUP\n");
}
void sigint(int signo) {
signal(SIGINT,sigint); /* reset signal */
printf("CHILD: I have received a SIGINT\n");
}
void sigquit(int signo) {
printf("My DADDY has Killed me!!!\n");
exit(0);
}
Also, you shouldn't be using signal(2): it is unreliable in many ways, and its exact semantics are platform dependent. To ensure maximum portability, you should use sigaction(2). Refer to the manpages to learn more. Here's the same code using sigaction(2) instead:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void sighup(int); /* routines child will call upon sigtrap */
void sigint(int);
void sigquit(int);
int main(void) {
struct sigaction sigact;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigact.sa_handler = sighup;
if (sigaction(SIGHUP, &sigact, NULL) < 0) {
perror("sigaction()");
exit(1);
}
sigact.sa_handler = sigint;
if (sigaction(SIGINT, &sigact, NULL) < 0) {
perror("sigaction()");
exit(1);
}
sigact.sa_handler = sigquit;
if (sigaction(SIGQUIT, &sigact, NULL) < 0) {
perror("sigaction()");
exit(1);
}
pid_t pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
/* child */
for(;;); /* loop for ever */
} else {
sigact.sa_handler = SIG_DFL;
sigaction(SIGHUP, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGQUIT, &sigact, NULL);
/* parent */
/* pid hold id of child */
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid,SIGHUP);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGINT\n\n");
kill(pid,SIGINT);
sleep(3); /* pause for 3 secs */
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid,SIGQUIT);
sleep(3);
}
return 0;
}
void sighup(int signo) {
signal(SIGHUP,sighup); /* reset signal */
printf("CHILD: I have received a SIGHUP\n");
}
void sigint(int signo) {
signal(SIGINT,sigint); /* reset signal */
printf("CHILD: I have received a SIGINT\n");
}
void sigquit(int signo) {
printf("My DADDY has Killed me!!!\n");
exit(0);
}
Last, but not least, it is important to mention that you should always compile with -Wall. Your program has some mistakes:
The return type of main() should be int.
Signal handlers receive the signal number as an argument, please use the right prototype and declaration.
fork(2) returns a pid_t, not an int, please use the correct type.
You need to include unistd.h to get the right prototype for fork(2).
printf(3) is not async-signal safe and as such you shouldn't call it inside a signal handler. It's ok in this toy program to see how signals work together, but keep in mind that you should never do it in the real world. To see a list of async-signal safe functions, as well as the default actions for each signal, see man 7 signal.
Word of advice: stop learning from that website. If you want to learn this kind of stuff, read Advanced Programming in the UNIX Environment. Go straight to chaper 10 to learn why exactly signal(2) is considered unreliable and obsolescent. It's a big book, but it's well worth investing your time on it.

sigwait() and signal handler

If I setup and signal handler for SIGABRT and meanwhile I have a thread that waits on sigwait() for SIGABRT to come (I have a blocked SIGABRT in other threads by pthread_sigmask).
So which one will be processed first ? Signal handler or sigwait() ?
[I am facing some issues that sigwait() is get blocked for ever. I am debugging it currently]
main()
{
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigprocmask(SIG_BLOCK, &signal_set, NULL);
// Dont deliver SIGABORT while running this thread and it's kids.
pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
pthread_create(&tAbortWaitThread, NULL, WaitForAbortThread, NULL);
..
Create all other threads
...
}
static void* WaitForAbortThread(void* v)
{
sigset_t signal_set;
int stat;
int sig;
sigfillset( &signal_set);
pthread_sigmask( SIG_BLOCK, &signal_set, NULL ); // Dont want any signals
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT); // Add only SIGABRT
// This thread while executing , will handle the SIGABORT signal via signal handler.
pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
stat= sigwait( &signal_set, &sig ); // lets wait for signal handled in CatchAbort().
while (stat == -1)
{
stat= sigwait( &signal_set, &sig );
}
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
// Abort signal handler executed via sigaction().
static void CatchAbort(int i, siginfo_t* info, void* v)
{
sleep(20); // Dont return , hold on till the other threads are down.
}
Here at sigwait(), i will come to know that SIGABRT is received. I will tell other threads about it. Then will hold abort signal handler so that process is not terminated.
I wanted to know the interaction of sigwait() and the signal handler.
From sigwait() documentation :
The sigwait() function suspends execution of the calling thread until
one of the signals specified in the signal set becomes pending.
A pending signal means a blocked signal waiting to be delivered to one of the thread/process. Therefore, you need not to unblock the signal like you did with your pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL) call.
This should work :
static void* WaitForAbortThread(void* v){
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigwait( &signal_set, &sig );
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
I got some information from this <link>
It says :
To allow a thread to wait for asynchronously generated signals, the threads library provides the sigwait subroutine. The sigwait subroutine blocks the calling thread until one of the awaited signals is sent to the process or to the thread. There must not be a signal handler installed on the awaited signal using the sigwait subroutine.
I will remove the sigaction() handler and try only sigwait().
From the code snippet you've posted, it seems you got the use of sigwait() wrong. AFAIU, you need WaitForAbortThread like below:
sigemptyset( &signal_set); // change it from sigfillset()
for (;;) {
stat = sigwait(&signal_set, &sig);
if (sig == SIGABRT) {
printf("here's sigbart.. do whatever you want.\n");
pthread_kill(tid, signal); // thread id and signal
}
}
I don't think pthread_sigmask() is really needed. Since you only want to handle SIGABRT, first init signal_set as empty then simply add SIGABRT, then jump into the infinite loop, sigwait will wait for the particular signal that you're looking for, you check the signal if it's SIGABRT, if yes - do whatever you want. NOTE the uses of pthread_kill(), use it to sent any signal to other threads specified via tid and the signal you want to sent, make sure you know the tid of other threads you want to sent signal. Hope this will help!
I know this question is about a year old, but I often use a pattern, which solves exactly this issue using pthreads and signals. It is a little length but takes care of any issues I am aware of.
I recently used in combination with a library wrapped with SWIG and called from within Python. An annoying issue was that my IRQ thread waiting for SIGINT using sigwait never received the SIGINT signal. The same library worked perfectly when called from Matlab, which didn't capture the SIGINT signal.
The solution was to install a signal handler
#define _NTHREADS 8
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
#include <linux/unistd.h>
#include <sys/signal.h>
#include <sys/syscall.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h> // strerror
#define CallErr(fun, arg) { if ((fun arg)<0) \
FailErr(#fun) }
#define CallErrExit(fun, arg, ret) { if ((fun arg)<0) \
FailErrExit(#fun,ret) }
#define FailErrExit(msg,ret) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr); \
return ret; }
#define FailErr(msg) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr);}
typedef struct thread_arg {
int cpu_id;
int thread_id;
} thread_arg_t;
static jmp_buf jmp_env;
static struct sigaction act;
static struct sigaction oact;
size_t exitnow = 0;
pthread_mutex_t exit_mutex;
pthread_attr_t attr;
pthread_t pids[_NTHREADS];
pid_t tids[_NTHREADS+1];
static volatile int status[_NTHREADS]; // 0: suspended, 1: interrupted, 2: success
sigset_t mask;
static pid_t gettid( void );
static void *thread_function(void *arg);
static void signalHandler(int);
int main() {
cpu_set_t cpuset;
int nproc;
int i;
thread_arg_t thread_args[_NTHREADS];
int id;
CPU_ZERO( &cpuset );
CallErr(sched_getaffinity,
(gettid(), sizeof( cpu_set_t ), &cpuset));
nproc = CPU_COUNT(&cpuset);
for (i=0 ; i < _NTHREADS ; i++) {
thread_args[i].cpu_id = i % nproc;
thread_args[i].thread_id = i;
status[i] = 0;
}
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_mutex_init(&exit_mutex, NULL);
// We pray for no locks on buffers and setbuf will work, if not we
// need to use filelock() on on FILE* access, tricky
setbuf(stdout, NULL);
setbuf(stderr, NULL);
act.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT;
act.sa_handler = signalHandler;
sigemptyset(&act.sa_mask);
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
if (setjmp(jmp_env)) {
if (gettid()==tids[0]) {
// Main Thread
printf("main thread: waiting for clients to terminate\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
if (status[i] == 1)
printf("thread %d: terminated\n",i+1);
}
// On linux this can be done immediate after creation
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
else {
// Should never happen
printf("worker thread received signal");
}
return -1;
}
// Install handler
CallErr(sigaction, (SIGINT, &act, &oact));
// Block SIGINT
CallErr(pthread_sigmask, (SIG_BLOCK, &mask, NULL));
tids[0] = gettid();
srand ( time(NULL) );
for (i = 0; i < _NTHREADS; i++) {
// Inherits main threads signal handler, they are blocking
CallErr(pthread_create,
(&pids[i], &attr, thread_function,
(void *)&thread_args[i]));
}
if (pthread_sigmask(SIG_UNBLOCK, &mask, NULL)) {
fprintf(stderr, "main thread: can't block SIGINT");
}
printf("Infinite loop started - CTRL-C to exit\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
//printf("%d\n",status[i]);
if (status[i] == 2)
printf("thread %d: finished succesfully\n",i+1);
}
// Clean up and exit
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
static void signalHandler(int sig) {
int i;
pthread_t id;
id = pthread_self();
for (i = 0; i < _NTHREADS; i++)
if (pids[i] == id) {
// Exits if worker thread
printf("Worker thread caught signal");
break;
}
if (sig==2) {
sigaction(SIGINT, &oact, &act);
}
pthread_mutex_lock(&exit_mutex);
if (!exitnow)
exitnow = 1;
pthread_mutex_unlock(&exit_mutex);
longjmp(jmp_env, 1);
}
void *thread_function(void *arg) {
cpu_set_t set;
thread_arg_t* threadarg;
int thread_id;
threadarg = (thread_arg_t*) arg;
thread_id = threadarg->thread_id+1;
tids[thread_id] = gettid();
CPU_ZERO( &set );
CPU_SET( threadarg->cpu_id, &set );
CallErrExit(sched_setaffinity, (gettid(), sizeof(cpu_set_t), &set ),
NULL);
int k = 8;
// While loop waiting for exit condition
while (k>0) {
sleep(rand() % 3);
pthread_mutex_lock(&exit_mutex);
if (exitnow) {
status[threadarg->thread_id] = 1;
pthread_mutex_unlock(&exit_mutex);
pthread_exit(NULL);
}
pthread_mutex_unlock(&exit_mutex);
k--;
}
status[threadarg->thread_id] = 2;
pthread_exit(NULL);
}
static pid_t gettid( void ) {
pid_t pid;
CallErr(pid = syscall, (__NR_gettid));
return pid;
}
I run serveral tests and the conbinations and results are:
For all test cases, I register a signal handler by calling sigaction in the main thread.
main thread block target signal, thread A unblock target signal by calling pthread_sigmask, thread A sleep, send target signal.
result: signal handler is executed in thread A.
main thread block target signal, thread A unblock target signal by calling pthread_sigmask, thread A calls sigwait, send target signal.
result: sigwait is executed.
main thread does not block target signal, thread A does not block target signal, thread A calls sigwait, send target signal.
result: main thread is chosen and the registered signal handler is executed in the main thread.
As you can see, conbination 1 and 2 are easy to understand and conclude.
It is:
If a signal is blocked by a thread, then the process-wide signal handler registered by sigaction just can't catch or even know it.
If a signal is not blocked, and it's sent before calling sigwait, the process-wide signal handler wins. And that's why APUE the books require us to block the target signal before calling sigwait. Here I use sleep in thread A to simulate a long "window time".
If a signal is not blocked, and it's sent when sigwait has already been waiting, sigwait wins.
But you should notice that for test case 1 and 2, main thread is designed to block the target signal.
At last for test case 3, when main thread is not blocked the target signal, and sigwait in thread A is also waiting, the signal handler is executed in the main thread.
I believe the behaviour of test case 3 is what APUE talks about:
From APUE ยง12.8:
If a signal is being caught (the process has established a signal
handler by using sigaction, for example) and a thread is waiting for
the same signal in a call to sigwait, it is left up to the
implementation to decide which way to deliver the signal. The
implementation could either allow sigwait to return or invoke the
signal handler, but not both.
Above all, if you want to accomplish one thread <-> one signal model, you should:
block all signals in the main thread with pthread_sigmask (subsequent thread created in main thread inheris the signal mask)
create threads and call sigwait(target_signal) with target signal.
test code
#define _POSIX_C_SOURCE 200809L
#include <signal.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
FILE* file;
void* threadA(void* argv){
fprintf(file, "%ld\n", pthread_self());
sigset_t m;
sigemptyset(&m);
sigaddset(&m, SIGUSR1);
int signo;
int err;
// sigset_t q;
// sigemptyset(&q);
// pthread_sigmask(SIG_SETMASK, &q, NULL);
// sleep(50);
fprintf(file, "1\n");
err = sigwait(&m, &signo);
if (err != 0){
fprintf(file, "sigwait error\n");
exit(1);
}
switch (signo)
{
case SIGUSR1:
fprintf(file, "SIGUSR1 received\n");
break;
default:
fprintf(file, "?\n");
break;
}
fprintf(file, "2\n");
}
void hello(int signo){
fprintf(file, "%ld\n", pthread_self());
fprintf(file, "hello\n");
}
int main(){
file = fopen("daemon", "wb");
setbuf(file, NULL);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = hello;
sigaction(SIGUSR1, &sa, NULL);
sigset_t n;
sigemptyset(&n);
sigaddset(&n, SIGUSR1);
// pthread_sigmask(SIG_BLOCK, &n, NULL);
pthread_t pid;
int err;
err = pthread_create(&pid, NULL, threadA, NULL);
if(err != 0){
fprintf(file, "create thread error\n");
exit(1);
}
pause();
fprintf(file, "after pause\n");
fclose(file);
return 0;
}
run with ./a.out & (run in the background), and use kill -SIGUSR1 pid to test. Do not use raise. raise, sleep, pause are thread-wide.

Race conditions in my signal handlers? (C)

I'm working on a shell lab for a Systems course, and I've been having some really weird race condition bugs that I've been trying to solve since Friday night and can't seem to pin down.
My current code: http://buu700.com/tsh
Everything before START OF MY CODE and after END OF MY CODE is provided by the course instructors, so none of that should be the source of the issues.
We have a test script as well; here is the output of my current test results: http://buu700.com/sdriver
/*****************
* Signal handlers
*****************/
/*
* sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
* a child job terminates (becomes a zombie), or stops because it
* received a SIGSTOP, SIGTSTP, SIGTTIN or SIGTTOU signal. The
* handler reaps all available zombie children, but doesn't wait
* for any other currently running children to terminate.
*/
void
sigchld_handler(int sig)
{
pid_t pid;
int status, termsig;
struct job_t *job;
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGCHLD);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGTSTP);
sigprocmask(SIG_BLOCK, &s, NULL);
while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
if (WIFEXITED(status)) {
deletejob(job_list, pid);
}
if ((termsig = WTERMSIG(status))) {
deletejob(job_list, pid);
safe_printf("Job [%i] (%i) %s by signal %i\n",
pid2jid(pid), pid, "terminated", termsig);
}
if (WIFSTOPPED(status)) {
job = getjobpid(job_list, pid);
job->state = ST;
safe_printf("Job [%i] (%i) %s by signal %i\n",
pid2jid(pid), pid, "stopped", SIGTSTP);
}
}
if (errno != ECHILD)
unix_error("waitpid error");
sigprocmask(SIG_UNBLOCK, &s, NULL);
return;
}
/*
* sigint_handler - The kernel sends a SIGINT to the shell whenver the
* user types ctrl-c at the keyboard. Catch it and send it along
* to the foreground job.
*/
void
sigint_handler(int sig)
{
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGCHLD);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGTSTP);
sigprocmask(SIG_BLOCK, &s, NULL);
kill(-1, sig);
sigprocmask(SIG_UNBLOCK, &s, NULL);
return;
}
/*
* sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
* the user types ctrl-z at the keyboard. Catch it and suspend the
* foreground job by sending it a SIGTSTP.
*/
void
sigtstp_handler(int sig)
{
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGCHLD);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGTSTP);
sigprocmask(SIG_BLOCK, &s, NULL);
kill(-1, sig);
sigprocmask(SIG_UNBLOCK, &s, NULL);
return;
}
I have a suspicion that the bug comes from your racing against other signals in your signal handlers:
sigint_handler(int sig)
{
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGCHLD);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGTSTP);
sigprocmask(SIG_BLOCK, &s, NULL);
When you register the signal handler with sigaction(2), you can provide an sa_mask that the kernel will use to block signals for you while your signal handler is running. This is done atomically and requires no extra work on your part. Simply populate this mask once when registering the signal handler.
Another possibility comes from the SIGTSTP signal; page 350 of my copy of APUE, 2nd edition says, in part:
Only a job-control shell should reset the disposition of [SIGTSTP, SIGTTIN, SIGTTOU] to SIG_DFL.
What's left unsaid in those paragraphs, but I think is fair to assume, is that the shell should set the signal disposition to SIG_DFL for the child processes -- it still needs to do something to handle the signals itself.
Did you properly handle the signal dispositions in the children?

Resources