I have a hard time understanding how Wait Queues works in the Linux kernel, I tried to implement a simple example of Wait Queues but with an unsuccessful attempt. I know that when using wait_event_interruptible(w, condition), the process is put to sleep (TASK_INTERRUPTIBLE) until the condition evaluates to true.
I don't understand how these both things works.
The condition is checked each time the waitqueue wq is woken up.
wake_up has to be called after changing any variable that could change the result of the wait condition.
How can we wake up the process after it put to sleep exactly?
This code goes to sleep and never wakes up.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
// Declare the wait queue with a macro
DECLARE_WAIT_QUEUE_HEAD(wait_queue_t);
// Variables
int i = 5;
static void change_val(int *i)
{
*i = 4;
}
static int __init sys_module_init(void)
{
wait_event_interruptible(wait_queue_t, (i == 4)); // Sleeping until "i" is equal to 4
change_val(&i); // Changing the value of "i"
wake_up(&wait_queue_t); // Waking up the wait_queue
return 0;
}
// ... other kernel module code
In the Chapter 10 Signals of the APUE book, there is a sample code:
#include <signal.h>
#include <unistd.h>
static void sig_alrm(int signo) {
/* nothing to do, just return to wake up the pause */
}
unsigned int sleep1(unsigned int seconds) {
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
return(seconds);
alarm(seconds); /* start the timer */
pause(); /* next caught signal wakes us up */
return(alarm(0)); /* turn off timer, return unslept time */
}
int main() {
sleep1(1);
return 0;
}
which simply implement an "imcomplete" sleep().
The book says that "This function looks like the sleep function, but this
simple implementation has three problems."
If the caller already has an alarm set, that alarm is erased by the first call to alarm. We can correct this by looking at alarm’s return value. If the number of seconds until some previously set alarm is less than the argument, then we should wait only until the existing alarm expires. If the previously set alarm will go off after ours, then before returning we should reset this alarm to occur at its designated time in the future.
We have modified the disposition for SIGALRM. If we’re writing a function for others to call, we should save the disposition when our function is called and restore it when we’re done. We can correct this by saving the return value from signal and resetting the disposition before our function returns.
There is a race condition between the first call to alarm and the call to pause. On a busy system, it’s possible for the alarm to go off and the signal handler to be called before we call pause. If that happens, the caller is suspended forever in the call to pause (assuming that some other signal isn’t caught).
I have some doubt about the above 3 statements:
Can somebody provide some example code for the statement1?
I don't know what the statement2 says, can someone give me robust explanation?
I don't know why statement3 will cause a race condition.
Thanks a lot!
to answer your question in the title.
The returned value is the number of seconds left in the prior call to alarm()
Your Q1:
int r1 = alarm(20);
printf("r1 = %d\n", r1);
int r2 = alarm(10);
printf("r2 = %d\n", r2);
This will usually print r1 = 0 and r2 = 20 because there was no alarm set at the first call and there were still 20 seconds left on the previous call at the second. At that point, there's 10 seconds left until the alarm goes off. There is only ever one alarm signal scheduled. You can't set alarms for 10 and 20 and 53 seconds hence; you can at most have one of these pending.
Your Q2:
If the code calling sleep1() already had a signal handler set to handle SIGALRM, you've thrown that information away. You should capture the return value from signal(SIGALRM, sig_alrm) and restore that when sleep1() is done. (It isn't clear what you should do if it was set to SIG_IGN.)
Your Q3:
Scheduling is not determinate. Your process could call alarm(1), and then be pre-empted. It could be more than a second before your program is rescheduled, and it will find that the alarm has gone off and handle the alarm before returning to executed pause(). Your call to pause() then will hang the program indefinitely because there is no alarm pending to wake it. You'll have to send a signal somehow.
So I'm trying to call an alarm to display a message "still working.." every second.
I included signal.h.
Outside of my main I have my function: (I never declare/define s for int s)
void display_message(int s); //Function for alarm set up
void display_message(int s) {
printf("copyit: Still working...\n" );
alarm(1); //for every second
signal(SIGALRM, display_message);
}
Then, in my main
while(1)
{
signal(SIGALRM, display_message);
alarm(1); //Alarm signal every second.
That's in there as soon as the loop begins. But the program never outputs the 'still working...' message. What am I doing incorrectly? Thank you, ver much appreciated.
Signal handlers are not supposed to contain "business logic" or make library calls such as printf. See C11 §7.1.4/4 and its footnote:
Thus, a signal handler cannot, in general, call standard library functions.
All the signal handler should do is set a flag to be acted upon by non-interrupt code, and unblock a waiting system call. This program runs correctly and does not risk crashing, even if some I/O or other functionality were added:
#include <signal.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
volatile sig_atomic_t print_flag = false;
void handle_alarm( int sig ) {
print_flag = true;
}
int main() {
signal( SIGALRM, handle_alarm ); // Install handler first,
alarm( 1 ); // before scheduling it to be called.
for (;;) {
sleep( 5 ); // Pretend to do something. Could also be read() or select().
if ( print_flag ) {
printf( "Hello\n" );
print_flag = false;
alarm( 1 ); // Reschedule.
}
}
}
Move the calls to signal and alarm to just before your loop. Calling alarm over and over at high speed keeps resetting the alarm to be in one second from that point, so you never reach the end of that second!
For example:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void display_message(int s) {
printf("copyit: Still working...\n" );
alarm(1); //for every second
signal(SIGALRM, display_message);
}
int main(void) {
signal(SIGALRM, display_message);
alarm(1);
int n = 0;
while (1) {
++n;
}
return 0;
}
Do not call alarm() twice, just call it once in main() to initiate the callback, then once in display_message().
Try this code on Linux (Debian 7.8) :
#include <stdio.h>
#include <signal.h>
void display_message(int s); //Function for alarm set up
void display_message(int s)
{
printf("copyit: Still working...\n" );
alarm(1); //for every second
signal(SIGALRM, display_message);
}
int main()
{
signal(SIGALRM, display_message);
alarm(1); // Initial timeout setting
while (1)
{
pause();
}
}
The result will be the following one :
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
The alarm() call is for a one off signal.
To repeat an alarm, you have to call alarm() again each time the signal occurs.
Another issue, also, is that you're likely to get EINTR errors. Many system functions get interrupted when you receive a signal. This makes for much more complicated programming since many of the OS functions are affected.
In any event, the correct way to wait for the next SIGALRM is to use the pause() function. Something the others have not mentioned (instead they have tight loops, ugly!)
That being said, what you are trying to do would be much easier with a simple sleep() call as in:
// print a message every second (simplified version)
for(;;)
{
printf("My Message\n");
sleep(1);
}
and such a loop could appear in a separate thread. Then you don't need a Unix signal to implement the feat.
Note: The sleep() function is actually implemented using the same timer as the alarm() and it is clearly mentioned that you should not mix both functions in the same code.
sleep(3) may be implemented using SIGALRM; mixing calls to alarm() and sleep(3) is a bad idea.
(From Linux man alarm)
void alarm_handler(int)
{
alarm(1); // recurring alarm
}
int main(int argc, char *argv[])
{
signal(SIGALRM, alarm_handler);
alarm(1);
for(;;)
{
printf("My Message\n");
// ...do other work here if needed...
pause();
}
// not reached (use Ctrl-C to exit)
return 0;
}
You can create variations. For example, if you want the first message to happen after 1 second instead of immediately, move the pause() before the printf().
The "other work" comment supposes that your other work does not take more than 1 second.
It is possible to get the alarm signal on a specific thread if work is required in parallel, however, this can be complicated if any other timers are required (i.e. you can't easily share the alarm() timer with other functions.)
P.S. as mentioned by others, doing your printf() inside the signal handler is not a good idea at all.
There is another version where the alarm() is reset inside main() and the first message appears after one second and the loop runs for 60 seconds (1 minute):
void alarm_handler(int)
{
}
int main(int argc, char *argv[])
{
signal(SIGALRM, alarm_handler);
for(int seconds(0); seconds < 60; ++seconds)
{
alarm(1);
// ...do other work here if needed...
pause();
printf("My Message\n");
}
// reached after 1 minute
return 0;
}
Note that with this method, the time when the message will be printed is going to be skewed. The time to print your message is added to the clock before you restart the alarm... so it is always going to be a little over 1 second between each call. The other loop is better in that respect but it still is skewed. For a perfect (much better) timer, the poll() function is much better as you can specify when to wake up next. poll() can be used just and only with a timer. My Snap library uses that capability (look for the run() function, near the bottom of the file). In 2019. I moved that one .cpp file to the eventdispatcher library. The run() function is in the communicator.cpp file.
POSIX permits certain of its functions to be called from signal handling context, the async-signal safe functions, search for "async-sgnal safe" here. (These may be understood as "system calls" rather than library calls). Notably, this includes write(2).
So you could do
void
display_message (int s) {
static char const working_message [] = "copyit: Still working...\n";
write (1, working_message, sizeof working_message - sizeof "");
alarm(1); /* for every second */
}
By the way, precise periodic alarms are better implemented using setitimer(2),
since these will not be subject to drift. Retriggering the alarm via software, as done here, will unavoidably accumulate error over time because of the time spent executing the software as well as scheduling latencies.
In POSIX sigaction(2) superceedes signal(2) for good reason:
the original Unix signal handling model was simple. In particular,
a signal handler was reset to its original "deposition" (e.g., terminate
the process) once it was fired. You would have to re-associate
SIGALRM with display_message() by calling signal() just before
calling alarm() in display_message().
An even more important reason for using sigaction(2) is the
SA_RESTART flag. Normally, system calls are interrupted when
a signal handler is invoked. I.e., when then signal handler returns,
the system call returns an error indication (often -1) and errno is
set to EINTR, interrupted system call. (One reason for this
is to be able to use SIGALRM to effect time outs, another is
to have a higher instance, such as a user, to "unblock" the
current process by sending it a signal, e.g.,
SIGINT by pressing control-C at the terminal).
In your case, you want signal handling to be transparent
to the rest of the code, so you would set the SA_RESTART flag
when invoking sigaction(2). This means the kernel should
restart the interrupted system call automatically.
ooga is correct that you keep reloading the alarm so that it will never go off. This works. I just put a sleep in here so you don't keep stepping on yourself in the loop but you might want to substitute something more useful depending on where you are headed with this.
void display_message(int s)
{
printf("copyit: Still working...\n" );
// alarm(1); //for every second
// signal(SIGALRM, display_message);
}
int main(int argc, char *argv[])
{
int ret;
while(1)
{
signal(SIGALRM, display_message);
alarm(1);
if ((ret = sleep(3)) != 0)
{
printf("sleep was interrupted by SIGALRM\n");
}
}
return (0);
}
In the following program, pause is interrupted once, but then pause never returns. I have set alarm to interrupt pause, so i am confused why pause never returns?
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
static void sig_alrm(int);
static jmp_buf env_alrm;
int main(int arc, char **argv)
{
int x;
x = setjmp(env_alrm);
printf("setjmp was created with return value: %d\n", x);
if(signal(SIGALRM, sig_alrm) == SIG_ERR)
{
printf("Error settting SIGALRM\n");
exit(1);
}
if((x!= 0) && (x!=1))
{
printf("Error setting setjmp\n");
exit(1);
}
printf("Line next to setjmp\n");
x = alarm(2);
printf("Alarm set for 2 seconds, remaning secs from previous alarm: %d\n");
pause();
printf("Line next to pause()\n");
alarm(0);
return 0;
}
static void sig_alrm(int signo)
{
longjmp(env_alrm, 1);
}
Here is the output and the last line shows where the application pauses
setjmp was created with return value: 0
Line next to setjmp
Alarm set for 2 seconds, remaining secs from previous alarm: 0
setjmp was created with return value: 1
Line next to setjmp
Alarm set for 2 seconds, remaining secs from previous alarm: 0
use sigsetjmp() and siglongjmp() instead, to save and restore the signal masks, which are not saved by default in Linux, to clear any pending signals, from man setjmp():
POSIX does not specify whether setjmp() will save the signal mask. In System V it will not.By default, Linux/glibc follows the System V behavior. If you want to portably save and restore signal masks, use sigsetjmp()
and siglongjmp().
Note: I'm not sure what you're trying to accomplish, but your code looks like it's supposed to run in an infinite loop, calling longjmp() restores execution as if it had just returned from setjmp() and it goes on forever.
According to http://linux.die.net/man/2/pause :
pause() only returns when a signal was caught and the signal-catching
function returned.
In your case it never returns, it does the longjmp out.
#include <signal.h>
#include <stdio.h>
void ints(int i )
{
printf("ints \n");
}
int main(void)
{
signal(SIGINT, ints);
sleep(10);
}
input Ctrl+C , the program will terminate immediately with output:
^ints
I was wondering why,in my opinion,the program should terminate after 10 seconds no matter how many times Ctrl+C is input.
sleep() is one of those functions that is never re-started when interrupted.
interestingly, it also does not return a EINT as one would expect.
It instead returns success with the time remaining to sleep.
See:
http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html
for details on other APIs that do not restart when interrupted