Reputation: 11
i have this program that forks itself a lot of times (resetting its proccess block via execve every time) and another program that send signals to it so it can fork again. In the handler I blocked every signal (via sigprocmask) so that i dont receive a double signal but somehow it wakes up from the semaphore even if the signals are blocked. (the program that sends the signals doest send KILL and the other signal that cannot be blocked)
So a blocked signal can wake up a process from a waiting state? (I used semop for the semaphore wait)
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
sigset_t all_signals;
void sigusr1_handler(int signo) {
printf("SIGUSER1 received \n");
sigfillset(&all_signals);
sigprocmask(SIG_SETMASK, &all_signals, NULL);
}
int main() {
struct sigaction sa;
bzero(&sa, sizeof(sa));
sa.sa_handler = sigusr1_handler;
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
raise(SIGUSR1);
raise(SIGUSR1);
return 0;
}
Upvotes: 1
Views: 130
Reputation: 33601
A few issues ...
sa_mask
only blocks signals while the signal handler is running.sa_mask
to the current mask before calling the handler.sigprocmask
is valid/safe inside a signal handler, none of the changes will be preserved when the handler exits.printf
from a signal handler is unsafe (because it uses malloc/free
). So, to do a safe printf
, one can do sprintf
followed by write
Here is some modified code:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
sigset_t all_signals;
#define safe_printf(_fmt...) \
do { \
char buf[100]; \
size_t len = sprintf(buf,_fmt); \
write(1,buf,len); \
} while (0)
#define SIGSHOW(_signo) \
{ .sig_name = #_signo, .sig_no = _signo },
#define SIGALL(_cmd) \
_cmd(SIGHUP) \
_cmd(SIGINT) \
_cmd(SIGQUIT) \
_cmd(SIGILL) \
_cmd(SIGTRAP) \
_cmd(SIGABRT) \
_cmd(SIGIOT) \
_cmd(SIGBUS) \
_cmd(SIGFPE) \
_cmd(SIGKILL) \
_cmd(SIGUSR1) \
_cmd(SIGSEGV) \
_cmd(SIGUSR2) \
_cmd(SIGPIPE) \
_cmd(SIGALRM) \
_cmd(SIGTERM) \
_cmd(SIGSTKFLT) \
_cmd(SIGCHLD) \
_cmd(SIGCONT) \
_cmd(SIGSTOP) \
_cmd(SIGTSTP) \
_cmd(SIGTTIN) \
_cmd(SIGTTOU) \
_cmd(SIGURG) \
_cmd(SIGXCPU) \
_cmd(SIGXFSZ) \
_cmd(SIGVTALRM) \
_cmd(SIGPROF) \
_cmd(SIGWINCH) \
_cmd(SIGIO) \
_cmd(SIGPWR) \
_cmd(SIGSYS)
struct sigshow {
const char *sig_name;
int sig_no;
};
struct sigshow siglist[] = {
SIGALL(SIGSHOW)
{ .sig_name = NULL },
};
void
showmask(const sigset_t *set,const char *who)
{
char buf[1000];
char *rhs = buf;
char *lhs = buf;
sigset_t mask;
if (set == NULL) {
set = &mask;
sigprocmask(SIG_SETMASK,NULL,&mask);
}
rhs += sprintf(rhs,"showmask (from %s):\n",who);
for (struct sigshow *show = siglist; show->sig_name != NULL; ++show) {
if (sigismember(set,show->sig_no))
rhs += sprintf(rhs," %s",show->sig_name);
if ((rhs - lhs) >= 66) {
rhs += sprintf(rhs,"\n");
lhs = rhs;
}
}
rhs += sprintf(rhs,"\n");
write(1,buf,rhs - buf);
}
void
sigusr1_handler(int signo)
{
//safe_printf("SIGUSER1 received \n");
showmask(NULL,"handler/ENTER");
sigfillset(&all_signals);
sigprocmask(SIG_SETMASK, &all_signals, NULL);
showmask(NULL,"handler/EXIT");
}
int
main()
{
struct sigaction sa;
showmask(NULL,"main/ENTER");
bzero(&sa, sizeof(sa));
sa.sa_handler = sigusr1_handler;
sa.sa_flags = 0;
sigaddset(&sa.sa_mask,SIGUSR1);
showmask(&sa.sa_mask,"main/sa_mask");
sigaction(SIGUSR1, &sa, NULL);
showmask(NULL,"main/preraise1");
raise(SIGUSR1);
showmask(NULL,"main/preraise2");
raise(SIGUSR1);
showmask(NULL,"main/postraise2");
return 0;
}
Here is the program output:
showmask (from main/ENTER):
showmask (from main/sa_mask):
SIGUSR1
showmask (from main/preraise1):
showmask (from handler/ENTER):
SIGUSR1
showmask (from handler/EXIT):
SIGHUP SIGINT SIGQUIT SIGILL SIGTRAP
SIGABRT SIGIOT SIGBUS SIGFPE SIGUSR1 SIGSEGV SIGUSR2 SIGPIPE SIGALRM
SIGTERM SIGSTKFLT SIGCHLD SIGCONT SIGTSTP SIGTTIN SIGTTOU SIGURG SIGXCPU
SIGXFSZ SIGVTALRM SIGPROF SIGWINCH SIGIO SIGPWR SIGSYS
showmask (from main/preraise2):
showmask (from handler/ENTER):
SIGUSR1
showmask (from handler/EXIT):
SIGHUP SIGINT SIGQUIT SIGILL SIGTRAP
SIGABRT SIGIOT SIGBUS SIGFPE SIGUSR1 SIGSEGV SIGUSR2 SIGPIPE SIGALRM
SIGTERM SIGSTKFLT SIGCHLD SIGCONT SIGTSTP SIGTTIN SIGTTOU SIGURG SIGXCPU
SIGXFSZ SIGVTALRM SIGPROF SIGWINCH SIGIO SIGPWR SIGSYS
showmask (from main/postraise2):
UPDATE:
Oh I understand thanks for explaining. Ik about the printf being unsafe but it was only for the test.
Although, for this limited test case, it might be safe (because the base level is not doing any printf
or malloc
), I wanted to be sure. Especially, since I added some base level printf
.
So are there any effective ways of blocking all signals in a handler for the whole process or it can only be done in the main? – David
I was thinking that doing getcontext
, modifying uc_sigmask
, and calling setcontext
might work. So, I tried it. I just got [effectively] an infinite loop where the handler is never exited.
More/better trickery with this might be possible. But... I don't think it's worth the effort as it would be a bit fragile if it could be made to work at all.
Without seeing all of your code, it's difficult to give a definitive answer. That is, what the sender is doing, what the receiver is doing when not processing a signal, etc.
But, a better proposition is to rearchitect the "signaling" mechanism.
That is, SIGUSR1
is not queued. So, a program that does rapid signalling could race against the receiver and some signals could be lost. Better to use a realtime signal because they are queued
Also, signals are asynchronous to the recipient. They are designed for exceptional conditions and not to send "messages" between processes.
There are race conditions galore in the recipient between receiving the signal (in the handler) and masking so the child process you do fork/execve
for doesn't get the signals (even with RT signals).
A better way would be to use SysV IPC to send/receive messsages (i.e. msgsnd/msgrcv
). With this mechanism, the recipient is not racing against itself.
Also, we can eliminate the semaphore altogether.
I've used SysV IPC for many similar purposes in commercial products for realtime systems with great success.
For a small example of this, loosely derived from my production code, see my answer: In C, is storing data in local variables in threads akin to creating a local copy? AKA Does this Threadpool synchronization make sense?
Upvotes: 0