What do you do when somebody asks you for something you can't do right away? If you're a human being and you're bothered by a human being, the only thing you can say is: "Not right now, I'm busy. Go away!". But if you're a kernel module and you're bothered by a process, you have another possibility. You can put the process to sleep until you can service it. After all, processes are being put to sleep by the kernel and woken up all the time (that's the way multiple processes appear to run on the same time on a single CPU).
This kernel module is an example of this. The file (called /proc/sleep) can only be opened by a
single process at a time. If the file is already open, the kernel module calls
wait_event_interruptible
[1]. This function changes the status of the task (a task is the kernel data
structure which holds information about a process and the system call it's in, if any) to
TASK_INTERRUPTIBLE
, which means that the task will not run until it is woken up somehow, and adds
it to WaitQ
, the queue of tasks waiting to access the file. Then, the function calls the
scheduler to context switch to a different process, one which has some use for the CPU.
When a process is done with the file, it closes it, and module_close
is called. That function
wakes up all the processes in the queue (there's no mechanism to only wake up one of them). It then returns and the
process which just closed the file can continue to run. In time, the scheduler decides that the process has had enough
and gives control of the CPU to another process. Eventually, one of the processes which was in the queue will be given
control of the CPU by the scheduler. It starts at the point right after the call to
module_interruptible_sleep_on
[2]. It can then proceed to set a global variable to tell all the other
processes that the file is still open and go on with its life. When the other processes get a piece of the CPU, they'll
see that global variable and go back to sleep.
So we'll use tail -f to keep the file open in the background, while trying to access it with another process (again in the background, so that we need not switch to a different vt). As soon as the first background process is killed with kill %1 , the second is woken up, is able to access the file and finally terminates.
To make our life more interesting, module_close
doesn't have a monopoly on waking up the
processes which wait to access the file. A signal, such as Ctrl+c (SIGINT
) can also wake up a
process. [3] In that case, we want
to return with -EINTR
immediately. This is
important so users can, for example, kill the process before it receives the file.
There is one more point to remember. Some times processes don't want to sleep, they want either to get what they
want immediately, or to be told it cannot be done. Such processes use the O_NONBLOCK
flag when
opening the file. The kernel is supposed to respond by returning with the error code -EAGAIN
from
operations which would otherwise block, such as opening the file in this example. The program
cat_noblock, available in the source directory for this chapter, can be used to open a file with
O_NONBLOCK
.
hostname:~/lkmpg-examples/09-BlockingProcesses# insmod sleep.ko hostname:~/lkmpg-examples/09-BlockingProcesses# cat_noblock /proc/sleep Last input: hostname:~/lkmpg-examples/09-BlockingProcesses# tail -f /proc/sleep & Last input: Last input: Last input: Last input: Last input: Last input: Last input: tail: /proc/sleep: file truncated [1] 6540 hostname:~/lkmpg-examples/09-BlockingProcesses# cat_noblock /proc/sleep Open would block hostname:~/lkmpg-examples/09-BlockingProcesses# kill %1 [1]+ Terminated tail -f /proc/sleep hostname:~/lkmpg-examples/09-BlockingProcesses# cat_noblock /proc/sleep Last input: hostname:~/lkmpg-examples/09-BlockingProcesses# |
[1] | The easiest way to keep a file open is to open it with tail -f. |
[2] | This means that the process is still in kernel mode --
as far as the process is concerned, it issued the |
[3] | This is because we used |