NeXTSTEPDRIVERKIT:Chapter1 7
- I/O and Interrupt Requests
I/O 그리고 인터럽트 요청
Everything a driver does—whether or not it's a Driver Kit driver—is the result of one of two types of requests:
- I/O requests (from a user-level program, the kernel, or another driver)
- Interrupt requests (from the hardware)
Interrupt requests include "soft interrupts," such as timeout notifications. The Driver Kit thread-based design allows you to manage I/O requests and interrupts one at a time.
Scheduling Hardware Access with I/O Threads
Different drivers have different requirements for ordering their accesses to the hardware. Driver Kit display drivers are very simple in this respect: they don't have to queue requests because the Window Server is the only process that makes requests, and it sends them one at a time. Display drivers may be particularly simple because on many systems, display hardware doesn't generate interrupts.
Other drivers have to be more careful. These drivers use an I/O thread—a single thread of execution that handles all access to a single hardware device. Some of the device classes, such as those for SCSI controllers, network, and sound devices, start up the default I/O thread for you.
Typically, each driver instance has exactly one I/O thread. However, some drivers use a single I/O thread for more than one instance. What matters is that only one thread at a time has access to any particular hardware resource.
Note: Some hardware devices can handle more than one request at once. For example, some SCSI controllers can queue multiple commands.
At any given time, the I/O thread should be doing exactly one of two things:
- Waiting for an I/O request (from a user, the kernel, or another driver) or an interrupt message
- Executing (dealing with the hardware)
Processes can use a variety of mechanisms to communicate I/O requests to the I/O thread. One of these mechanisms—Mach messages—is the same way the kernel informs the I/O thread that an interrupt has occurred. In this scheme, the kernel enqueues Mach messages for the I/O thread. When the I/O thread isn't executing a request, it dequeues the message and invokes an appropriate driver method in response. (You can also write a custom I/O thread to take whatever action you want in response to messages.) "Synchronizing with the I/O Thread" in Chapter 2 provides more details.
The I/O thread model greatly simplifies driver development and lessens the time needed for debugging the driver. Only one thread deals with any hardware resource at a time, so it's not necessary to use locks and disable interrupts to protect access to hardware and data structures. The user thread communicates requests to the I/O thread, and commands can be enqueued for the I/O thread to execute. The driver can handle one request at a time—instead of many requests to access multiple resources at the same time.
Servicing Interrupts
The Driver Kit has a simple scheme for servicing interrupts: The kernel notifies drivers of interrupts by sending them Mach messages. Each driver can receive these messages whenever it chooses, typically when it isn't executing any other requests.
The advantages of this scheme become clear when you consider an alternative—the traditional UNIX method of handling interrupts. Traditional UNIX drivers handle interrupts as soon as they happen—even if the driver is already executing an I/O request. Each driver registers an interrupt handling function that's called whenever the device interrupts. Some systems can't tell exactly which device interrupted, so they call several drivers' interrupt handlers until one accepts the interrupt. While an interrupt is being handled, nothing else in the system (except higher priority interrupt handlers) can execute.
Under the traditional UNIX scheme, drivers can't control when interrupts occur. All they can do is control when interrupts don't occur by disabling interrupts. Drivers disable interrupts to protect critical sections of code, such as those that access hardware or access data structures that are also used by interrupt handlers. However, disabling interrupts has disadvantages:
- If a driver disables interrupts for too long, the consequences can be anything from reduced performance to system crashes or hangs.
- If a driver disables interrupts and, through some bug, fails to reenable them, the system will hang.
- It's easy to fail to protect a critical section—especially when you're changing code that someone else wrote—which can result in bugs that are hard to track down.
The Driver Kit scheme of interrupt handling lets you choose when to handle interrupts, so you don't have to protect critical sections from interrupt handlers. This scheme works well with most hardware devices.
IODirectDevice provides a default I/O thread that intercepts Mach interrupt messages and notifies drivers of them with Objective C messages. Driver objects are notified of interrupts with the interruptOccurred or interruptOccurredAt: message. See the sections "Interfacing with the Driver" and "Handling Interrupts" in Chapter 2 and the IODirectDevice class specification in Chapter 5 for more information.
A few devices require that interrupts be handled immediately. For example, a device might have a register that must be read within 50 microseconds of the interrupt occurring. On some devices data overruns occur if interrupts aren't handled quickly enough. In these cases, a kernel-level driver might need to register a direct interrupt handler—a function that's called as soon as the interrupt is detected. This function should perform any time-critical operations and, if necessary, send a Mach message so that the driver can further process the interrupt. The section "Custom Interrupt Handlers" in Chapter 2 describes how this interrupt handling function should work.