【UEFI基础】UEFI网络框架之支撑模块DPC

本文介绍了UEFI中DPC(Deferred Procedure Call)的工作原理及其内部实现细节,包括DPC的注册与调度过程,以及相关的数据结构和核心函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DPC

DPC全称Deferred Procedure Call。

Defer的意思是延迟,而这个DPC的作用就是注册函数,然后在之后的某个阶段调用。

DPC在UEFI的实现中包括两个部分。

一部分是库函数DxeDpcLib,它实现了两个函数:

QueueDpc():

/**
  Add a Deferred Procedure Call to the end of the DPC queue.

  @param[in]  DpcTpl        The EFI_TPL that the DPC should be invoked.
  @param[in]  DpcProcedure  Pointer to the DPC's function.
  @param[in]  DpcContext    Pointer to the DPC's context.  Passed to DpcProcedure
                            when DpcProcedure is invoked.

  @retval EFI_SUCCESS            The DPC was queued.
  @retval EFI_INVALID_PARAMETER  DpcTpl is not a valid EFI_TPL.
  @retval EFI_INVALID_PARAMETER  DpcProcedure is NULL.
  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to
                                 add the DPC to the queue.

**/
EFI_STATUS
EFIAPI
QueueDpc (
  IN EFI_TPL            DpcTpl,
  IN EFI_DPC_PROCEDURE  DpcProcedure,
  IN VOID               *DpcContext    OPTIONAL
  );

DispatchDpc():

/**
  Dispatch the queue of DPCs.  ALL DPCs that have been queued with a DpcTpl
  value greater than or equal to the current TPL are invoked in the order that
  they were queued.  DPCs with higher DpcTpl values are invoked before DPCs with
  lower DpcTpl values.

  @retval EFI_SUCCESS    One or more DPCs were invoked.
  @retval EFI_NOT_FOUND  No DPCs were invoked.

**/
EFI_STATUS
EFIAPI
DispatchDpc (
  VOID
  );

另一个部分是EFI_DPC_PROTOCOL:

EFI_DPC_PROTOCOL mDpc = {
  DpcQueueDpc,
  DpcDispatchDpc
};

实际上在DxeDpcLib中只是简单调用了EFI_DPC_PROTOCOL的接口函数。

所以真正的重点还是在EFI_DPC_PROTOCOL上。

EFI_DPC_PROTOCOL

EFI_DPC_PROTOCOL是在一个DXE模块中安装的,安装的过程也很简单,查看具体的模块入口代码:

EFI_STATUS
EFIAPI
DpcDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  UINTN       Index;

  //
  // ASSERT() if the EFI_DPC_PROTOCOL is already present in the handle database
  //
  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiDpcProtocolGuid);

  //
  // Initialize the DPC queue for all possible TPL values
  //
  for (Index = 0; Index <= TPL_HIGH_LEVEL; Index++) {
    InitializeListHead (&mDpcQueue[Index]);
  }

  //
  // Install the EFI_DPC_PROTOCOL instance onto a new handle
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &mDpcHandle,
                  &gEfiDpcProtocolGuid, 
                  &mDpc,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);

  return Status;
}

这里唯一需要关注的就是mDpcQueue这个全局变量。它是一个链表数组:

LIST_ENTRY      mDpcQueue[TPL_HIGH_LEVEL + 1];

每一个元素对应一种优先级。

这里创建了所有可能的优先级,但实际上常用的也就下面几种:

//
// Task priority level
//
#define TPL_APPLICATION       4
#define TPL_CALLBACK          8
#define TPL_NOTIFY            16
#define TPL_HIGH_LEVEL        31

这个链表真正的元素是DpcEntry,其结构如下:

//
// Internal data struture for managing DPCs.  A DPC entry is either on the free
// list or on a DPC queue at a specific EFI_TPL.
//
typedef struct {
  LIST_ENTRY             ListEntry;
  EFI_DPC_PROCEDURE  DpcProcedure;
  VOID               *DpcContext;
} DPC_ENTRY;

第一个参数ListEntry就是用来处理链表的,而后面的两个参数,一个是函数指针,一个是函数的参数。

EFI_DPC_PROCEDURE定义如下:

/**
  Invoke a Deferred Procedure Call.

  @param  DpcContext           The pointer to the Deferred Procedure Call's context,
                               which is implementation dependent.

**/
typedef
VOID
(EFIAPI *EFI_DPC_PROCEDURE)(
  IN VOID  *DpcContext
  );

这个就是我们的主角:Deferred Procedure Call,简称DPC。

初始化完成后,mDpcQueue大致是这样的:

EFI_DPC_PROTOCOL中就两个接口函数,下面来具体介绍。

DpcQueueDpc

EFI_STATUS
EFIAPI
DpcQueueDpc (
  IN EFI_DPC_PROTOCOL   *This,
  IN EFI_TPL            DpcTpl,
  IN EFI_DPC_PROCEDURE  DpcProcedure,
  IN VOID               *DpcContext    OPTIONAL
  )

该函数的操作流程如下:

1. 首先需要判断优先级DpcTpl是否满足要求;

2. 初始化一个全局的变量mDpcEntryFreeList:

需要注意以下几点:

1)64个是一次创建的个数,如果64个全用完了,就会再创建64个。这里的创建是指真正的分配了内存了的,不像mDpcQueue那样只是指针的链接。

2)这些DpcEntry会被移动到mDpcQueue中去,执行完了之后又会被放回到mDpcEntryFreeList中,以达到循环使用的目的。

这么做得好处,一是内存分配不至于太分散;二是提高了效率和利用率。

3. 从mDpcEntryFreeList中拿出一个DpcEntry,为它赋值DpcProcedure和DpcContext,这也算是初始化;

4. 将初始化好的DpcEntry插入到mDpcQueue对应的优先级上;

5. mDpcQueueDepth全局变量的值加1,表示又多了一个DPC;

以上就是大致的流程。

DpcDispatchDpc

EFI_STATUS
EFIAPI
DpcDispatchDpc (
  IN EFI_DPC_PROTOCOL  *This
  )

该函数的操作流程如下:

1. 判断mDpcQueueDepth值是否大于0,如果大于0,表示有DPC可以被调用;

2. 遍历mDpcQueue,找到非空的链表节点,将它从mDpcQueue中删去,mDpcQueueDepth的值相应的减1;

3. 执行那个从mDpcQueue去除下来的DpcEntry对应的DpcProcedure:

        //
        // Invoke the DPC passing in its context
        //
        (DpcEntry->DpcProcedure) (DpcEntry->DpcContext);

4. 将从mDpcQueue去除下来的DpcEntry在放回到mDpcEntryFreeList中。

以上就是DpcDispatchDpc的流程。

一次DpcDispatchDpc会将mDpcQueue中的所有DPC都执行一遍。

说明

目前DPC只在网络相关的驱动中使用,并且使用的地方还很多。

具体如何使用的,后续可以举几个例子。

<think>我们正在分析两个指标:DPCsQueued/sec(每秒排队的DPC数量)高,但%DPCTime(处理器用于处理DPC的时间百分比)正常的情况。首先回顾基本概念:-DPC(DeferredProcedureCall,延迟过程调用)是Windows内核中用于延迟处理中断相关任务的一种机制。当中断发生时,中断服务例程(ISR)会快速处理最紧急的部分,然后将需要进一步处理的任务作为DPC排队,以便在较低的中断请求级别(IRQL)上执行。-DPCsQueued/sec计数器表示每秒被排入队列的DPC数量。-%DPCTime表示处理器在DPC例程上花费的时间百分比。问题:为什么会出现DPCsQueued/sec很高,但%DPCTime却正常(比如低于20%)?原因分析:1.**高频低负载DPC**:-驱动程序中可能注册了大量非常短暂的DPC调用。这些DPC执行速度极快(例如,每个DPC只需几微秒),因此即使排队数量很大,但总执行时间并不长。-数学表达:设每秒排队DPC数量为$N$,每个DPC平均执行时间为$T$秒,则总DPC时间为$N\timesT$秒。若$T$非常小,即使$N$很大,$N\timesT$也可能很小,因此%DPCTime正常。-例如:假设$N=10000$DPCs/sec,$T=1\mus$(即$10^{-6}$秒),则总时间=10000×0.000001=0.01秒,即占单个CPU核心1%(假设单核系统),%DPCTime=1%,属于正常范围。2.**多处理器系统负载分散**:-在拥有多个CPU核心的系统上,DPC队列可能被均匀分配到各个核心。假设系统有$M$个核心,则每个核心实际处理的DPC数量为$N/M$。此时,即使总体DPCsQueued/sec很高,但每个核心的%DPCTime可能并不高。-示例:$N=8000$DPCs/sec,$M=8$个核心,则每个核心约处理1000DPCs/sec。若每个DPC执行时间$T=10\mus$,则每个核心的DPC总时间为1000×0.00001=0.01秒(即1%的利用率),因此%DPCTime显示正常。3.**DPC队列深度增加但执行未阻塞**:-DPCsQueued/sec高表示DPC被排队的速率高,但如果处理DPC的速率(DPC执行速率)同样很高,则队列不会积压,每个DPC都能被及时处理,因此不会导致%DPCTime升高。这通常发生在系统能够高效处理DPC的情况下。-注意:DPCsQueued/sec仅表示入队速率,并不反映队列积压情况。实际队列积压可通过`\System\DPCQueueLength`性能计数器观察。4.**定时器驱动的DPC**:-某些驱动程序使用定时器(如引用[1]提到的同步定时器)定期触发DPC。如果定时器间隔设置得非常短(例如1毫秒),则每秒可能产生1000次DPC排队(即1000DPCsQueued/sec)。但每次DPC执行的任务非常简单,因此%DPCTime仍然很低。关联分析:-**正常情况**:当系统处理DPC的效率很高(即DPC执行速度快且在多核上分布良好)时,即使DPCsQueued/sec很高,%DPCTime也可以保持正常。这通常不是问题。-**异常情况**:如果同时观察到%DPCTime高(例如>20%)和DPCsQueued/sec高,则表明DPC执行效率低(单个DPC耗时过长)或系统过载,需要优化。监控建议:1.检查`\System\DPCQueueLength`:如果队列长度持续增长,说明DPC处理速度跟不上入队速度,可能导致延迟。2.使用Windows性能分析器(WPA)或xperf分析DPC执行时间分布,识别是否有少数DPC执行时间过长。3.结合`Interrupts/sec`:高中断率可能驱动高DPC排队率,但中断处理本身不计入%DPCTime(属于%InterruptTime)。结论:DPCsQueued/sec高而%DPCTime正常,通常表明系统正在高效处理大量轻量级DPC任务,无需立即干预。但需注意:-如果DPCsQueued/sec持续异常高(例如单核超过5000/sec),即使%DPCTime正常,也可能消耗一定CPU资源,间接影响应用程序性能。-应排查是否有不必要的频繁DPC注册(如驱动程序设置过短的定时器间隔),可通过更新驱动或调整配置优化。§§相关问题§§1.如何检测系统中哪个驱动程序注册了最多的DPC?2.DPC执行时间过长会对系统性能产生什么具体影响?3.在多核系统中如何优化DPC的负载均衡?4.如何区分DPC队列增长是由硬件中断还是软件定时器引起的?</think>### DPCs Queued/sec 高而 %DPC Time 正常的关联分析 当出现 **DPCs Queued/sec 数值高** 但 **%DPC Time 保持正常** 时,表明系统存在特殊的运行状态。以下是关键原因和机制分析: #### 核心原因机制 1. **高频轻量级DPC请求** - 驱动程序注册了大量**执行时间极短**(< 10μs)的DPC任务 - 数学关系: $$ \text{总DPC时间} = \sum_{i=1}^{N} T_i \quad (N=\text{DPCs Queued/sec}, T_i=\text{单DPC执行时间}) $$ - 当 $T_i$ 足够小,即使 $N$ 很大,总时间占比 (%DPC Time) 仍可维持在低位 - *示例*:若每秒处理 10,000 个 DPC,每个耗时 5μs,则总耗时仅 50ms(单核占比 5%) 2. **多处理器高效分发** - 在 SMP 系统中,DPC 队列被均匀分配到多个 CPU 核心 - 处理效率公式: $$ \text{有效处理率} = \frac{N}{M} \times \frac{1}{T_{\text{avg}}} \quad (M=\text{CPU核心数}) $$ - 核心数 $M$ 增加时,单个核心的 %DPC Time 显著降低[^1] 3. **中断风暴与快速响应** - 网络适配器/存储设备触发**高频中断**(如万兆网卡小包传输) - 驱动快速转换中断为 DPC,但 DPC 本身仅做轻量级数据中转 - 此时 `Interrupts/sec` 与 `DPCs Queued/sec` 同步升高,但 %DPC Time 稳定 4. **定时器精度优化** - 驱动程序使用 **periodic timers** 但设置极小间隔(如 100μs) - 每个 timer 到期仅触发简单状态检查,不执行耗时操作 - 导致 DPC 排队率激增但实际 CPU 消耗有限 #### 诊断流程图 ```mermaid graph TD A[DPCs Queued/sec > 500/核心] --> B{检查 %DPC Time} B -->|≤ 20%| C[确认多核负载分散] C --> D[分析PerfMon:<br>• Processor(_Total)\DPC Rate<br>• Processor(*)\%DPC Time] D -->|单核负载正常| E[高频轻量级DPC] D -->|某核心%DPC Time偏高| F[驱动负载不均衡] B -->|> 20%| G[存在耗时DPC任务] ``` #### 潜在风险与监控建议 即使 %DPC Time 正常,高 DPC 排队率仍可能引发: 1. **线程调度延迟** DPC 在 IRQL >= DISPATCH_LEVEL 运行,会阻塞用户线程调度 2. **DPC 队列积压** 当瞬时请求超过处理能力时,`\System\DPC Queue Length` 计数器将增长 3. **硬件兼容隐患** 常见于未优化的 UEFI 固件或 beta 版驱动(如 NVIDIA/Aquantia 网卡驱动) **排查工具建议**: ```powershell # 1. 实时监控核心负载分布 typeperf "\Processor(*)\% DPC Time" "\System\DPCs Queued/sec" -si 2 # 2. 捕获DPC执行详情 wpr.exe -start DPCInterrupt -filemode # 复现问题后停止 wpr.exe -stop DPC_Trace.etl ``` > **关键结论**:DPCs Queued/sec 高而 %DPC Time 正常,表明系统正在**高效处理高频轻量级任务**。需警惕调度延迟风险,但通常无需立即干预,除非伴随 `DPC Queue Length` 持续增长或应用程序延迟。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值