Analysis of Win32 core DPC design ideas and Implementation ideas [reprinted]

Source: Internet
Author: User
Tags apc

the X86 architecture is designed based on the concept of interruption. Therefore, from DOS to Win32, the concept of interruption is widely used in the operating system to express asynchronous operations. However, it is not exclusive to dos.
in Win32, the system needs to schedule multiple tasks, therefore, the Code of the interrupt response must be as simple as possible and the control should be handed back to the system as soon as possible. Although the response speed and implementation of system scheduling are convenient, there are still many functions that need to be completed in the interrupt response. Therefore, the Win32 core provides two special IRQL software interrupt levels: DPC (deferred procedure
call) and APC (asynchronous procedure call), which are used to implement latency and asynchronous process calls.
In IRQL hierarchy, DPC and APC are between high-level device interruptions and passive interruptions at the lowest level, and are at the interruption level used by the operating system to complete special method calls. Unlike device interruptions that process hardware
, device interruptions are designed to implement asynchronous function calls, therefore, the operating system itself is highly dependent on them.
APC is not discussed here for the time being. I will have the opportunity to write more articles for further discussion

DPC functions as ISR (Interrupt Service
Routine. Just because ISR splits some functions out of the operating system and puts them in the corresponding DPC for simplicity and return control to the operating system, the call is delayed. Because DPC
IRQL is only based on APC and passive interruptions. Therefore, the system can easily handle high-level interruptions, and then slowly process the accumulated relatively less urgent functions at the DPC level.
DPC can be understood as an encapsulated object of a callback function. System or device driverProgram, Such as the adddevice function of the device driver or
When the dispatchpnp function processes the irp_mn_start_device request, it initializes a DPC object. In ISR, it determines whether to further process the interrupt.
Insert the DPC object to the system DPC queue. After the system finishes IRQL processing
At the dispatch_level, the DPC objects in the DPC queue are processed slowly. The callback function encapsulated by each DPC object uses the simultaneously encapsulated call parameters, which are called by the system.
ISR is too late to complete the work; if you need further work, you can still request to insert the DPC object to the DPC queue.

DPC objects have two types of objects: dpcforisr and customdpc. The former is a device
Object); the latter is maintained by the driver. However, in terms of implementation, only one DPC object exists. The maintenance functions involved in dpcforisr are actually
Only one encapsulation of customdpc.

First, let's look at the implementation of initializing DPC objects. The keinitializedpc function (ntos/ke/dpcobj. C: 39) initializes the specific DPC object. In fact, it is to fill in a memory structure kdpc (ntos/INC/ntosdef. h: 331 ).

Reference:

//
// Deferred Procedure Call (DPC) object
//

Typedef struct _ kdpc {
Cshort type;
Uchar number;
Uchar importance;
List_entry dpclistentry;
Pkdeferred_routine deferredroutine;
Pvoid deferredcontext;
Pvoid systemargument1;
Pvoid systemargument2;
Pulong_ptr lock;
} Kdpc, * pkdpc, * restricted_pointer prkdpc;

Type indicates the type of this kernel object, which is defined in kobjects Enumeration type (ntos/INC/ke. h: 122). The default value is dpcobject = 0X13. In addition, a threadeddpcobject = 0x18 is added to WINXP/2003.
Number is used to specify the processor's DPC queue to which the DPC object is added in a multi-processor environment. We will discuss the detailed description of the multi-processor. The default value is 0.
Importance indicates the importance of this DPC object. It is defined in the kdpc_importance Enumeration type (ntos/INC/ntosdef. h: 321). The default value is mediumimportance = 1.
Dpclistentry is a linked list pointer used to maintain DPC queues.
Deferredroutine
Is the callback function bound to the DPC object. The following deferredcontext, systemargument1, and systemargument2 are the callback functions respectively.
The parameter used for calling. For example, when iorequestdpc is called in ISR, the next two parameters are used to pass the IRP and context parameters to the callback function of DPC.
Lock stores the spin lock of the DPC queue where the DPC object is located. It is used to lock the DPC queue and determine whether the DPC object is added to a DPC queue.

After understanding the structure of the kdpc object, it is very easy to maintain the code. The keinitializedpc function initializes the structure of the kdpc object to the initial value. The ioinitializedpcrequest function is a simple packaging of the keinitializedpc function, as shown below:

Reference:

# Define ioinitializedpcrequest (deviceobject, dpcroutine )(/
Keinitializedpc (& (deviceobject)-> DPC ,/
(Pkdeferred_routine) (dpcroutine ),/
(Deviceobject )))

Note that in WINXP/2003, both the keinitializedpc function and the keinitializethreadeddpc function are completed by a kiinitializedpc function, but the object types defined by the last parameter passed are different.

The keinsertqueuedpc function (ntos/ke/dpcobj. C: 89) is actually the core function of the system to maintain the DPC queue. Its pseudo code is as follows:

Reference:

Boolean keinsertqueuedpc (in prkdpc DPC, in pvoid systemargument1, in pvoid systemargument2)
{
Pkspin_lock lock;
Kirql oldirql;

Keraiseirql (high_level, & oldirql); // improves the current IRQL to the highest level, blocking other interruptions

Pkprcb = kegetcurrentprcb (); // obtain the current Processor Control Block

// Compare DPC-> lock to determine whether the 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 the parameter information of the DPC object
DPC-> systemargument1 = systemargument1;
DPC-> systemargument2 = systemargument2;

// Based on the priority of the DPC object, it is determined to add it to the header 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 a DPC interrupt request, determine whether to issue a DPC interrupt request.
If (prcb-> dpcroutineactive = false & prcb-> dpcinterruptrequested = false)
{
// If the priority of a DPC object is high;
// Or the DPC queue length exceeds the threshold value of maximumdpcqueuedepth;
// Or the DPC request rate is smaller than the threshold value minimumdpcrate
If (DPC-> importance! = Lowimportance) |
(Prcb-> dpcqueuedepth> = prcb-> maximumdpcqueuedepth) |
(Prcb-> dpcrequestrate <prcb-> minimumdpcrate ))
{
// If the trigger condition is met, a DPC interrupt request is sent.
Prcb-> dpcinterruptrequested = true;
Kirequestsoftwareinterrupt (dispatch_level );
}
}
}
Kelowerirql (oldirql );
Return (Lock = NULL );
}

The following thresholds are used in the kiinitializekernel function (ntos/ke/i386/kernlini. C: 246) According to the global variables.
Kimaximumdpcqueuedepth, kiminimumdpcrate, and kiadjustdpcthreshold are determined. These global variables can
To use the registry key (HKEY_LOCAL_MACHINE/system/CurrentControlSet/control/session
Dpcqueuedepth, minimumdpcrate, and adjustdpcthreshold in Manager/kernel /)
. For detailed settings, see msdn and dynamic indexes such as processor/% DPC time of performance counters.

The iorequestdpc function that processes the DPC object bound to the driver is a simple packaging of the keinsertqueuedpc function.

Reference:

# Define iorequestdpc (deviceobject, IRP, context )(/
Keinsertqueuedpc (& (deviceobject)-> DPC, (IRP), (context )))

The keremovequeuedpc function (ntos/ke/dpcobj. C: 272) corresponding to the keinsertqueuedpc function is actually just a simple function to delete a DPC object from a DPC queue.

Finally, the kesetimportancedpc function (ntos/ke/dpcobj. C: 367) and
The kesettargetprocessordpc function (ntos/ke/dpcobj. C: 401) is actually the corresponding domain for directly modifying the DPC object structure.
Kdpc: number greater than maximum_processors =
32 is used to specify the target cpu Of the DPC object. For example, after you call kesettargetprocessordpc (pkdpc, 2), pkdpc =
Maximum_processors + 2.

After learning about the maintenance functions of DPC objects and DPC queues, let's take a look at the complicated maintenance process of DPC queues under multi-processor.

As mentioned above, kdpc: Number specifies the processor number used by the DPC object. Therefore, when the keinsertqueuedpc function starts to obtain the processor control block, it is necessary to determine whether the number points to a processor, obtain the corresponding processor control block from the global Processor Control Block List. The Code is as follows:

Reference:

If (DPC-> number> = maximum_processors) // if the number is greater than maximum_processors, it is used to specify the processor.
{
Processor = DPC-> Number-maximum_processors;
Prcb = kiprocessorblock [processor]; // list of globally unique Processor Control Blocks

}
Else
{
Prcb = kegetcurrentprcb ();
}

Kiacquirespinlock (& prcb-> dpclock); // use the spin lock to protect the DPC queue in the Processor Control Block

When the keinsertqueuedpc function determines whether a DPC interrupt request is sent, it also requires more complex logic judgment.
The target processor of the DPC object is the current processor. It can be processed in the same way as the previous one processor and directly send DPC interrupt requests. However, if the target processor of the DPC object is another processor,
You must use the kiipisend function to send IPI (interprocessor
Interrupt) is interrupted to notify the target processor to execute the action. This IPI interrupt is a special IRQL between the power-down (power_level) of the system and the clock interruption. It is specially used
Coordinates the work of multiple processors in the case of multiple processors.
In addition, in the case of multi-processor, various operations on the DPC queue must be protected by the DPC queue spin lock of the control block of this processor to avoid synchronization problems.

From this we can see that the DPC queue is actually one for each processor. We can bind a DPC object to a certain processor to achieve thread affinity (thread
Affinity) to optimize the performance in a multi-processor environment. But this also brings about a problem, that is, ISR programs can be called with DPC callback functions at the same time. To some extent
The increase in sending complexity. For more information, see the related documentation in DDK.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.