Linux has the wait_queue_head_t data structure for implementing blocking I/O operations. A process that wants to wait for a condition can call either of the following wait_event_xxx() functions:
- wait_event()
- wait_event_interruptible()
- wait_event_timeout()
- wait_event_interruptible_timeout()
- wait_event_interruptible_exclusive()
The first four API’s will result in a non-exclusive wait. The last API will result in a exclusive wait.
There are some interesting facts to be noted about the corresponding wake_up_xxx() functions.
- The wake_up(queue) and wake_up_interruptible(queue) functions will wake up all the non exclusive processes + one exclusive process waiting on the queue and whose wait condition is satisfied.
- The wake_up_nr(queue, nr) and wake_up_interruptible(queue, nr) functions will wake up all the non exclusive processes + upto ‘nr’ exclusive processes waiting on the queue and whose wait conditions are satisfied.
- The wake_up_all(queue) and wake_up_interruptible_all(queue) functions will wake up all the non exclusive processes + all exclusive processes waiting on the queue and whose wait conditions are satisfied.
Considering that we are not interested in ‘exclusive’ wait, let us analyze following two programs:
Program 1: Using wake_up() calls without delays
example_wake_up.c is a program that implements the following functionality:
- read() function that is blocked for data using the wait_event_interruptible() call.
- write() function that copies some data and then calls the wake_up_interruptible() call.
In order to test this program, perform the following operations:
- Insert the example_wake_up.ko module into the kernel using the insmod utility.
- Create a /dev/simple_char_dev entry using the mknod utility.
- Open a terminal and execute ‘cat /dev/simple_char_dev‘.
- Open another terminal and execute ‘cat /dev/simple_char_dev‘.
- Now in another terminal, execute the ‘echo “hello world for wake up testing” > /dev/simple_char_dev‘.
- You will notice that only one of the ‘cat’ programs will get the data. The other ‘cat’ program waits for data.
Program 2: Using wake_up() calls with delay
example_wake_up_delay.c is a program that implements the following functionality:
- read() function that is blocked for data using the wait_event_interruptible() call.
- write() function that copies some data and then calls the wake_up_interruptible() call.
- On wake up, the read() functions sleeps for one second before resetting the ‘wait condition’.
In order to test this program, perform the following operations:
- Insert the example_wake_up.ko module into the kernel using the insmod utility.
- Create a /dev/simple_char_dev entry using the mknod utility.
- Open a terminal and execute ‘cat /dev/simple_char_dev‘.
- Open another terminal and execute ‘cat /dev/simple_char_dev‘.
- Now in another terminal, execute the ‘echo “hello world for wake up testing” > /dev/simple_char_dev‘.
- You will notice that both the ‘cat’ programs will get the data.
Reason:
The above behavior is due the following facts about the wake_up() functions:
- The ‘cat’ program is waiting in the read() function.
- The ‘echo’ program provides some data and calls the wake_up() function.
- As a result of the wake_up() function, a sleeping process can move to executing state and can get executed before the wake_up() function returns.
- Without the delay, the first ‘cat’ program to wake up consumes the data and resets the condition.
- When the wake_up() function continues to execute, it finds that the condition is already false. Hence it does not wake up any more processes.
- With the delay before resetting the condition, the wake_up() function call will wake up all the processes.