So far, you have seen how the kernel can identify which set of device driver functions are the appropriate ones to call for operations on a particular device special file. Now we'll look at some of the more general aspects of device drivers, including their relationship to the user processes that call them.
When a process is actually running on the CPU, Linux treats it as the current process. When the current process executes a system call that results in the execution of a device driver internal function, the context of the current process is still available even within the device driver routines. This makes it a relatively simple task to copy data to buffers in the process from buffers in the device driver, and vice-versa.
Sometimes, calls to device driver functions will require input or output to a piece of hardware, like a disk drive. In these circumstances, the hardware operates orders of magnitude slower than the CPU. If steps weren't taken to do something about it, these I/O bursts would dramatically slow the system down as the CPU waited for the I/O to complete. In reality, the CPU is not expected to wait for the hardware I/O to finish. What happens is that the I/O is scheduled to take place and then the current process suspends its execution, thus allowing other processes to get on with some useful work while the first process waits for its I/O to happen.
Obviously, some mechanism needs to be provided to resume the suspended process once its I/O has taken place. This mechanism is called interrupts. Interrupts are to the kernel what signals are to user processes except that interrupts are generated by hardware events. Just like signals, interrupts can be caught and can be made to cause special service routines to execute to deal with the condition that caused the interrupt.
When some I/O takes place for which a process is waiting, an interrupt can be generated which executes the associated interrupt service routine. It is part of the task of the interrupt service routine to resume any processes that were suspended waiting for the completion of this I/O event to take place.
Care must be taken inside the interrupt service routine, however, as the CPU has moved on to execute other processes so that when the interrupt (a strictly asynchronous event) takes place and the service routine executes, the kernel will not be executing in the context of the suspended process. This means that the interrupt service routine must not try to perform any operation which requires access to the context of the suspended process as this is not immediately available.
Another point to bear in mind when you write a device driver is that the driver will run as an integral part of the operating system k4rnel and that the kernel cannot be preempted. What this means is that, unlike ordinary user processes, there is no mechanism in the kernel to force a kernel routine to relinquish control of the CPU in favor of some other routine. The only way that kernel routines terminate is if they return at the end of the function or if they voluntarily give up the CPU in favor of some other process by suspending the current process on whose behalf they are running.
Whenever you add code to the Linux kernel you must always be aware that Linux will trust your new code completely. This means that if your code goes into an infinite loop then the kernel will effectively just 'hang' as far as all the other processes on the system are concerned. Even if your kernel code is logically correct, if it is inefficient in its use of the CPU, memory or hardware it can still cause major system performance penalties.
These are the main things to keep in mind as we go on to look at character device drivers.