}

 DebugPrintStarted = FALSE;

 ClearEvents();

 KeSetEvent(&ThreadExiting, 0, FALSE);

 PsTerminateSystemThread(STATUS_SUCCESS);

}

System Worker Threads

An alternative technique is available if you want to perform occasional short tasks at PASSIVE_LEVEL IRQL. However, this system worker thread method was not suitable for the DebugPrint software because the thread context may change between calls.

To use the system worker thread method, first allocate a WORK_QUEUE_ITEM structure from nonpaged memory. Call ExInitializeWorkItem passing this pointer, a callback routine, and a context for it. When you want your function to be run, call ExQueueWorkItem. In due course, your callback routine is called at PASSIVE_LEVEL in the context of a system thread. Do not forget to free the WORK_QUEUE_ITEM structure memory when finished with it. System worker threads have a lower priority than system threads running at the lowest real time priority, but higher than most user mode threads.

The WdmIo and PHDIo drivers, described in Chapters 15-18, show how to use a system worker thread.

In W2000, it is recommended that you use the IoAllocateWorkItem, IoQueueWorkItem, IoFreeWorkItem functions instead.

Events

The main DebugPrint test driver code sets the ExitNow Boolean to true when it wants its system thread to terminate. However, it is not a good idea for the system thread to spin continuously waiting for this value to become true.

Instead, a kernel event called ThreadEvent signals when to check ExitNow. A KEVENT must be defined for the event in nonpaged memory. Kernel events are very similar to their user mode Win32 cousins.

Listing 14.1 shows how ThreadEvent is initialized using KeInitializeEvent, at PASSIVE_LEVEL IRQL. Two types of events are supported, SynchronizationEvent and NotificationEvent. The last parameter sets the initial state of the event, which is nonsignalled in this case.

When set, a Synchronization event only releases one waiting thread before reverting to the nonsignalled state. A Notification event stays signalled until explicitly reset.

DebugPrintClose uses KeSetEvent to set an event into the signalled state, after setting ExitNow to true. The third parameter to KeSetEvent specifies whether you are going to call one of the KeWait… routines straightaway. If not, you can call KeSetEvent at any IRQL up to and including DISPATCH_LEVEL. If waiting, you must be running at PASSIVE_LEVEL.

If you need to put an event into the nonsignalled state, call KeClearEvent or call KeResetEvent to determine the previous event state. You can use KeReadStateEvent to read the event state. All these routines can be called at DISPATCH_LEVEL or lower.

For NT and W2000 drivers you can use IoCreateNotificationEvent and IoCreateSynchronizationEvent to share an event between two or more drivers.

Synchronization

A thread running at PASSIVE_LEVEL can synchronize with other activities by waiting for dispatcher objects such as events, Mutex objects, and semaphores. You can wait for timer and thread objects. Finally, you can also wait on file objects if they have been opened in ZwCreateFile for overlapped I/O.

Although driver dispatch routines run at PASSIVE_LEVEL, they should not wait on kernel dispatcher objects, other than with a zero time-out. You can wait for inherently synchronous operations to complete using nonzero time-outs. Plug and Play handlers can wait on dispatcher objects. For example, the ForwardIrpAndWait routine described in Chapter 9 uses an event to signal when lower drivers have finished processing an IRP.

A thread waits for dispatcher objects to become signalled using KeWaitForSingleObject or KeWaitForMultipleObjects, which are similar to the Win32 equivalents. As Table 14.1 shows, KeWaitForSingleObject waits on just one dispatcher object, or until a time-out has expired. A negative timeout value is used for relative periods, as a LARGE_INTEGER in units of 100 nanoseconds. The DebugPrint system thread calls KeWaitForSingleObject with a relative time-out of one second. Positive time-out values represent an absolute system time, in 100-nanosecond units since January 1, 1601[32] in the GMT time zone.

The KeWaitForMultipleObjects routine works in a similar way, except that you can pass an array of dispatcher objects. You can opt to wait for just one of the objects to become signalled, or all of them.

Table 14.1 KeWaitForSingleObject function

NTSTATUS KeWaitForSingleObject (IRQL==PASSIVE_LEVEL) or at DISPATCH_LEVEL if a zero time-out is given
Parameter Description
IN PVOID Object Pointer to dispatcher object
IN KWAIT_REASON WaitReason Usually Executive for drivers, but can be UserRequest if running for user in a user thread.
IN KPROCESSOR_MODE WaitMode Kernel Mode for drivers
IN BOOLEAN Alertable FALSE for drivers
IN PLARGE_INTEGER Timeout NULL for an infinite time-out. Negative time-outs are relative. Positive time-outs are absolute.
Returns STATUS_SUCCESS STATUS_TIMEOUT

Mutex Objects

A Mutex is a mutual exclusion dispatcher object that can only be owned by one thread at a time. Mutexes are sometimes called 'mutants.' Initialize a KMUTEX object in nonpaged memory using KeInitializeMutex; the Level parameter is used to ensure that multiprocessor Windows 2000 systems can acquire multiple Mutexes safely.

A Mutex object is in the signalled state when it is available. A thread requests ownership using one of the KeWaitFor… routines. If two or more threads are waiting for a Mutex, only one thread will wake up and become its owner. Call KeReleaseMutex to release ownership.

If you already own a Mutex and ask for it again, the KeWaitFor… routine will return immediately. An internal counter is incremented, so call KeReleaseMutex once for each time you requested ownership of the Mutex.

The kernel causes a bugcheck if you do not release a Mutex before returning control to the I/O Manager. KeInitializeMutex and KeReleaseMutex must be called at PASSIVE_LEVEL You can also inspect the Mutex state using KeReadStateMutex at an IRQL up to and including DISPATCH_LEVEL

A Fast Mutex is a variation on an ordinary Mutex that is faster because it does not permit multiple ownership requests. An Executive Resource is another similar synchronization object, available in W2000 only. See the DDK documentation for more details of these objects.

Semaphores

A semaphore is a dispatcher object that maintains a count. Call KeInitializeSemaphore at PASSIVE_LEVEL IRQL to initialize a KSEMAPHORE object in nonpaged memory. You must specify maximum and initial counts.

A semaphore is nonsignalled when zero and signalled with any count greater than zero. A thread that calls one of the KeWaitFor… routines and finds a signalled semaphore will decrement its count and the thread will proceed. If a semaphore's count is 2 and three threads simultaneously attempt to wait for the semaphore, only two will proceed. The semaphore count ends up as 0 with one thread still waiting.

Call KeReleaseSemaphore, at DISPATCH_LEVEL or lower, to add a value to a semaphore count. You can read the semaphore count at any IRQL using KeReadStateSemaphore.

Timer, Thread, and File Objects

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×