for a homework assignment I have to use only linux system calls to manage signals in a game, programmed in C.
One thing I have to do, it's to call indefinitly sigsuspend in a thread and wait for a SIGALRM,then if there is a sigalarm (signal alarm) I have to printf() something (doesn't matter what).
However, I tried several things but it doesn't work, I don't know how to "deblock" sigsuspend and print what i want. And there is a timer in the game which send sigalrm. My code works but not the part with the sigsuspend
Of course I have also a sigarlm handler which does some stuff with SDL when it receives a sigarlm.
I don't really how I have to use masks with sigsuspend
Code :
//initialising sigaction struct
int init (void)
{
// Signal handler
sigact.sa_handler = sigalrm_handler;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
// Linked list
head = NULL;
sigaction(SIGALRM, &sigact, NULL);
pthread_create(&tid_sigrecv, NULL, (void *)deamon, NULL);
return 1; // Implementation ready ?
}
The sigsuspend part
void deamon(void * arg)
{
int sig;
while(1) {
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigsuspend(&mask);
printf("in thread\n");
}
}
Currently it only does not print "in thread" when a sigarlm is received, what should I do to use sigsuspend properly ? I am obliged to use sigsuspend
You create a thread that should wait for the signal SIGALRM thanks to sigsuspend(), but instead you open the thread to all signals but SIGALRM.
Replace
sigemptyset(&mask); // clear mask
sigaddset(&mask, SIGALRM); // set mask to SIGALRM
sigsuspend(&mask); // wait for any signal but blocks SIGALRM
with
sigfillset(&mask); // mask set to all signals
sigdelset(&mask, SIGALRM); // remove SIGALRM from the mask
sigsuspend(&mask); // block all signals but SIGALRM
this way sigsuspend() waits for a non blocked signal (SIGALRM).
For testing, you could send the thread the SIGALRM signal using
pthread_kill(tid_sigrecv, SIGALRM);
from the main thread, for instance.
Related
I got this issue:
I made a program in c, where the main process creates some child process, and these, after a while, are able to send a signal to the main process:
the signal is sent with this code:
kill(getppid(), SIGUSR1);
and the main process, in the while loop is waiting the SIGUSR1 message...
everything is fine, but if I increase the child number and automatically the possibility to have more signals in the same time, the program crash printing the message:
User defined signal 1
the main code is like this:
void signalHandler(int sig, siginfo_t* info, void* vp) {
if (sig == SIGUSR1) {
printf("SIGUSR1 has arrived\n");
} else if (sig == SIGUSR2) {
printf("SIGUSR2 has arrived\n");
}
}
int main(int argc, char const *argv[]) {
struct sigaction action, old_action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_sigaction = signalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART | SA_NODEFER;
while (1) {
sigaction(SIGUSR1, &action, &old_action);
sigaction(SIGUSR2, &action, &old_action);
}
}
I think the problem is that the signal is sent when the master is still working on the previous signal...but how can I do to fix this thing
thank you very much
It means that the child is sending the signal before the parent process was able to call sigaction() to configure the signal handler. When this happens, the default signal reaction to SIGUSR1 terminates the program:
SIGUSR1 P1990 Term User-defined signal 1
https://man7.org/linux/man-pages/man7/signal.7.html
However, there are many problems with your code. printf() is not safe to be called inside a signal handler (it's AS-Unsafe as defined by POSIX):
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_03
Also, using SA_NODEFER may create nested signals (another signal handler is called while some signal handler is running) but your program does not protect against a flood. Given enough children this will generate a stack overflow. Finally, the main program keeps running a non-stop infinite loop reconfiguring the signals, while it should have configured them only once outside the loop and blocked inside the loop (for example sigwait() or pselect()):
https://man7.org/linux/man-pages/man2/select.2.html
Finally, if you expect to run a large number of children that might flood the parent with signals, then it would be better to use the real time signal generation function (sigqueue()) rather than kill(). The difference is that with sigqueue(), all signals are queued and SA_NODEFER is not necessary to avoid discarding signals while some other signal handler is running:
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_02
Final conclusion: the code should be completely rewritten.
The POSIX pselect function take a signal mask argument. The signal mask is "atomically" set as the current mask before execution of the function begins, and is restored as the function returns.
This allows an otherwise masked signal to be unmasked while the function executes, and masked again when the function returns. It's guaranteed* that if a signal unmasked in this way is caught, the pselect function will be interrupted by the signal and (unless the signal action is specified with the SA_RESTART flag) will return an EINTR error.
(*: or is it? the language in the document linked above would seem to allow that a signal being received between when pselect unblocked due to seeing a file readiness or timeout and when it replaced the signal mask with the original would not necessarily cause EINTR, since EINTR is required if "The function was interrupted while blocked ..." - however, that ultimately doesn't affect this question).
My question is: supposing that two separate signals are temporarily unmasked during pselect execution, is it possible that both signals will be caught before the pselect function returns and the previous signal mask is restored - or is there some kind of guarantee that only one signal will be caught in this case (leaving the other one pending)? (For purposes of the question, suppose that SA_RESTART is not set for the signal action, and that all signals were specified to be masked during execution of the signal handler when it was established via sigaction).
I can find nothing which suggests that only one signal may be processed, but I may have missed something, and I am writing some code for which this would be a very useful guarantee. I'd be interested to know if POSIX itself makes any guarantee, and also if different OSes provide such a guarantee independently.
No, but it also doesn’t specify that multiple signals can or must. Since it is unspecified, it is best to follow the general rule, which allows all pending unmasked signals to be processed. If you attempt to strictly depend upon this, you are likely on a bad path because the timing of asynchronous events is difficult to predict.
In general, it would be very difficult to make an implementation that imposed an ‘only one' restriction because the os runtime would have to leave one or more signals pending but unmasked until some unspecified point. Remember that the signal handler which runs when pselect is interrupted could do a siglongjmp rather than returning, so the kernel would have to keep a complicated, possibly unbounded data structure to track which signal mask to enforce.
Below is a modified version of your test program. In this one, each event emits a string via write() so there are no buffering problems. The program sets its “main” environment to mask SIGUSR1, SIGUSR2; but while pselect is running, it permits SIGUSR1, SIGUSR2, SIGTERM.
The program forks, with the parent (default:) sitting in a loop invoking pselect(), then outputting ‘.’ after it completes.
The child sits in a loop, delivering SIGUSR1, SIGUSR2 to the parent, then sleeping for a bit. It outputs ‘^’ after delivering the signals.
The handler emits a prefix “(1” or “(2” for SIGUSR1, SIGUSR2 resp; then sleeps for a bit, and outputs “)” to indicate the sleep has completed.
The output I see on macos (10.12.6, but I doubt it matters much) is:
^(2)(1).^(2)(1).^(2)(1).^(2)(1).Terminated: 15
which indicates that the signal handler for each of SIGUSR1 and SIGUSR2 are being run for every invocation of pselect(). This is what I would expect; as it is designed to not admit a window of uncertainty as would be the case with bracketting select() with sigprocmasks().
#include <stdio.h>
#include <signal.h>
#include <sys/select.h>
#include <unistd.h>
void handle(int signo)
{
char s[2];
s[0] = '(';
s[1] = signo == SIGUSR1? '1' : '2';
write(1, s, 2);
sleep(1);
write(1, ")", 1);
}
int main(int argc, char **argv)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_SETMASK, &mask, NULL);
sigfillset(&mask);
sigdelset(&mask, SIGUSR1);
sigdelset(&mask, SIGUSR2);
sigdelset(&mask, SIGTERM);
signal(SIGUSR1, handle);
signal(SIGUSR2, handle);
pid_t t = fork();
switch (t) {
default:
while (1) {
/* no USR1, USR2 */
pselect(0, NULL, NULL, NULL, NULL, &mask);
/* no USR1, USR2 */
write(1, ".", 1);
}
break;
case 0:
t = getppid();
for (int i = 0; i < 4; i++) {
kill(t, SIGUSR1);
kill(t, SIGUSR2);
write(1, "^", 1);
sleep(5);
}
kill(t, SIGTERM);
break;
case -1:
perror("fork\n");
}
return 0;
}
I've continued searching and found no additional information, so I can only conclude that there are no guarantees in POSIX generally.
Under Linux, if I understand the code below correctly, only one signal can be handled (assuming that the signal handler itself doesn't unmask signals): the relevant code and a revealing comment is in fs/select.c, in the do_pselect function:
ret = core_sys_select(n, inp, outp, exp, to);
ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
if (ret == -ERESTARTNOHAND) {
/*
* Don't restore the signal mask yet. Let do_signal() deliver
* the signal on the way back to userspace, before the signal
* mask is restored.
*/
if (sigmask) {
memcpy(¤t->saved_sigmask, &sigsaved,
sizeof(sigsaved));
set_restore_sigmask();
}
} else ...
It essentially returns from the system call, allowing the signal handler to execute, after which the original signal mask will immediately be restored (from current->saved_sigmask, because set_restore_sigmask() sets a flag indicating that this should occur).
The following test program verifies this:
#include <stdio.h>
#include <signal.h>
#include <sys/select.h>
volatile sig_atomic_t got_usr1 = 0;
volatile sig_atomic_t got_usr2 = 0;
void handle_usr1(int signo, siginfo_t *info, void *v)
{
got_usr1 = 1;
}
void handle_usr2(int signo, siginfo_t *info, void *v)
{
got_usr2 = 1;
}
int main(int argc, char **argv)
{
// mask SIGUSR1 and SIGUSR2:
sigset_t curmask;
sigemptyset(&curmask);
sigaddset(&curmask, SIGUSR1);
sigaddset(&curmask, SIGUSR2);
sigprocmask(SIG_SETMASK, &curmask, NULL);
// Create a mask for all but SIGUSR1 and SIGUSR2:
sigset_t mask;
sigfillset(&mask);
sigdelset(&mask, SIGUSR1);
sigdelset(&mask, SIGUSR2);
// Set up signal handlers:
struct sigaction action;
action.sa_sigaction = handle_usr1;
sigfillset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &action, NULL);
action.sa_sigaction = handle_usr2;
sigaction(SIGUSR2, &action, NULL);
// Make signals pending:
raise(SIGUSR1);
raise(SIGUSR2);
// pselect with no file descriptors and no timeout:
pselect(0, NULL, NULL, NULL, NULL, &mask);
int count = got_usr1 + got_usr2;
printf("Handled %d signals while in pselect.\n", count);
return 0;
}
On Linux, the output of the above is consistently:
Handled 1 signals while in pselect.
This also seems to be the case on FreeBSD; however, I'm not willing to count on this being the case on all other platforms. The solution I have found to ensuring that only one signal can be handled is to use siglongjmp to jump out of the signal handler as well as out of the pselect call while also restoring the signal mask so that no further signals can be processed.
Essentially, that code looks like this:
jmp_buf jbuf; // signal handlers have access to this
if (sigsetjmp(jbuf, 1) != 0) {
// We received a signal while in pselect ...
}
int r = pselect(nfds, &read_set_c, &write_set_c, &err_set, wait_ts, &sigmask);
The signal handlers must execute a siglongjmp:
void signal_handler(int signo, siginfo_t *siginfo, void *v)
{
siglongjmp(jbuf, 1);
}
This feels crufty, but seems to work on all platforms that I've tested it on (Linux, MacOS and FreeBSD) - furthermore it seems to be supported by POSIX generally.
Is there a way to block certain signals and unblock other signals in the same set?
I just dont seem to get my head around it!
An example
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
// Block signal SIGUSR1 in this thread
pthread_sigmask(SIG_BLOCK, &set, NULL);
sigaddset(&set, SIGALRM);
// Listen to signal SIGUSR2
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
pthread_t printer_thread1, printer_thread2;
pthread_create(&printer_thread1, NULL, print, (void *)&f1);
pthread_create(&printer_thread2, NULL, print, (void *)&f2);
bool tl = true;
while(1)
{
if(tl)
{
// thread1 does something
kill(pid, SIGUSR1);
// main thread waits for SIGALRM
sigwait(&set, &sig);
tl = !tl;
}
else
{
// thread2 does something
kill(pid, SIGUSR2);
// main thread waits for SIGALRM
sigwait(&set, &sig);
tl = !tl;
}
}
I am not allowed to use Mutexs, semaphores etc only signals.
Can someone help? :)
Is there a way to block certain signals and unblock other signals in
the same set?
With pthread_sigmask, you can choose to either:
add a set of signals to the set of blocked signals, using the constant SIG_BLOCK
remove a set of signals to the set of blocked signals, using the constant SIG_UNBLOCK
define the set of signals to be blocked, using the constant SIG_SET
In other words, there's a current set of blocked signals for the thread, and you can modify it as specified above, one operation at a time.
The important point is that newly created threads inherit the signal mask of the creating thread, so you can set the mask of the new thread right before creating it, or in the function that the new thread will run, at your convenience.
Regarding your example, I suppose that you are trying to have printer_thread1 block SIGUSR2 and SIGALRM, and have printer_thread2 block SIGUSR1 and SIGALRM, and have the main thread block SIGUSR1 and SIGUSR2, so that each thread can send a signal that will be caught by a single thread (without the code of print, f1 and f2, it's impossible to know for sure what is your intent in your example).
You should be able to achieve that by the following code:
sigset_t set;
pthread_t printer_thread1, printer_thread2;
// Block signal SIGUSR1 & SIGALRM in printer_thread1
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SET, &set, NULL);
pthread_create(&printer_thread1, NULL, print, (void *)&f1);
// Block signal SIGUSR2 & SIGALRM in printer_thread2
sigaddset(&set, SIGUSR2);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SET, &set, NULL);
pthread_create(&printer_thread2, NULL, print, (void *)&f2);
// Block signal SIGUSR1 & SIGUSR2 in the main thread
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGUSR2);
// Listen to signal SIGALRM
pthread_sigmask(SIG_SET, &set, NULL);
bool tl = true;
while(1)
{
if(tl)
{
// thread1 does something
kill(pid, SIGUSR1);
// main thread waits for SIGALRM
sigwait(&set, &sig);
tl = !tl;
}
else
{
// thread2 does something
kill(pid, SIGUSR2);
// main thread waits for SIGALRM
sigwait(&set, &sig);
tl = !tl;
}
}
see these man pages :
pthread_sigmask
sigprocmask
for further details.
I think what you want to do here is
// Block signal SIGUSR1 in this thread
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &set, NULL);
// Listen to signal SIGALRM
sigemptyset(&set);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
The set is only used to tell it what to block or unblock. Once passed to the command, you are free to reset it and build up another signal mask. If you skip the sigemptyset, the set will still contain SIGUSR1, which will subsequently be unblocked again. Well, I think that is how it works, at least - it has been a long time since I used signals.
I have a thread in an application that has a loop like this:
...
while (1)
{
checkDatabase();
checkChildren();
sleep(3);
}
...
checkDatabase() is self-explanatory; checkChildren() simply calls waitpid(-1, &status, WNOHANG) to deal with child processes that have either exited or received a signal.
The application works fairly well, but it has default signal handling. The problem is that this parent process has a number of threads (don't worry about child processes for now) and I don't have any experience with synchronous signals, let alone in a POSIX threads application. I have used signal() before but apparently it's non-portable and it doesn't do what I need anyway. I have no experience at all with sigaction methods, and I can't find good documentation on how to fill in the structs and so on.
What I need to do is to synchronously catch terminating signals like SIGINT, SIGTERM and SIGQUIT in the above loop (and I need to ignore SIGPIPE altogether so that I can catch the EPIPE error from IO methods), so it would look like this:
...
while (1)
{
checkDatabase();
checkChildren();
checkForSignals();
sleep(3);
}
...
All other threads should not have anything to do with the signal; only the thread that executes this loop should be aware of it. And, obviously, it needs to be a non-blocking check so the loop doesn't block during its first iteration. The method called if a signal is found will sort out the other threads and destroy mutexes, and all that.
Could anyone please give me a heads-up? Many thanks.
(Following the question's comments, and for completeness, this solution tries to avoid signal handlers.)
It is possible to block signals from being raised through sigprocmask() (or, rather, pthread_sigmask() since you're using threads). From there on, the signals that were raised but blocked are available through sigpending().
Therefore, you could do something like (error checking omitted for brevity):
sigset_t blocked;
sigemptyset(&blocked);
sigaddset(&blocked, SIGINT);
sigaddset(&blocked, SIGTERM);
sigaddset(&blocked, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &blocked, NULL); // Block SIGINT, SIGTERM and SIGQUIT.
signal(SIGPIPE, SIG_IGN); // Ignore SIGPIPE.
Then, later:
void checkForSignals(void)
{
sigset_t pending;
sigpending(&pending);
if (sigismember(&pending, SIGINT)) {
// Handle SIGINT...
}
if (sigismember(&pending, SIGTERM)) {
// Handle SIGTERM...
}
if (sigismember(&pending, SIGQUIT)) {
// Handle SIGQUIT...
}
}
Since sigpending() does not block, this seems to match your requirements.
Create a signal handler for SIGINT, SIGTERM and SIGQUIT, using the same function. In that signal function just set a flag that can be polled in your loop.
Something like this:
/* Global variable, will be set to non-zero if SIGINT, SIGTERM or SIGQUIT is caught */
int term_signal_set = 0;
void my_signal_handler(int)
{
term_signal_set = 1;
}
/* ... */
signal(SIGINT, my_signal_handler);
signal(SIGTERM, my_signal_handler);
signal(SIGQUIT, my_signal_handler);
signal(SIGPIPE, SIG_IGN); /* So functions return EPIPE */
while (1)
{
/* ... */
if (term_signal_set > 0)
break; /* Or do something else */
sleep(3);
}
In a multithreaded application receiving a signal, there is no predetermination, which thread receives the signal. Typical workaraounds include setting a global variable in the signal handler and checking it from a dedicated thread.
So in your case the signal handler (called from whatever thread) would just set something like a global variable for the signal received, and in CheckForSignals() you would test it.
sigaction is the way to go. man sigaction should help you. Here is an example from the web
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
struct sigaction act;
void sighandler(int signum, siginfo_t *info, void *ptr)
{
printf("Received signal %d\n", signum);
printf("Signal originates from process %lu\n",
(unsigned long)info->si_pid);
}
int main()
{
printf("I am %lu\n", (unsigned long)getpid());
memset(&act, 0, sizeof(act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &act, NULL);
// Waiting for CTRL+C...
sleep(100);
return 0;
}
As per the man page of sigsuspend(), it will replace the current signal mask set with its 1st parameter. In APUE, I saw an example as follows. The code maybe a little too long but I don't want to miss anything.
int pr_mask(char *s)
{
sigset_t sigset;
sigprocmask(0, NULL, &sigset);
printf("%s: ", s);
if(sigismember(&sigset, SIGINT)) printf("SIGINT ");
if(sigismember(&sigset, SIGQUIT)) printf("SIGQUIT ");
if(sigismember(&sigset, SIGUSR1)) printf("SIGUSR1 ");
if(sigismember(&sigset, SIGALRM)) printf("SIGALRM ");
/* ..... */
printf("\n");
return 0;
}
void sig_quit(int signo)
{
pr_mask("in sig quit");
}
int main()
{
sigset_t new, old, tempset;
signal(SIGQUIT, sig_quit);
sigemptyset(&tempset);
sigaddset(&tempset, SIGINT);
sigemptyset(&new);
sigaddset(&new, SIGQUIT);
sigprocmask(SIG_BLOCK, &new, &old);
pr_mask("in critical section");
/* critical section */
sigsuspend(&tempset);
pr_mask("after return form sigsuspend");
sigprocmask(SIG_UNBLOCK, &new, NULL);
pr_mask("program exit");
return 0;
}
Output is:
in critical section: SIGQUIT
in sig quit: SIGINT SIGQUIT
after return form sigsuspend: SIGQUIT
program exit:
The problem is the second line. The SIGQUIT is still in the signal mask set.
Should it be only SIGINT as sigsuspend has already replaced the signal mask with tempset, which is set to SIGINT only?
While executing the SIGQUIT handler, SIGQUIT itself is blocked. This is to avoid re-entering the handler by accident...
From the POSIX spec for sigaction (which you really should use instead of signal):
When a signal is caught by a signal-catching function installed by sigaction(), a new signal mask is calculated and installed for the duration of the signal-catching function (or until a call to either sigprocmask() or sigsuspend() is made). This mask is formed by taking the union of the current signal mask and the value of the sa_mask for the signal being delivered, and unless SA_NODEFER or SA_RESETHAND is set, then including the signal being delivered.
The POSIX spec for signal is a little less specific:
When a signal occurs, and func points to a function, it is implementation-defined whether the equivalent of a:
signal(sig, SIG_DFL);
is executed or the implementation prevents some implementation-defined set of signals (at least including sig) from occurring until the current signal handling has completed.
So it is legal for the OS to simply add the signal to the mask for the duration of the signal-handling function, and apparently that is what Linux does.
because the output "in sig quit: SIGINT SIGQUIT " is printed while executing the SIGQUIT handler.it would add the SIGQUIT to the signal mask, that means the current mask become the union of &tempset and the SIGQUIT.