dunniu
驱动老牛
驱动老牛
  • 注册日期2005-05-26
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分555分
  • 威望0点
  • 贡献值0点
  • 好评度997点
  • 原创分0分
  • 专家分0分
阅读:1406回复:0

driverwork object model 2

楼主#
更多 发布于:2005-07-01 09:47
  6.       Containers and Other Objects
There are additional useful classes in the object model which aid in driver development. Some of these are more like container classes, such as the list and FIFO classes. Others are more general, such as file objects and strings. Most are based on system services, while others were developed for Driver::Works to simplify the development of typical drivers.
Files
File objects are implemented by class KFile.
The file object encapsulates kernel mode file services. The methods for file objects are typical of most abstractions of files: create, open, close, read, write, seek, query properties, and set properties.
Files are accessible only when IRQL is PASSIVE_LEVEL, and this limits the utility of file objects in kernel mode drivers. Much of the execution of a driver occurs at higher IRQLs, and the driver cannot access files while in that state. For this reason, drivers sometimes create system threads to handle file operations.
Strings
String objects are implemented by class KUstring.
String objects are ordered groups of Unicode characters. The attributes of a string include its length and the maximum length to which it grow. Objects which are kept in low level system object directories (such as registry keys, files, device names, symbolic links, and dispatcher object) use string objects for naming. Strings are also used for creating event log entries.
The methods of a string object enable assignment, concatenation, comparison, access to first and last characters, and various type conversions.
A specialized string is provided for unitized names, i.e., strings that consist of a base string to which a number is appended. These are useful for device names and symbolic link names, and occasionally for accessing the registry.
FIFOs
FIFO objects are implemented by classes KFifo, KLockableFifo, and KInterruptSafeFifo.
FIFO is an acronym for "first in, first out", which describes the behavior of the object with respect to data written to and read from it. Conceptually, it is a pipe: data enters at one end and emerges at the other. FIFOs are very useful for buffering data between devices or between an application and a device. The methods exported by a FIFO include those for reading, writing, querying available data, and flushing.
Like list objects, FIFO objects are implemented using templates, enabling type-safe access. Declaration of a FIFO requires specification of the data type to be buffered, and the methods for reading and writing operate only on data of that type.
Again like list objects, there are three types of FIFO templates, each having different synchronization methods: no synchronization, spin lock protected, and synchronized via an interrupt object.
Lists
List objects are implemented by classes KList, KInterlockedList, and KInterruptSafeList.
The list objects present a simple implementation of doubly linked lists. Use of templates enables type-safe manipulation of objects in list. The methods of this object include those for insertion, deletion, traversing the list, and testing for emptiness.
There are three types of list templates, each having different synchronization methods. For the simplest list object, no synchronization is provided, and it is the responsibility of the developer to ensure that corruption does not occur. The second form uses a spin lock to protect all access to internal pointers. The third form requires a connected interrupt object, and uses a specialized set of protected methods that run under the control of the interrupt object's synchronization method. This last type of list object can be manipulated by ISRs.
Driver Parameters and Registry Objects
Interface to the registry is implemented by the class KRegistryKey.
The installation procedure for a driver and the devices that it supports may store information about the devices in the system registry. The registry is a system wide database. It is organized in a tree-like manner, much like a directory of files. Each node of the registry tree is called a key. The leaves of the tree are called values. Each value has a name and associated data. The data may be of several types, such as a simple integer, a string, or arbitrary binary data.
The system provides a registry key for each driver, and the path to this key is passed to DriverEntry. A driver sometimes reads parameters from subkeys of its registry key. Driver::Works provides a registry key object that enables the driver to conveniently access parameters in the registry.
A registry key is constructed by specifying its path. A registry key object exports methods to read and write values, and to enumerate its values and its subkeys.

7.       Configuration Query Objects
Query objects are implemented by class KConfigurationQuery.
Before a driver can construct device objects, the initialization method must determine how many devices that fall under the driver's control are present, and what the specific characteristics of those devices are. The device query can be carried out in an ad hoc manner by the initialization code, or can employ a query object. Query objects search for devices with particular characteristics, and call a supplied entry point in the driver for each one that is found.
There are two kinds of query objects. The first type searches for devices that the system can detect automatically, such as serial ports or disk controllers. The second type searches in the system registry under the key for the driver. The installation procedure sets up the registry keys in a predetermined hierarchy that includes a separate key for each device present. This enables the driver to dynamically determine what devices are present on a given system.
Both types of queries require the driver to supply a callback to be invoked for each qualifying device. For system detected devices, the parameters to the callback include the set of system resources that the device requires. For devices configured in the registry, the parameters to the callback specify which unit of a particular device class was found. Typically, the callback will construct new device object for each unit discovered. The callback or the device class constructor uses the registry key to access the registry and determine the resources that the unit requires.

8.       Controller Objects
Controller objects are implemented by class KController.
Another kind of synchronization problem arises when a set of device objects belonging to the same driver all share a common resource. For example, a pair of disk drives on the same disk controller are abstracted as separate device objects, but they cannot operate independently from one another because there are shared data channels on the controller.
Callbacks to devices queued in controller.
The system provides controller objects to simplify queuing of requests in cases such as this. When a device object dequeues an IRP that requires the shared resource, it calls a method of the controller object to request access. If the resource represented by the controller is available, the controller object immediately invokes a function specified by the device (possibly a method of the device). If the resource is not available, then the controller object queues the address of the callback function for invocation when the resource is freed. Note that the queue object embedded in a controller is not a queue of IRPs; it is a queue of function addresses passed to the controller by devices requesting access to the shared resource. When the controller calls one of these functions, it means access to the shared resource has been granted to the called device object. The device processes its current IRP, and then calls a controller method to release ownership of the shared resource.

9.       Driver Managed Queues
Driver managed queue objects are implemented by class KDriverManagedQueue.
The queue object embedded in the system device object is sufficient to serialize a single set of IRPs all of which require common resources of the device. However, in some cases a device object is capable of processing dissimilar requests independently. The classic example is a full duplex serial driver, that can transmit and receive independently. It would be inefficient to serialize both transmit and receive requests in the same queue, but the system device object provides only a single queue.
To deal with situations such as this, the system allows device objects to create and manage additional queue objects. These objects have methods with the same functionality as described above for the queue that is embedded in the device object. The driver writer subclasses the queue object, overriding the virtual method that the queue object invokes to start processing a request. When a device object receives an IRP, it calls the queuing method of the created queue object, rather than the queuing method of the system device object.

10.       Event Log Entries
Event Log Entry objects are implemented by class KErrorLogEntry.
The operating system provides a facility for logging events of interest to the system administrator. Drivers use this facility to document error conditions encountered during execution. This is preferable to displaying confusing error messages or bringing down the system. The system administrator can view the event log with a system utility.
This event log entry object encapsulates the services of the system error logger. Error log entries consist of a fixed length structure containing the event parameters, optional Unicode strings, and optional binary data. Drivers create an error log entry object and then post it to the system. The class has methods to set the binary data and add strings to the entry. It allows direct access to the event parameter structure, enabling the driver to set the various fields efficiently.

11.       Heaps
Heap objects are implemented by the KHeap, KHeapClient, KPagedHeap, and KPagedHeapClient heap template classes.
The heap template classes provide alternate heaps for drivers that make frequent allocations and deallocations of small blocks of a fixed size. Using an alternate heap may improve performance, and, more importantly, may help prevent fragmentation of the system memory pools.
The heap classes are built on an operating system feature known as lookaside lists. A lookaside list is a set of fixed size blocks, chained together in a list. Initially the list is empty. When a driver tries to allocate a block from the list, the list object tries to find a free block in the list. If not found, one is allocated from the system pool and put into the list. When the client frees the block, it stays in the list as a free block and is available for allocation.

12.       Image Section Object
Image Section Objects are implemented by class KImageSection.
Image section objects correspond to sections of the driver's executable image, each of which has characteristics that are defined when the driver is linked. Sections that have names that begin with "INIT" are discarded by the system after initialization. Section that have names that begin with "PAGE" are capable of being paged out to disk when not in use. Other sections remain resident in memory, and are said to be locked, or nonpaged.
Because physical memory is a critical shared resource, a driver should minimize the total size of its nonpaged sections. By making a section pageable, the driver enables the memory manager to allocate the physical memory where it is most needed.
At run time, a driver can control which sections can be paged out and which cannot. After making all sections pageable, a driver creates an image section object for each section of code which is to be locked. An image section object created in this fashion exports methods enabling it to be dynamically locked and unlocked.

13.       Interrupt Request Levels (IRQL)
The design of the operating system recognizes that conditions may arise which require the service of a processor within a short interval. The obvious example is an interrupt from a device that needs to transfer data. If the system does not call the interrupt service routine promptly, data may be lost.
To handle the competing demands of the various processes and devices that may be running on a computer, the system implements the concept of interrupt request level, commonly referred to as IRQL. All processing occurs at one of 32 IRQLs, numbered from zero to 31, with 31 being the highest priority. IRQL is not to be confused with the scheduler痴 process priority levels, which are gradations within the lowest IRQL, that is, IRQL 0 or Passive Level.
System Interrupt Request Levels
Highest = 31       Bus Error
30       Power Failure
29       Interprocessor Communication
28       System Timers
I/O Device Interrupts (DIRQL)
2       Dispatch Level
1       APC Level
Lowest = 0       Passive Level

The highest priorities are reserved for catastrophic events such as bus errors and power failures. Below that is the IRQL for interprocessor communication, followed by the IRQL for system timers. Next are the IRQLs for I/O devices, known also as device IRQL or simply DIRQL. The three lowest IRQLs in descending order are named DISPATCH_LEVEL, APC_LEVEL, and PASSIVE_LEVEL. These are associated with software events, and will be discussed further below.
At any given time, each processor is running at a particular IRQL, depending on the kind of processing it is doing. IRQLs of the processors in a multiprocessor system are independent from one another. The kernel will interrupt a processor's execution only if an event that requires IRQL higher than that at which the processor is currently running occurs. This means that all code in a kernel mode driver is interruptible, but only by events of higher priority.
It's important for kernel mode drivers to minimize the time spent at raised IRQL, especially at device IRQL. Running at DIRQL for longer than necessary may interfere with the operation of other devices by delaying the execution of their interrupt service routines. The system provides mechanisms that aid in writing drivers that are well behaved in this regard.
Many methods of system objects are not callable above DISPATCH_LEVEL, and some are only callable at PASSIVE_LEVEL. Others specify a minimum IRQL, rather than a maximum. Furthermore, code that runs at DISPATCH_LEVEL or above may not access paged code or data.

14.       I/O Request Objects
I/O Request Objects are implemented by class KIrp.
I/O request objects, known more commonly as IRPs (I/O Request Packets), drive the operation of most kernel mode drivers. When applications send data to or request data from a device, the I/O manager creates an IRP and delivers it to the driver responsible for the device. The driver dispatches the request to one of its device objects. The device may begin processing the IRP, or it may place it on a queue for processing later. The action taken depends on the nature of the request and the state of the device. Later, when the request is satisfied, a device sends the IRP back to the I/O Manager.
Each IRP describes an I/O operation to be performed by a particular device. Accessor methods retrieve attributes of the request, such as the major and minor function codes, the status, and the parameters to various operations, such as offsets and counts for read and write requests.
In general, an IRP may require action on its behalf by multiple device objects in order to satisfy it. For each device object that may share in its processing, the IRP reserves a data area where parameters and other information may be stored. Each of these data areas is known as an "IRP stack location." When a device object processes a request, it may only access the IRP stack location reserved for it and one for that of a device to which it passes the IRP. A device object that receives an IRP may set up the next IRP stack location and pass it to another device object for further processing.
While most IRPs are created by the I/O Manager in response to application calls, drivers commonly create them, as well. The most common IRPs are requests for either Create, Close, Read, Write, or DeviceControl processing. A device object may need to break up a large request into smaller pieces that other device objects closer to the hardware can handle. To do so, the device object creates multiple IRPs and sends them to the lower devices. When they have all completed, the device object deletes the IRPs that it created and completes the original request.
IRPs may also be canceled when an I/O operation needs to be aborted, possibly because the originating application has been terminated. The IRP object contains a pointer to a cancel routine that will be called when the IRP is canceled.

15.       Lower Device Objects
Lower device objects are implemented by class KLowerDevice.
Some device objects process IRPs without ever accessing hardware. Instead, they rely on the services of device objects under the control of other drivers to provide the interface to the hardware. From the system's point of view, all devices objects are treated symmetrically. However, from the perspective of a given driver, device objects that it creates are logically distinct from devices under the control of other drivers. In other words, the set of operations that a driver performs with its own device objects is quite different from the set of operations performed with device objects belonging to other drivers. The DriverWorks object model therefore distinguishes owned device objects as a class distinct from alien device objects, and the latter are referred to as "lower devices".
Some device objects exploit lower devices to carry out the processing of an I/O request. This characteristic of the system enables layering of drivers. It is common to speak of a stack of drivers, meaning a set of hierarchically related device objects, each with a well defined interface. Each device object has its own role in the processing of an IRP. Upper level drivers implement an interface to applications, and lower level drivers implement an interface to the hardware. For example, the keyboard class driver presents a generic keyboard interface to user subsystems. It relies on a keyboard port driver to provide the interface to the physical keyboard. The class driver is interoperable with a number of different port drivers, each of which supports a different kind of physical keyboard.
There are several benefits to this approach. A given device object is responsible for a limited set of functions. This enables applications to be written in a device independent manner, and facilitates development of drivers for new hardware. The benefits of modularity are as important in driver development as in most other areas of software development.

16.       Queue Objects and Request Serialization
Queue objects are implemented by the class KDeviceQueue.
The nature of a device determines whether or not it can process multiple requests simultaneously. For many kinds of hardware, the physical resources can only do one thing at a time. For example, it would not make sense for a simple parallel port to attempt to simultaneously transmit data for more than one I/O request, because the device is implemented as a single physical channel. As a result, many devices require a mechanism to assist in the serialization of requests, i.e., a means of ensuring that only one request can be processed at a time.
The simple solution to this problem is a queue. The system device object embeds a queue object for IRPs pending service. A subclass of the device object can call methods that manipulate the embedded queue. The operations are more sophisticated than simply placing an IRP on the queue or removing one from the queue. Instead, there is a method whose semantics are: "If the device is busy processing a request, then place this request on the queue. Otherwise, pass the request to a function that can start processing it immediately." An additional method behaves as follows: "This request is complete, so send it back to the I/O Manager. If there are requests in the queue, remove the head and pass it to a function that can start processing it." These methods greatly simplify the task of serialization for the device subclass.

17.       Resource Request and Assignment Objects
The responsibility for claiming resources may be assigned to the driver object or to its subordinate device objects. In either case, the process involves a resource request object and sometimes one or more resource assignment objects.
Resource request objects are implemented by class KResourceRequest.
A resource request is an object that describes the set of system resources that a driver or device requires in order to operate successfully. It may be very simple and contain a single required item, such as a particular interrupt line (IRQ). Or it may be a complex tree of alternative resource sets, each set having multiple resources of various kinds, and each resource allowing a range of values.
Assignment objects are implemented by class KResourceAssignment.
In cases where the request does not include ranges or alternates, the request either succeeds or fails. If the system cannot grant the required resource, the driver aborts the initialization process. For the more complex requests that present choices to the system, a driver needs a resource assignment object to extract the resources that are granted by the system. Each resource assignment object corresponds to a particular item of the request. If a driver creates a request object with three distinct resources, then it requires three assignment objects to determine which resources the system assigned. The resource request and assignment objects hide the complex data structures required for the raw interface to the underlying system calls.

18.       Spin Locks
Spin lock objects are implemented by the class KSpinLock.
Kernel mode drivers use spin locks to maintain integrity of data in multiprocessor systems. A driver associates a spin lock with data that it must access atomically. A spin lock exports two methods: lock and unlock. By requiring all code that accesses the data to acquire (lock) the associated spin lock before doing so, the driver ensures that different processors do not interleave accesses to the data in a way that could corrupt it.
The spin lock mechanism is two-fold: The lock method first raises IRQL to DISPATCH_LEVEL. Then, on a multiprocessor system, it performs a read-modify-write cycle on a variable that indicates the state of the lock. If it was already locked, the code loops, or spins, until the lock is unlocked by a thread running on another processor. The lock method returns when it succeeds in changing the state to locked. The caller remains at DISPATCH_LEVEL until the unlock method is called.
On a single processor system, acquiring a spin lock is equivalent to raising IRQL to DISPATCH_LEVEL. Therefore, spin locks are important for arranging atomic operations even on single processor systems, since only code running at device IRQL or higher can interrupt code that holds a spin lock.
Spin locks cannot be used to synchronize access to data that interrupt service routines access. This requires using the synchronization method of the interrupt object, as described above. Furthermore, interrupt service routines cannot lock arbitrary spin locks, because doing so would lower IRQL to DISPATCH_LEVEL.
游客

返回顶部