Analysis of Win32 Core DPC Design Thoughts and Implementation

zhaozj2021-02-17  48

http://flier_lu.blogone.net/?id=1397656

The X86 architecture is based on interrupt thinking, and thus, from DOS to Win32, a large number of interrupt concepts in the operating system express the behavior of asynchronous operations. However, unlike DOS, Win32 needs to be scheduled by the system under the system, so the interrupt response code must be as simple as possible, and the control is also given to the system as soon as possible. Although the response speed and implementation process of system scheduling is convenient, there are still many features that need to be completed in the interrupt response. To this end, Win32 core provides two IRQL special software interrupt levels for DPC (Asynchronous Procedure Call), for delaying and asynchronous process calls. From IRQL layers, DPC and APCs are interrupt between the high-level device interrupt and the lowest level Passive interrupt, by the operating system for completing the interrupt level called by a special method. The processor interrupts are different from the device interrupt and higher level of the device that handles the hardware operation, which is designed to implement function call asynchronousity, so the operating system itself has strong dependencies. APC is not discussed here, and later there will be a chance to write articles special discussion :) DPC can be understood as part of the ISR (Interrupt Service Routine). Just because the ISR is to give a part of the function peeling out into the corresponding DPC in order to simply and return control as much as possible to the operating system as much as possible, put it into the corresponding DPC. Because the IRQL of the DPC is only over the APC and Passive interruption, the system can handle the higher level interrupt, and then slowly handle the accumulation in the DPC level. DPC can be understood as a package object of a callback function. The system itself or device driver is initialized when the IRP_MN_START_DEVICE request is processed in a suitable place, such as the device driver, the IRP_MN_START_DEVICE request; if it is determined in the ISR whether it is needed to process the interrupt, it is requested to insert the DPC object into the system. During the DPC queue; after the system is processed, the DPC object in the DPC queue is slowly processed at the IRQL Dispatch_level level; the callback function packaged for each DPC object will use the calling parameters of the simultaneous package, and is called to complete in ISR. It is not enough to complete; if you need further work, you can continue to request inserting the DPC object into the DPC queue. DPC objects have two end users: DPCForism and CustomDPC. The former is bound to device drive objects; the latter is driven by drive. But from implementation, only one DPC object exists, the maintenance function involved in DPCForism is actually a package for CustomDPC. Let's take a look at the implementation of the initialization DPC object. The KeinitializedPC function (NTOS / Ke / DPCOBJ.C: 39) completes the initialization of the specific DPC object, actually filling a memory structure KDPC (NTOS / INC / NTOSDEF.H: 331).

The following is quoted:

//

// Deferred Procedure Call (DPC) Object

//

Typedef struct _kdpc {

Cshort Type;

Uchar Number;

Uchar Importance;

List_entry dpclistentry;

Pkdeferred_routine deferredroutine;

PVOID DeferredContext;

PVOID systemGument1;

PVOID systemGument2;

Pulong_ptr lock;} kdpc, * pkdpc, * restricted_pointer prkdpc;

TYPE indicates the type of this kernel object, defined in the KObjects enumeration type (NTOS / Inc / Ke.h: 122), default is DPCObject = 0x13. In addition, WinXP / 2003 has added a ThreadedDPCOBJECT = 0x18 Number to specify this DPC object in a multiprocessor environment to specify which processor's DPC queue, we will discuss a detailed description of multiprocessors. The default is 0 importance indicates the importance of this DPC object, defined in the KDPC_IMPortance enumeration type (NTOS / Inc / NtosDef.h: 321), default to MediumImportance = 1 DPCListentry is a linked list pointer for maintaining the DPC queue DeferRedroutine is This DPC object is bound to the callback function, which is the parameters when this callback function is called when the DEFERREDCONTEXT, SYSTEMARGUMENT1, and SYSTEMARGUMENT2 are called. When calling iRequestDPC as ISR, the following two parameters are used to deliver the IRP and Context parameters to the callback function of the DPC. Lock Saves the spin lock of the DPC queue where this DPC object is located, is used to lock the DPC queue, and it is also used to determine if the DPC object is added to a DPC queue. Understand the structure of the KDPC object, actually maintaining the code is very simple. The KeinitializedPC function initials the KDPC object structure as the initial value; the IONITIALIZEDPCREQUEST function is just a simple packaging of the KeinitializedPC function, as follows

The following is quoted:

#define oinitializedpcRequest (DeviceObject, DPCROUTINE) (/

KeinitializedPC (& (DeviceObject) -> DPC, /

(Pkdeferred_routine) (DPCROUTINE), /

(DeviceObject)))))))))))))

Note that the KeinitializedPC function and the KeinitializeThreadedDPC function in WinXP / 2003 are made by a kiinitializedpc function, just the object type defined by the last parameter defined. KeinsertQueuedPC function (NTOS / KE / DPCOBJ.C: 89) is actually a core function of the system's maintenance of the DPC queue, and its pseudo code is as follows:

The following is quoted:

Boolean KeinsertQueuedPC (in PVOID SYSTEMARGUMENT1, IN PVOID SYSTEMARGUMENT2)

{

PKSPIN_LOCK LOCK;

KIRQL Oldirql;

KeraiseiRQL (High_level, & Oldirql); // Improve current IRQL to the highest, shield other interrupts

PKPRCB = KegetCurrentPRCB (); // Get the current processor control block

// By comparing whether DPC-> Lock is empty, it is judged whether this DPC object has been added to the DPC queue;

// If the DPC object can be added to the queue, copy the DPC spin lock of the current processor control block to DPC-> LOCK

IF ((Lock = InterlockedCompareExchangePointer (& DPC-> Lock, & PrcB-> DPCLOCK, NULL) == NULL)

{

/ / Update the statistics of the current processor control block PRCB-> dpccount = 1;

PRCB-> DPCQueuedepth = 1;

/ / Update parameter information for DPC objects

DPC-> SystemGument1 = systemGument1;

DPC-> SystemGument2 = systemGument2;

/ / According to the DPC object priority, decide to add it to the head or tail of the DPC queue.

IF (DPC-> Importance == HIGHIMPORTANCE)

InsertHeadList (& prcb-> dpclisthead, & dpc-> dpclistentry);

Else

INSERTTAILLIST (& prcb-> dpclisthead, & dpc-> dpclistentry);

// If the current processor does not have a DPC object activity or DPC interrupt request, further determine whether a DPC interrupt request is issued

IF (PRCB-> DPCROUTINAACTIVE == false &&prb-> dpcinterruptrequested == false)

{

// If the DPC object priority is high;

/ / Or the DPC queue length exceeds the threshold maximumdpcqueueuedepth;

/ / Or the DPC request rate is less than the threshold minimumdpcrate

IF ((DPC-> Importance! = LowImportance) ||

(PRCB-> DPCQueuedepth> = prcb-> maximumth ||

(PRCB-> DPCRequestrate minimumdpcrate))))

{

/ / When the trigger condition is met, the DPC interrupt request is issued.

PRCB-> DPCINTERRUPTREQUESTED = TRUE;

KirequestSoftwareInterrupt (Dispatch_level);

}

}

}

KELOWERIRQL (Oldirql);

Return (Lock == NULL);

}

The threshold here, in the Kiinitializekernel function (NTOS / KE / I386 / KERNLINI.C: 246), based on global variables KimaximumUmumdpcqueueueuedepth, Kiminimumdpcrate, and KiadjustDPCTHRESHOLD. The global variables can be set via the DPCQueuedEpth, MiniMumdpcrate and AdjustDpcThreshold three key values ​​under the registry key (HKEY_LOCAL_MACHINE / SYSTEM / CURRENTCONTROLSET / Control / Session Manager / kernel /). For specific setup methods, refer to the dynamic index such as the processor /% DPC Time of MSDN and Performance Countries. The iRequestDPC function that handles the DPC object with the drive binding is just a simple package of the KeinsertQueuedPC function.

The following is quoted:

#define iRequestDPC (DeviceObject, IRP, Context) (/

KeinsertQueuedPC (& (DeviceObject) -> DPC, (IRP), (Context))))))))

The keremoveueuedPC function corresponding to the KeinsertQueuedPC function (NTOS / Ke / DPCOBJ.C: 272) is actually just a function of simply deleting DPC objects from the DPC queue. Finally, the KESETIMPORTANCEDPC function (NTOS / Ke / DPCOBJ.C: 367) and the KESETTARGETPROCE.C: 367) and KESETTARGETPROC: 401) are actually modified by directly modifying the DPC object structure. Kdpc :: Number is greater than maximum_processors = 32, used to specify the target CPU of the DPC object. If you call KSetTargetProcessRDPC (PKDPC, 2), PKDPC = Maximum_Processors 2. After learning the approximate maintenance function function of the DPC object and the DPC queue, let's take a look at the maintenance process slightly complex in the multiprocessor's DPC queue. The pro previously referred to the processor number used by the DPC object, so when the KeinsertQueuedPC function starts to get the processor control block, it is necessary to determine whether Number points to a processor and obtain the corresponding corresponding to the list of global processor control blocks. The processor control block is as follows: The following is a reference:

IF (DPC-> Number> = Maximum_Processors) // Number is used to specify the processor when Maximum_Processors

{

Processor = DPC-> Number - Maximum_Processors;

PRCB = KiprocessorBlock [Processor]; // Globally unique processor control block list

}

Else

{

PRCB = KegetCurrentPRCB ();

}

Kiacquirespinlock (& ​​prcb-> dpclock); // Using a spin lock protection processor control block DPC queue

When it is judged whether or not DPC interrupt request is issued in the KeinsertQueuedPC function, it is also necessary to do more complex logical judgment. The DPC object target processor is the current processor, and can be processed as the previous single processor, directly sends a DPC interrupt request; however, for the DPC object destination processor is the case of other processors, you must send IPI using the KIIPISEND function. (InterProcessor Interrupt) interrupt, notify the target processor execution action. This IPI interrupt is a special IRQL between the system power-down interrupt (Power_level) and clock interrupts specifically for coordinating multiple processors in the case of multiprocessor. In addition, in the case of multiprocessor, various operations of the DPC queue need to be protected from the DPC queue of this processor control block to avoid synchronization issues. Thus we can see that the DPC queue is one of the processors. We can completely bind a DPC object to a processor, implement the effects of thread affinity (Thread Affinity), optimized Performance in multiprocessor environments. But this also brings a problem, that is, the ISR program can be called with the DPC callback function, and some extent also cause an increase in development complexity. For details, please refer to DDK. KERNEL-MODE DRIVER Architecture / Design Guide / Servicing Interrupts / DPC Objects and DPCS

转载请注明原文地址:https://www.9cbs.com/read-29259.html

New Post(0)