mirror of
https://github.com/sysprog21/lkmpg.git
synced 2025-03-13 14:05:10 +08:00
Tidy section: Blocking Processes and threads
This commit is contained in:
parent
3070689d19
commit
678757e19c
53
lkmpg.tex
53
lkmpg.tex
@ -1226,32 +1226,53 @@ Depending on your kernel version, you might even need to hand apply the patch.
|
||||
\samplec{examples/syscall.c}
|
||||
|
||||
\section{Blocking Processes and threads}
|
||||
\label{sec:org864c195}
|
||||
\label{sec:blocking_process_thread}
|
||||
\subsection{Sleep}
|
||||
\label{sec:org4869971}
|
||||
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: "\emph{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).
|
||||
\label{sec:sleep}
|
||||
What do you do when somebody asks you for something you can not do right away?
|
||||
If you are a human being and you are bothered by a human being, the only thing you can say is: "\emph{Not right now, I'm busy. Go away!}".
|
||||
But if you are a kernel module and you are 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 is 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 \textbf{/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. The easiest way to keep a file open is to open it with:
|
||||
This kernel module is an example of this.
|
||||
The file (called \textbf{/proc/sleep}) can only be opened by a single process at a time.
|
||||
If the file is already open, the kernel module calls \verb|wait_event_interruptible|.
|
||||
The easiest way to keep a file open is to open it with:
|
||||
|
||||
\begin{codebash}
|
||||
tail -f
|
||||
\end{codebash}
|
||||
|
||||
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 \textbf{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.
|
||||
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 is in,
|
||||
if any) to \textbf{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 that 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 \textbf{module\_interruptible\_sleep\_on}.
|
||||
When a process is done with the file, it closes it, and \verb|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 that 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 \verb|module_interruptible_sleep_on|.
|
||||
|
||||
This means that the process is still in kernel mode - as far as the process is concerned, it issued the open system call and the system call hasn't returned yet. The process doesn't know somebody else used the CPU for most of the time between the moment it issued the call and the moment it returned.
|
||||
This means that the process is still in kernel mode - as far as the process is concerned, it issued the open system call and the system call has not returned yet.
|
||||
The process does not know somebody else used the CPU for most of the time between the moment it issued the call and the moment it returned.
|
||||
|
||||
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.
|
||||
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.
|
||||
So we will use \verb|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, \textbf{module\_close} doesn't have a monopoly on waking up the processes which wait to access the file. A signal, such as \emph{Ctrl +c} (\textbf{SIGINT}) can also wake up a process. This is because we used \textbf{module\_interruptible\_sleep\_on}. We could have used \textbf{module\_sleep\_on} instead, but that would have resulted in extremely angry users whose \emph{Ctrl+c}'s are ignored.
|
||||
To make our life more interesting, \textbf{module\_close} does not have a monopoly on waking up the processes which wait to access the file.
|
||||
A signal, such as \emph{Ctrl +c} (\textbf{SIGINT}) can also wake up a process. This is because we used \textbf{module\_interruptible\_sleep\_on}.
|
||||
We could have used \textbf{module\_sleep\_on} instead, but that would have resulted in extremely angry users whose \emph{Ctrl+c}'s are ignored.
|
||||
|
||||
In that case, we want to return with \textbf{-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 \textbf{O\_NONBLOCK} flag when opening the file. The kernel is supposed to respond by returning with the error code \textbf{-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 \textbf{O\_NONBLOCK}.
|
||||
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 \verb|O_NONBLOCK| flag when opening the file.
|
||||
The kernel is supposed to respond by returning with the error code \textbf{-EAGAIN} from operations which would otherwise block, such as opening the file in this example. The program \verb|cat_nonblock|, available in the \textit{examples/other} directory for this chapter, can be used to open a file with \verb|O_NONBLOCK|.
|
||||
|
||||
\begin{verbatim}
|
||||
$ sudo insmod sleep.ko
|
||||
@ -1281,18 +1302,20 @@ $
|
||||
\samplec{examples/other/cat_nonblock.c}
|
||||
|
||||
\subsection{Completions}
|
||||
\label{sec:orgd9f9de4}
|
||||
Sometimes one thing should happen before another within a module having multiple threads. Rather than using \textbf{/proc/sleep} commands the kernel has another way to do this which allows timeouts or interrupts to also happen.
|
||||
\label{sec:completion}
|
||||
Sometimes one thing should happen before another within a module having multiple threads.
|
||||
Rather than using \verb|/bin/sleep| commands, the kernel has another way to do this which allows timeouts or interrupts to also happen.
|
||||
|
||||
In the following example two threads are started, but one needs to start before another.
|
||||
|
||||
\samplec{examples/completions.c}
|
||||
|
||||
The \emph{machine} structure stores the completion states for the two threads. At the exit point of each thread the respective completion state is updated, and \emph{wait\_for\_completion} is used by the flywheel thread to ensure that it doesn't begin prematurely.
|
||||
The \emph{machine} structure stores the completion states for the two threads.
|
||||
At the exit point of each thread the respective completion state is updated, and \verb|{wait_for_completion| is used by the flywheel thread to ensure that it does not begin prematurely.
|
||||
|
||||
So even though \emph{flywheel\_thread} is started first you should notice if you load this module and run \emph{dmesg} that turning the crank always happens first because the flywheel thread waits for it to complete.
|
||||
|
||||
There are other variations upon the \emph{wait\_for\_completion} function, which include timeouts or being interrupted, but this basic mechanism is enough for many common situations without adding a lot of complexity.
|
||||
There are other variations upon the \verb|wait_for_completion| function, which include timeouts or being interrupted, but this basic mechanism is enough for many common situations without adding a lot of complexity.
|
||||
|
||||
\section{Avoiding Collisions and Deadlocks}
|
||||
\label{sec:org17fc2e9}
|
||||
|
Loading…
x
Reference in New Issue
Block a user