sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/networking/napimodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/zh_TW/networking/napimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/it_IT/networking/napimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/ja_JP/networking/napimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/ko_KR/networking/napimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hPortuguese (Brazilian)}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/pt_BR/networking/napimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/sp_SP/networking/napimodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhcomment)}(h7SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)h]h7SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhP/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/networking/napi.rsthKubhnote)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]h paragraph)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hh5Documentation/translations/zh_CN/disclaimer-zh_CN.rsthKhhubah}(h]h ]h"]h$]h&]uh1hhhhhhhhNubh field_list)}(hhh](hfield)}(hhh](h field_name)}(hOriginalh]hOriginal}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh field_body)}(h"Documentation/networking/napi.rst h]h)}(h!Documentation/networking/napi.rsth]h!Documentation/networking/napi.rst}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h翻译h]h翻译}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubj)}(h-王亚鑫 Yaxin Wang h]h)}(h,王亚鑫 Yaxin Wang h](h王亚鑫 Yaxin Wang <}(hj4hhhNhNubh reference)}(hwang.yaxin@zte.com.cnh]hwang.yaxin@zte.com.cn}(hj>hhhNhNubah}(h]h ]h"]h$]h&]refurimailto:wang.yaxin@zte.com.cnuh1j<hj4ubh>}(hj4hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj0ubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(hNAPIh]hNAPI}(hjqhhhNhNubah}(h]h ]h"]h$]h&]uh1johjlhhhhhK ubh)}(hvNAPI 是 Linux 网络堆栈中使用的事件处理机制。NAPI 的名称现在不再代表任何特定含义 [#]_。h](hoNAPI 是 Linux 网络堆栈中使用的事件处理机制。NAPI 的名称现在不再代表任何特定含义 }(hjhhhNhNubhfootnote_reference)}(h[#]_h]h1}(hjhhhNhNubah}(h]id1ah ]h"]h$]h&]autoKrefidid13docname"translations/zh_CN/networking/napiuh1jhjresolvedKubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjlhhubh)}(h在基本操作中,设备通过中断通知主机有新事件发生。主机随后调度 NAPI 实例来处理这些事件。 该设备也可以通过 NAPI 进行事件轮询,而无需先接收中断信号(:ref:`忙轮询`)。h](h在基本操作中,设备通过中断通知主机有新事件发生。主机随后调度 NAPI 实例来处理这些事件。 该设备也可以通过 NAPI 进行事件轮询,而无需先接收中断信号(}(hjhhhNhNubh)}(h:ref:`忙轮询`h]hinline)}(hjh]h 忙轮询}(hjhhhNhNubah}(h]h ](xrefstdstd-refeh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftyperef refexplicitrefwarn reftarget poll_zh_cnuh1hhhhKhjubh)。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjlhhubh)}(hNAPI 处理通常发生在软中断上下文中,但有一个选项,可以使用 :ref:`单独的内核线程` 来进行 NAPI 处理。h](hTNAPI 处理通常发生在软中断上下文中,但有一个选项,可以使用 }(hjhhhNhNubh)}(h,:ref:`单独的内核线程`h]j)}(hjh]h单独的内核线程}(hjhhhNhNubah}(h]h ](jstdstd-refeh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftyperef refexplicitrefwarnjthreaded_zh_cnuh1hhhhKhjubh 来进行 NAPI 处理。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjlhhubh)}(hz总的来说,NAPI 为驱动程序抽象了事件(数据包接收和发送)处理的上下文环境和配置情况。h]hz总的来说,NAPI 为驱动程序抽象了事件(数据包接收和发送)处理的上下文环境和配置情况。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjlhhubjk)}(hhh](jp)}(h驱动程序APIh]h驱动程序API}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1johjhhhhhKubh)}(hXNAPI 最重要的两个元素是 struct napi_struct 和关联的 poll 方法。struct napi_struct 持有 NAPI 实例的状态,而方法则是与驱动程序相关的事件处理器。该方法通常会释放已传输的发送 (Tx)数据包并处理新接收的数据包。h]hXNAPI 最重要的两个元素是 struct napi_struct 和关联的 poll 方法。struct napi_struct 持有 NAPI 实例的状态,而方法则是与驱动程序相关的事件处理器。该方法通常会释放已传输的发送 (Tx)数据包并处理新接收的数据包。}(hj/hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubhtarget)}(h.. _drv_ctrl_zh_CN:h]h}(h]h ]h"]h$]h&]jdrv-ctrl-zh-cnuh1j=hK*hjhhhhubjk)}(hhh](jp)}(h 控制APIh]h 控制API}(hjMhhhNhNubah}(h]h ]h"]h$]h&]uh1johjJhhhhhK"ubh)}(hnetif_napi_add() 和 netif_napi_del() 用于向系统中添加/删除一个 NAPI 实例。实例会被 附加到作为参数传递的 netdevice上(并在 netdevice 注销时自动删除)。实例在添加时处于禁 用状态。h]hnetif_napi_add() 和 netif_napi_del() 用于向系统中添加/删除一个 NAPI 实例。实例会被 附加到作为参数传递的 netdevice上(并在 netdevice 注销时自动删除)。实例在添加时处于禁 用状态。}(hj[hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK$hjJhhubh)}(hnapi_enable() 和 napi_disable() 管理禁用状态。禁用的 NAPI 不会被调度,并且保证其 poll 方法不会被调用。napi_disable() 会等待 NAPI 实例的所有权被释放。h]hnapi_enable() 和 napi_disable() 管理禁用状态。禁用的 NAPI 不会被调度,并且保证其 poll 方法不会被调用。napi_disable() 会等待 NAPI 实例的所有权被释放。}(hjihhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK(hjJhhubh)}(hX这些控制 API 并非幂等的。控制 API 调用在面对数据路径 API 的并发使用时是安全的,但控制 API 调用顺序错误可能会导致系统崩溃、死锁或竞态条件。例如,连续多次调用 napi_disable() 会造成死锁。h]hX这些控制 API 并非幂等的。控制 API 调用在面对数据路径 API 的并发使用时是安全的,但控制 API 调用顺序错误可能会导致系统崩溃、死锁或竞态条件。例如,连续多次调用 napi_disable() 会造成死锁。}(hjwhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK+hjJhhubeh}(h](jIid2eh ]h"]( 控制apidrv_ctrl_zh_cneh$]h&]uh1jjhjhhhhhK"expect_referenced_by_name}jj?sexpect_referenced_by_id}jIj?subjk)}(hhh](jp)}(h数据路径APIh]h数据路径API}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1johjhhhhhK0ubh)}(hnapi_schedule() 是调度 NAPI 轮询的基本方法。驱动程序应在其中断处理程序中调用此函数 (更多信息请参见 :ref:`drv_sched_zh_CN`)。成功的 napi_schedule() 调用将获得 NAPI 实例 的所有权。h](hnapi_schedule() 是调度 NAPI 轮询的基本方法。驱动程序应在其中断处理程序中调用此函数 (更多信息请参见 }(hjhhhNhNubh)}(h:ref:`drv_sched_zh_CN`h]j)}(hjh]hdrv_sched_zh_CN}(hjhhhNhNubah}(h]h ](jstdstd-refeh"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]refdocj refdomainjreftyperef refexplicitrefwarnjdrv_sched_zh_cnuh1hhhhK2hjubhK)。成功的 napi_schedule() 调用将获得 NAPI 实例 的所有权。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK2hjhhubh)}(hX9之后,在 NAPI 被调度后,驱动程序的 poll 方法将被调用以处理事件/数据包。该方法接受一个 ``budget`` 参数 - 驱动程序可以处理任意数量的发送 (Tx) 数据包完成,但处理最多处理 ``budget`` 个接收 (Rx) 数据包。处理接收数据包通常开销更大。h](hz之后,在 NAPI 被调度后,驱动程序的 poll 方法将被调用以处理事件/数据包。该方法接受一个 }(hjhhhNhNubhliteral)}(h ``budget``h]hbudget}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhe 参数 - 驱动程序可以处理任意数量的发送 (Tx) 数据包完成,但处理最多处理 }(hjhhhNhNubj)}(h ``budget``h]hbudget}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhF 个接收 (Rx) 数据包。处理接收数据包通常开销更大。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK6hjhhubh)}(hX换句话说,对于接收数据包的处理,``budget`` 参数限制了驱动程序在单次轮询中能够处理的数 据包数量。当 ``budget`` 为 0 时,像页面池或 XDP 这类专门用于接收的 API 根本无法使用。 无论 ``budget`` 的值是多少,skb 的发送处理都应该进行,但是如果 ``budget`` 参数为 0, 驱动程序就不能调用任何 XDP(或页面池)API。h](h0换句话说,对于接收数据包的处理,}(hj hhhNhNubj)}(h ``budget``h]hbudget}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhT 参数限制了驱动程序在单次轮询中能够处理的数 据包数量。当 }(hj hhhNhNubj)}(h ``budget``h]hbudget}(hj#hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh^ 为 0 时,像页面池或 XDP 这类专门用于接收的 API 根本无法使用。 无论 }(hj hhhNhNubj)}(h ``budget``h]hbudget}(hj5hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhE 的值是多少,skb 的发送处理都应该进行,但是如果 }(hj hhhNhNubj)}(h ``budget``h]hbudget}(hjGhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhM 参数为 0, 驱动程序就不能调用任何 XDP(或页面池)API。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK:hjhhubhwarning)}(h如果内核仅尝试处理skb的发送完成情况,而不处理接收 (Rx) 或 XDP 数据包,那么 ``budget`` 参数可能为 0。h]h)}(h如果内核仅尝试处理skb的发送完成情况,而不处理接收 (Rx) 或 XDP 数据包,那么 ``budget`` 参数可能为 0。h](hi如果内核仅尝试处理skb的发送完成情况,而不处理接收 (Rx) 或 XDP 数据包,那么 }(hjehhhNhNubj)}(h ``budget``h]hbudget}(hjmhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjeubh 参数可能为 0。}(hjehhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKAhjaubah}(h]h ]h"]h$]h&]uh1j_hjhhhhhNubh)}(hX轮询方法会返回已完成的工作量。如果驱动程序仍有未完成的工作(例如,``budget`` 已用完), 轮询方法应精确返回 ``budget`` 的值。在这种情况下,NAPI 实例将再次被处理 / 轮询(无需 重新调度)。h](hc轮询方法会返回已完成的工作量。如果驱动程序仍有未完成的工作(例如,}(hjhhhNhNubj)}(h ``budget``h]hbudget}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh- 已用完), 轮询方法应精确返回 }(hjhhhNhNubj)}(h ``budget``h]hbudget}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubha 的值。在这种情况下,NAPI 实例将再次被处理 / 轮询(无需 重新调度)。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKDhjhhubh)}(h如果事件处理已完成(所有未处理的数据包都已处理完毕),轮询方法在返回之前应调用 napi_complete_done()。 napi_complete_done() 会释放实例的所有权。h]h如果事件处理已完成(所有未处理的数据包都已处理完毕),轮询方法在返回之前应调用 napi_complete_done()。 napi_complete_done() 会释放实例的所有权。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKHhjhhubj`)}(hXx当出现既完成了所有事件处理,又恰好达到了 ``budget`` 数量的情况时,必须谨慎处理。因为没 有办法将这种(很少出现的)情况报告给协议栈,所以驱动程序要么不调用 napi_complete_done() 并等待再次被调用,要么返回 ``budget - 1``。 当 ``budget`` 为 0 时,napi_complete_done() 绝对不能被调用。h](h)}(hX.当出现既完成了所有事件处理,又恰好达到了 ``budget`` 数量的情况时,必须谨慎处理。因为没 有办法将这种(很少出现的)情况报告给协议栈,所以驱动程序要么不调用 napi_complete_done() 并等待再次被调用,要么返回 ``budget - 1``。h](h=当出现既完成了所有事件处理,又恰好达到了 }(hjhhhNhNubj)}(h ``budget``h]hbudget}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 数量的情况时,必须谨慎处理。因为没 有办法将这种(很少出现的)情况报告给协议栈,所以驱动程序要么不调用 napi_complete_done() 并等待再次被调用,要么返回 }(hjhhhNhNubj)}(h``budget - 1``h]h budget - 1}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKMhjubh)}(hH当 ``budget`` 为 0 时,napi_complete_done() 绝对不能被调用。h](h当 }(hjhhhNhNubj)}(h ``budget``h]hbudget}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh: 为 0 时,napi_complete_done() 绝对不能被调用。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKQhjubeh}(h]h ]h"]h$]h&]uh1j_hjhhhhhNubeh}(h]id3ah ]h"]数据路径apiah$]h&]uh1jjhjhhhhhK0ubjk)}(hhh](jp)}(h 调用序列h]h 调用序列}(hj2hhhNhNubah}(h]h ]h"]h$]h&]uh1johj/hhhhhKTubh)}(hX9驱动程序不应假定调用的顺序是固定不变的。即使驱动程序没有调度该实例,轮询方法也可能会被调用 (除非该实例处于禁用状态)。同样,即便 napi_schedule() 调用成功,也不能保证轮询方法一定 会被调用(例如,如果该实例被禁用)。h]hX9驱动程序不应假定调用的顺序是固定不变的。即使驱动程序没有调度该实例,轮询方法也可能会被调用 (除非该实例处于禁用状态)。同样,即便 napi_schedule() 调用成功,也不能保证轮询方法一定 会被调用(例如,如果该实例被禁用)。}(hj@hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKVhj/hhubh)}(hX$正如在 :ref:`drv_ctrl_zh_CN` 部分所提到的,napi_disable() 以及后续对轮询方法的调用, 仅会等待该实例的所有权被释放,而不会等待轮询方法退出。这意味着,驱动程序在调用 napi_complete_done() 之后,应避免访问任何数据结构。h](h 正如在 }(hjNhhhNhNubh)}(h:ref:`drv_ctrl_zh_CN`h]j)}(hjXh]hdrv_ctrl_zh_CN}(hjZhhhNhNubah}(h]h ](jstdstd-refeh"]h$]h&]uh1jhjVubah}(h]h ]h"]h$]h&]refdocj refdomainjdreftyperef refexplicitrefwarnjdrv_ctrl_zh_cnuh1hhhhKZhjNubhX 部分所提到的,napi_disable() 以及后续对轮询方法的调用, 仅会等待该实例的所有权被释放,而不会等待轮询方法退出。这意味着,驱动程序在调用 napi_complete_done() 之后,应避免访问任何数据结构。}(hjNhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKZhj/hhubj>)}(h.. _drv_sched_zh_CN:h]h}(h]h ]h"]h$]h&]jdrv-sched-zh-cnuh1j=hKihj/hhhhubeh}(h]id4ah ]h"] 调用序列ah$]h&]uh1jjhjhhhhhKTubjk)}(hhh](jp)}(h调度与IRQ屏蔽h]h调度与IRQ屏蔽}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1johjhhhhhKaubh)}(h驱动程序应在调度 NAPI 实例后保持中断屏蔽 - 直到 NAPI 轮询完成,任何进一步的中断都是不必要的。h]h驱动程序应在调度 NAPI 实例后保持中断屏蔽 - 直到 NAPI 轮询完成,任何进一步的中断都是不必要的。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKchjhhubh)}(h显式屏蔽中断的驱动程序(而非设备自动屏蔽 IRQ)应使用 napi_schedule_prep() 和 __napi_schedule() 调用:h]h显式屏蔽中断的驱动程序(而非设备自动屏蔽 IRQ)应使用 napi_schedule_prep() 和 __napi_schedule() 调用:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKehjhhubh literal_block)}(hif (napi_schedule_prep(&v->napi)) { mydrv_mask_rxtx_irq(v->idx); /* 在屏蔽后调度以避免竞争 */ __napi_schedule(&v->napi); }h]hif (napi_schedule_prep(&v->napi)) { mydrv_mask_rxtx_irq(v->idx); /* 在屏蔽后调度以避免竞争 */ __napi_schedule(&v->napi); }}hjsbah}(h]h ]h"]h$]h&]hhƌforcelanguagechighlight_args}uh1jhhhKhhjhhubh)}(hAIRQ 仅应在成功调用 napi_complete_done() 后取消屏蔽:h]hAIRQ 仅应在成功调用 napi_complete_done() 后取消屏蔽:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKphjhhubj)}(hif (budget && napi_complete_done(&v->napi, work_done)) { mydrv_unmask_rxtx_irq(v->idx); return min(work_done, budget - 1); }h]hif (budget && napi_complete_done(&v->napi, work_done)) { mydrv_unmask_rxtx_irq(v->idx); return min(work_done, budget - 1); }}hjsbah}(h]h ]h"]h$]h&]hhjjjj}uh1jhhhKrhjhhubh)}(hXKnapi_schedule_irqoff() 是 napi_schedule() 的一个变体,它利用了在中断请求(IRQ)上下文 环境中调用所带来的特性(无需屏蔽中断)。如果中断请求(IRQ)是通过线程处理的(例如启用了 ``PREEMPT_RT`` 时的情况),napi_schedule_irqoff() 会回退为使用 napi_schedule() 。h](hnapi_schedule_irqoff() 是 napi_schedule() 的一个变体,它利用了在中断请求(IRQ)上下文 环境中调用所带来的特性(无需屏蔽中断)。如果中断请求(IRQ)是通过线程处理的(例如启用了 }(hjhhhNhNubj)}(h``PREEMPT_RT``h]h PREEMPT_RT}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhP 时的情况),napi_schedule_irqoff() 会回退为使用 napi_schedule() 。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKyhjhhubeh}(h](irqjeh ]h"](调度与irq屏蔽drv_sched_zh_cneh$]h&]uh1jjhjhhhhhKaj}jjsj}jjsubjk)}(hhh](jp)}(h实例到队列的映射h]h实例到队列的映射}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1johjhhhhhK~ubh)}(hX!现代设备每个接口有多个 NAPI 实例(struct napi_struct)。关于实例如何映射到队列和中断没有 严格要求。NAPI 主要是事件处理/轮询抽象,没有用户可见的语义。也就是说,大多数网络设备最终以 非常相似的方式使用 NAPI。h]hX!现代设备每个接口有多个 NAPI 实例(struct napi_struct)。关于实例如何映射到队列和中断没有 严格要求。NAPI 主要是事件处理/轮询抽象,没有用户可见的语义。也就是说,大多数网络设备最终以 非常相似的方式使用 NAPI。}(hj.hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hNAPI 实例最常以 1:1:1 映射到中断和队列对(队列对是由一个接收队列和一个发送队列组成的一组 队列)。h]hNAPI 实例最常以 1:1:1 映射到中断和队列对(队列对是由一个接收队列和一个发送队列组成的一组 队列)。}(hj<hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX/在不太常见的情况下,一个 NAPI 实例可能会用于处理多个队列,或者在单个内核上,接收(Rx)队列 和发送(Tx)队列可以由不同的 NAPI 实例来处理。不过,无论队列如何分配,通常 NAPI 实例和中断 之间仍然保持一一对应的关系。h]hX/在不太常见的情况下,一个 NAPI 实例可能会用于处理多个队列,或者在单个内核上,接收(Rx)队列 和发送(Tx)队列可以由不同的 NAPI 实例来处理。不过,无论队列如何分配,通常 NAPI 实例和中断 之间仍然保持一一对应的关系。}(hjJhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX值得注意的是,ethtool API 使用了 “通道” 这一术语,每个通道可以是 ``rx`` (接收)、``tx`` (发送)或 ``combined`` (组合)类型。目前尚不清楚一个通道具体由什么构成,建议的理解方式是 将一个通道视为一个为特定类型队列提供服务的 IRQ(中断请求)/ NAPI 实例。例如,配置为 1 个 ``rx`` 通道、1 个 ``tx`` 通道和 1 个 ``combined`` 通道的情况下,预计会使用 3 个中断、 2 个接收队列和 2 个发送队列。h](h]值得注意的是,ethtool API 使用了 “通道” 这一术语,每个通道可以是 }(hjXhhhNhNubj)}(h``rx``h]hrx}(hj`hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjXubh (接收)、}(hjXhhhNhNubj)}(h``tx``h]htx}(hjrhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjXubh (发送)或 }(hjXhhhNhNubj)}(h ``combined``h]hcombined}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjXubh (组合)类型。目前尚不清楚一个通道具体由什么构成,建议的理解方式是 将一个通道视为一个为特定类型队列提供服务的 IRQ(中断请求)/ NAPI 实例。例如,配置为 1 个 }(hjXhhhNhNubj)}(h``rx``h]hrx}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjXubh 通道、1 个 }(hjXhhhNhNubj)}(h``tx``h]htx}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjXubh 通道和 1 个 }(hjXhhhNhNubj)}(h ``combined``h]hcombined}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjXubh^ 通道的情况下,预计会使用 3 个中断、 2 个接收队列和 2 个发送队列。}(hjXhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id5ah ]h"]实例到队列的映射ah$]h&]uh1jjhjhhhhhK~ubjk)}(hhh](jp)}(h持久化NAPI配置h]h持久化NAPI配置}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1johjhhhhhKubh)}(hXg驱动程序常常会动态地分配和释放 NAPI 实例。这就导致每当 NAPI 实例被重新分配时,与 NAPI 相关 的用户配置就会丢失。netif_napi_add_config() API接口通过将每个 NAPI 实例与基于驱动程序定义 的索引值(如队列编号)的持久化 NAPI 配置相关联,从而避免了这种配置丢失的情况。h]hXg驱动程序常常会动态地分配和释放 NAPI 实例。这就导致每当 NAPI 实例被重新分配时,与 NAPI 相关 的用户配置就会丢失。netif_napi_add_config() API接口通过将每个 NAPI 实例与基于驱动程序定义 的索引值(如队列编号)的持久化 NAPI 配置相关联,从而避免了这种配置丢失的情况。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h使用此 API 可实现持久化的 NAPI 标识符(以及其他设置),这对于使用 ``SO_INCOMING_NAPI_ID`` 的用户空间程序来说是有益的。有关其他 NAPI 配置的设置,请参阅以下章节。h](h]使用此 API 可实现持久化的 NAPI 标识符(以及其他设置),这对于使用 }(hjhhhNhNubj)}(h``SO_INCOMING_NAPI_ID``h]hSO_INCOMING_NAPI_ID}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhg 的用户空间程序来说是有益的。有关其他 NAPI 配置的设置,请参阅以下章节。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h?驱动程序应尽可能尝试使用 netif_napi_add_config()。h]h?驱动程序应尽可能尝试使用 netif_napi_add_config()。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id6ah ]h"]持久化napi配置ah$]h&]uh1jjhjhhhhhKubeh}(h]apiah ]h"]驱动程序apiah$]h&]uh1jjhjlhhhhhKubjk)}(hhh](jp)}(h 用户APIh]h 用户API}(hj:hhhNhNubah}(h]h ]h"]h$]h&]uh1johj7hhhhhKubh)}(h用户与 NAPI 的交互依赖于 NAPI 实例 ID。这些实例 ID 仅通过 ``SO_INCOMING_NAPI_ID`` 套接字 选项对用户可见。h](hM用户与 NAPI 的交互依赖于 NAPI 实例 ID。这些实例 ID 仅通过 }(hjHhhhNhNubj)}(h``SO_INCOMING_NAPI_ID``h]hSO_INCOMING_NAPI_ID}(hjPhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjHubh# 套接字 选项对用户可见。}(hjHhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj7hhubh)}(h用户可以使用 Netlink 来查询某个设备或设备队列的 NAPI 标识符。这既可以在用户应用程序中通过编程 方式实现,也可以使用内核源代码树中包含的一个脚本:tools/net/ynl/pyynl/cli.py 来完成。h]h用户可以使用 Netlink 来查询某个设备或设备队列的 NAPI 标识符。这既可以在用户应用程序中通过编程 方式实现,也可以使用内核源代码树中包含的一个脚本:tools/net/ynl/pyynl/cli.py 来完成。}(hjhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj7hhubh)}(hl例如,使用该脚本转储某个设备的所有队列(这将显示每个队列的 NAPI 标识符):h]hl例如,使用该脚本转储某个设备的所有队列(这将显示每个队列的 NAPI 标识符):}(hjvhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj7hhubj)}(h$ kernel-source/tools/net/ynl/pyynl/cli.py \ --spec Documentation/netlink/specs/netdev.yaml \ --dump queue-get \ --json='{"ifindex": 2}'h]h$ kernel-source/tools/net/ynl/pyynl/cli.py \ --spec Documentation/netlink/specs/netdev.yaml \ --dump queue-get \ --json='{"ifindex": 2}'}hjsbah}(h]h ]h"]h$]h&]hhjjbashj}uh1jhhhKhj7hhubh)}(hk有关可用操作和属性的更多详细信息,请参阅 ``Documentation/netlink/specs/netdev.yaml``。h](h=有关可用操作和属性的更多详细信息,请参阅 }(hjhhhNhNubj)}(h+``Documentation/netlink/specs/netdev.yaml``h]h'Documentation/netlink/specs/netdev.yaml}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj7hhubjk)}(hhh](jp)}(h软件IRQ合并h]h软件IRQ合并}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1johjhhhhhKubh)}(h默认情况下,NAPI 不执行任何显式的事件合并。在大多数场景中,数据包的批量处理得益于设备进行 的中断请求(IRQ)合并。不过,在某些情况下,软件层面的合并操作也很有帮助。h]h默认情况下,NAPI 不执行任何显式的事件合并。在大多数场景中,数据包的批量处理得益于设备进行 的中断请求(IRQ)合并。不过,在某些情况下,软件层面的合并操作也很有帮助。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hXg可以将 NAPI 配置为设置一个重新轮询定时器,而不是在处理完所有数据包后立即取消屏蔽硬件中断。 网络设备的 ``gro_flush_timeout`` sysfs 配置项可用于控制该定时器的延迟时间,而 ``napi_defer_hard_irqs`` 则用于控制在 NAPI 放弃并重新启用硬件中断之前,连续进行空轮询的次数。h](h可以将 NAPI 配置为设置一个重新轮询定时器,而不是在处理完所有数据包后立即取消屏蔽硬件中断。 网络设备的 }(hjhhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhA sysfs 配置项可用于控制该定时器的延迟时间,而 }(hjhhhNhNubj)}(h``napi_defer_hard_irqs``h]hnapi_defer_hard_irqs}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhd 则用于控制在 NAPI 放弃并重新启用硬件中断之前,连续进行空轮询的次数。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX*上述参数也可以通过 Netlink 的 netdev-genl 接口,基于每个 NAPI 实例进行设置。当通过 Netlink 进行配置且是基于每个 NAPI 实例设置时,上述参数使用连字符(-)而非下划线(_) 来命名,即 ``gro-flush-timeout`` 和 ``napi-defer-hard-irqs``。h](h上述参数也可以通过 Netlink 的 netdev-genl 接口,基于每个 NAPI 实例进行设置。当通过 Netlink 进行配置且是基于每个 NAPI 实例设置时,上述参数使用连字符(-)而非下划线(_) 来命名,即 }(hjhhhNhNubj)}(h``gro-flush-timeout``h]hgro-flush-timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 和 }(hjhhhNhNubj)}(h``napi-defer-hard-irqs``h]hnapi-defer-hard-irqs}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h基于每个 NAPI 实例的配置既可以在用户应用程序中通过编程方式完成,也可以使用内核源代码树中的 一个脚本实现,该脚本为 ``tools/net/ynl/pyynl/cli.py``。h](h基于每个 NAPI 实例的配置既可以在用户应用程序中通过编程方式完成,也可以使用内核源代码树中的 一个脚本实现,该脚本为 }(hj7hhhNhNubj)}(h``tools/net/ynl/pyynl/cli.py``h]htools/net/ynl/pyynl/cli.py}(hj?hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj7ubh。}(hj7hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h-例如,通过如下方式使用该脚本:h]h-例如,通过如下方式使用该脚本:}(hjWhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(h$ kernel-source/tools/net/ynl/pyynl/cli.py \ --spec Documentation/netlink/specs/netdev.yaml \ --do napi-set \ --json='{"id": 345, "defer-hard-irqs": 111, "gro-flush-timeout": 11111}'h]h$ kernel-source/tools/net/ynl/pyynl/cli.py \ --spec Documentation/netlink/specs/netdev.yaml \ --do napi-set \ --json='{"id": 345, "defer-hard-irqs": 111, "gro-flush-timeout": 11111}'}hjesbah}(h]h ]h"]h$]h&]hhjjbashj}uh1jhhhKhjhhubh)}(h类似地,参数 ``irq-suspend-timeout`` 也可以通过 netlink 的 netdev-genl 设置。没有全局 的 sysfs 参数可用于设置这个值。h](h类似地,参数 }(hjuhhhNhNubj)}(h``irq-suspend-timeout``h]hirq-suspend-timeout}(hj}hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjuubhj 也可以通过 netlink 的 netdev-genl 设置。没有全局 的 sysfs 参数可用于设置这个值。}(hjuhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h``irq-suspend-timeout`` 用于确定应用程序可以完全挂起 IRQ 的时长。与 SO_PREFER_BUSY_POLL 结合使用,后者可以通过 ``EPIOCSPARAMS`` ioctl 在每个 epoll 上下文中设置。h](j)}(h``irq-suspend-timeout``h]hirq-suspend-timeout}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhv 用于确定应用程序可以完全挂起 IRQ 的时长。与 SO_PREFER_BUSY_POLL 结合使用,后者可以通过 }(hjhhhNhNubj)}(h``EPIOCSPARAMS``h]h EPIOCSPARAMS}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh, ioctl 在每个 epoll 上下文中设置。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj>)}(h.. _poll_zh_CN:h]h}(h]h ]h"]h$]h&]j poll-zh-cnuh1j=hKhjhhhhubeh}(h]id8ah ]h"]软件irq合并ah$]h&]uh1jjhj7hhhhhKubjk)}(hhh](jp)}(h 忙轮询h]h 忙轮询}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1johjhhhhhKubh)}(h忙轮询允许用户进程在设备中断触发前检查传入的数据包。与其他忙轮询一样,它以 CPU 周期换取更低 的延迟(生产环境中 NAPI 忙轮询的使用尚不明确)。h]h忙轮询允许用户进程在设备中断触发前检查传入的数据包。与其他忙轮询一样,它以 CPU 周期换取更低 的延迟(生产环境中 NAPI 忙轮询的使用尚不明确)。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h通过在选定套接字上设置 ``SO_BUSY_POLL`` 或使用全局 ``net.core.busy_poll`` 和 ``net.core.busy_read`` 等 sysctls 启用忙轮询。还存在基于 io_uring 的 NAPI 忙轮询 API 可使用。h](h"通过在选定套接字上设置 }(hjhhhNhNubj)}(h``SO_BUSY_POLL``h]h SO_BUSY_POLL}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 或使用全局 }(hjhhhNhNubj)}(h``net.core.busy_poll``h]hnet.core.busy_poll}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 和 }(hjhhhNhNubj)}(h``net.core.busy_read``h]hnet.core.busy_read}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh[ 等 sysctls 启用忙轮询。还存在基于 io_uring 的 NAPI 忙轮询 API 可使用。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h](jid9eh ]h"]( 忙轮询 poll_zh_cneh$]h&]uh1jjhj7hhhhhKj}j?jsj}jjsubjk)}(hhh](jp)}(h基于epoll的忙轮询h]h基于epoll的忙轮询}(hjGhhhNhNubah}(h]h ]h"]h$]h&]uh1johjDhhhhhKubh)}(h可以从 ``epoll_wait`` 调用直接触发数据包处理。为了使用此功能,用户应用程序必须确保添加到 epoll 上下文的所有文件描述符具有相同的 NAPI ID。h](h 可以从 }(hjUhhhNhNubj)}(h``epoll_wait``h]h epoll_wait}(hj]hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjUubh 调用直接触发数据包处理。为了使用此功能,用户应用程序必须确保添加到 epoll 上下文的所有文件描述符具有相同的 NAPI ID。}(hjUhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjDhhubh)}(hXh如果应用程序使用专用的 acceptor 线程,那么该应用程序可以获取传入连接的 NAPI ID(使用 SO_INCOMING_NAPI_ID)然后将该文件描述符分发给工作线程。工作线程将该文件描述符添加到其 epoll 上下文。这确保了每个工作线程的 epoll 上下文中所包含的文件描述符具有相同的 NAPI ID。h]hXh如果应用程序使用专用的 acceptor 线程,那么该应用程序可以获取传入连接的 NAPI ID(使用 SO_INCOMING_NAPI_ID)然后将该文件描述符分发给工作线程。工作线程将该文件描述符添加到其 epoll 上下文。这确保了每个工作线程的 epoll 上下文中所包含的文件描述符具有相同的 NAPI ID。}(hjuhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjDhhubh)}(h或者,如果应用程序使用 SO_REUSEPORT,可以插入 bpf 或 ebpf 程序来分发传入连接,使得每个 线程只接收具有相同 NAPI ID 的连接。但是必须谨慎处理系统中可能存在多个网卡的情况。h]h或者,如果应用程序使用 SO_REUSEPORT,可以插入 bpf 或 ebpf 程序来分发传入连接,使得每个 线程只接收具有相同 NAPI ID 的连接。但是必须谨慎处理系统中可能存在多个网卡的情况。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjDhhubh)}(h*为了启用忙轮询,有两种选择:h]h*为了启用忙轮询,有两种选择:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjDhhubhenumerated_list)}(hhh](h list_item)}(hX$``/proc/sys/net/core/busy_poll`` 可以设置为微秒数以在忙循环中等待事件。这是一个系统 范围的设置,将导致所有基于 epoll 的应用程序在调用 epoll_wait 时忙轮询。这可能不是理想 的情况,因为许多应用程序可能不需要忙轮询。 h]h)}(hX#``/proc/sys/net/core/busy_poll`` 可以设置为微秒数以在忙循环中等待事件。这是一个系统 范围的设置,将导致所有基于 epoll 的应用程序在调用 epoll_wait 时忙轮询。这可能不是理想 的情况,因为许多应用程序可能不需要忙轮询。h](j)}(h ``/proc/sys/net/core/busy_poll``h]h/proc/sys/net/core/busy_poll}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhX 可以设置为微秒数以在忙循环中等待事件。这是一个系统 范围的设置,将导致所有基于 epoll 的应用程序在调用 epoll_wait 时忙轮询。这可能不是理想 的情况,因为许多应用程序可能不需要忙轮询。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h使用最新内核的应用程序可以在 epoll 上下文的文件描述符上发出 ioctl 来设置(``EPIOCSPARAMS``) 或获取(``EPIOCGPARAMS``) ``struct epoll_params``,用户程序定义如下: h]h)}(h使用最新内核的应用程序可以在 epoll 上下文的文件描述符上发出 ioctl 来设置(``EPIOCSPARAMS``) 或获取(``EPIOCGPARAMS``) ``struct epoll_params``,用户程序定义如下:h](hf使用最新内核的应用程序可以在 epoll 上下文的文件描述符上发出 ioctl 来设置(}(hjhhhNhNubj)}(h``EPIOCSPARAMS``h]h EPIOCSPARAMS}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh ) 或获取(}(hjhhhNhNubj)}(h``EPIOCGPARAMS``h]h EPIOCGPARAMS}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh) }(hjhhhNhNubj)}(h``struct epoll_params``h]hstruct epoll_params}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh,用户程序定义如下:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]enumtypearabicprefixhsuffix.uh1jhjDhhhhhKubj)}(hstruct epoll_params { uint32_t busy_poll_usecs; uint16_t busy_poll_budget; uint8_t prefer_busy_poll; /* 将结构填充到 64 位的倍数 */ uint8_t __pad; };h]hstruct epoll_params { uint32_t busy_poll_usecs; uint16_t busy_poll_budget; uint8_t prefer_busy_poll; /* 将结构填充到 64 位的倍数 */ uint8_t __pad; };}hj% sbah}(h]h ]h"]h$]h&]hhjjjj}uh1jhhhKhjDhhubeh}(h]epollah ]h"]基于epoll的忙轮询ah$]h&]uh1jjhj7hhhhhKubjk)}(hhh](jp)}(h IRQ缓解h]h IRQ缓解}(hj? hhhNhNubah}(h]h ]h"]h$]h&]uh1johj< hhhhhMubh)}(h]虽然忙轮询旨在用于低延迟应用,但类似的机制可用于减少中断请求。h]h]虽然忙轮询旨在用于低延迟应用,但类似的机制可用于减少中断请求。}(hjM hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj< hhubh)}(h每秒高请求的应用程序(尤其是路由/转发应用程序和特别使用 AF_XDP 套接字的应用程序) 可能希望在处理完一个请求或一批数据包之前不被中断。h]h每秒高请求的应用程序(尤其是路由/转发应用程序和特别使用 AF_XDP 套接字的应用程序) 可能希望在处理完一个请求或一批数据包之前不被中断。}(hj[ hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj< hhubh)}(hXS此类应用程序可以向内核承诺会定期执行忙轮询操作,而驱动程序应将设备的中断请求永久屏蔽。 通过使用 ``SO_PREFER_BUSY_POLL`` 套接字选项可启用此模式。为避免系统出现异常,如果 在 ``gro_flush_timeout`` 时间内没有进行任何忙轮询调用,该承诺将被撤销。对于基于 epoll 的忙轮询应用程序,可以将 ``struct epoll_params`` 结构体中的 ``prefer_busy_poll`` 字段设置为 1,并使用 ``EPIOCSPARAMS`` 输入 / 输出控制(ioctl)操作来启用此模式。 更多详情请参阅上述章节。h](h此类应用程序可以向内核承诺会定期执行忙轮询操作,而驱动程序应将设备的中断请求永久屏蔽。 通过使用 }(hji hhhNhNubj)}(h``SO_PREFER_BUSY_POLL``h]hSO_PREFER_BUSY_POLL}(hjq hhhNhNubah}(h]h ]h"]h$]h&]uh1jhji ubhN 套接字选项可启用此模式。为避免系统出现异常,如果 在 }(hji hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhji ubh~ 时间内没有进行任何忙轮询调用,该承诺将被撤销。对于基于 epoll 的忙轮询应用程序,可以将 }(hji hhhNhNubj)}(h``struct epoll_params``h]hstruct epoll_params}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhji ubh 结构体中的 }(hji hhhNhNubj)}(h``prefer_busy_poll``h]hprefer_busy_poll}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhji ubh 字段设置为 1,并使用 }(hji hhhNhNubj)}(h``EPIOCSPARAMS``h]h EPIOCSPARAMS}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhji ubha 输入 / 输出控制(ioctl)操作来启用此模式。 更多详情请参阅上述章节。}(hji hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj< hhubh)}(hXNAPI 忙轮询的 budget 低于默认值(这符合正常忙轮询的低延迟意图)。减少中断请求的场景中 并非如此,因此 budget 可以通过 ``SO_BUSY_POLL_BUDGET`` 套接字选项进行调整。对于基于 epoll 的忙轮询应用程序,可以通过调整 ``struct epoll_params`` 中的 ``busy_poll_budget`` 字段为特定值,并使用 ``EPIOCSPARAMS`` ioctl 在特定 epoll 上下文中设置。更多详细信 息请参见上述部分。h](hNAPI 忙轮询的 budget 低于默认值(这符合正常忙轮询的低延迟意图)。减少中断请求的场景中 并非如此,因此 budget 可以通过 }(hj hhhNhNubj)}(h``SO_BUSY_POLL_BUDGET``h]hSO_BUSY_POLL_BUDGET}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh` 套接字选项进行调整。对于基于 epoll 的忙轮询应用程序,可以通过调整 }(hj hhhNhNubj)}(h``struct epoll_params``h]hstruct epoll_params}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 中的 }(hj hhhNhNubj)}(h``busy_poll_budget``h]hbusy_poll_budget}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 字段为特定值,并使用 }(hj hhhNhNubj)}(h``EPIOCSPARAMS``h]h EPIOCSPARAMS}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhW ioctl 在特定 epoll 上下文中设置。更多详细信 息请参见上述部分。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj< hhubh)}(hX需要注意的是,为 ``gro_flush_timeout`` 选择较大的值会延迟中断请求,以实现更好的批 量处理,但在系统未满载时会增加延迟。为 ``gro_flush_timeout`` 选择较小的值可能会因 设备中断请求和软中断处理而干扰尝试进行忙轮询的用户应用程序。应权衡这些因素后谨慎选择 该值。基于 epoll 的忙轮询应用程序可以通过为 ``maxevents`` 选择合适的值来减少用户 处理的干扰。h](h需要注意的是,为 }(hj' hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj/ hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj' ubh{ 选择较大的值会延迟中断请求,以实现更好的批 量处理,但在系统未满载时会增加延迟。为 }(hj' hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hjA hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj' ubh 选择较小的值可能会因 设备中断请求和软中断处理而干扰尝试进行忙轮询的用户应用程序。应权衡这些因素后谨慎选择 该值。基于 epoll 的忙轮询应用程序可以通过为 }(hj' hhhNhNubj)}(h ``maxevents``h]h maxevents}(hjS hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj' ubh5 选择合适的值来减少用户 处理的干扰。}(hj' hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj< hhubh)}(ha用户可能需要考虑使用另一种方法,IRQ 挂起,以帮助应对这些权衡问题。h]ha用户可能需要考虑使用另一种方法,IRQ 挂起,以帮助应对这些权衡问题。}(hjk hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj< hhubeh}(h]id10ah ]h"] irq缓解ah$]h&]uh1jjhj7hhhhhMubjk)}(hhh](jp)}(h IRQ挂起h]h IRQ挂起}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1johj hhhhhMubh)}(hdIRQ 挂起是一种机制,其中设备 IRQ 在 epoll 触发 NAPI 数据包处理期间被屏蔽。h]hdIRQ 挂起是一种机制,其中设备 IRQ 在 epoll 触发 NAPI 数据包处理期间被屏蔽。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM hj hhubh)}(hX只要应用程序对 epoll_wait 的调用成功获取事件,内核就会推迟 IRQ 挂起定时器。如果 在忙轮询期间没有获取任何事件(例如,因为网络流量减少),则会禁用IRQ挂起功能,并启 用上述减少中断请求的策略。h]hX只要应用程序对 epoll_wait 的调用成功获取事件,内核就会推迟 IRQ 挂起定时器。如果 在忙轮询期间没有获取任何事件(例如,因为网络流量减少),则会禁用IRQ挂起功能,并启 用上述减少中断请求的策略。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM"hj hhubh)}(hG这允许用户在 CPU 消耗和网络处理效率之间取得平衡。h]hG这允许用户在 CPU 消耗和网络处理效率之间取得平衡。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM&hj hhubh)}(h要使用此机制:h]h要使用此机制:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM(hj hhubh block_quote)}(hX1. 每个 NAPI 的配置参数 ``irq-suspend-timeout`` 应设置为应用程序可以挂起 IRQ 的最大时间(纳秒)。这通过 netlink 完成,如上所述。此超时时间作为一 种安全机制,如果应用程序停滞,将重新启动中断驱动程序的中断处理。此值应选择 为覆盖用户应用程序调用 epoll_wait 处理数据所需的时间,需注意的是,应用程 序可通过在调用 epoll_wait 时设置 ``max_events`` 来控制获取的数据量。 2. sysfs 参数或每个 NAPI 的配置参数 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs`` 可以设置为较低值。它们将用于在忙轮询未找到数据时延迟 IRQs。 3. 必须将 ``prefer_busy_poll`` 标志设置为 true。如前文所述,可使用 ``EPIOCSPARAMS`` ioctl操作来完成此设置。 4. 应用程序按照上述方式使用 epoll 触发 NAPI 数据包处理。 h]j)}(hhh](j)}(hX每个 NAPI 的配置参数 ``irq-suspend-timeout`` 应设置为应用程序可以挂起 IRQ 的最大时间(纳秒)。这通过 netlink 完成,如上所述。此超时时间作为一 种安全机制,如果应用程序停滞,将重新启动中断驱动程序的中断处理。此值应选择 为覆盖用户应用程序调用 epoll_wait 处理数据所需的时间,需注意的是,应用程 序可通过在调用 epoll_wait 时设置 ``max_events`` 来控制获取的数据量。 h]h)}(hX每个 NAPI 的配置参数 ``irq-suspend-timeout`` 应设置为应用程序可以挂起 IRQ 的最大时间(纳秒)。这通过 netlink 完成,如上所述。此超时时间作为一 种安全机制,如果应用程序停滞,将重新启动中断驱动程序的中断处理。此值应选择 为覆盖用户应用程序调用 epoll_wait 处理数据所需的时间,需注意的是,应用程 序可通过在调用 epoll_wait 时设置 ``max_events`` 来控制获取的数据量。h](h每个 NAPI 的配置参数 }(hj hhhNhNubj)}(h``irq-suspend-timeout``h]hirq-suspend-timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhX 应设置为应用程序可以挂起 IRQ 的最大时间(纳秒)。这通过 netlink 完成,如上所述。此超时时间作为一 种安全机制,如果应用程序停滞,将重新启动中断驱动程序的中断处理。此值应选择 为覆盖用户应用程序调用 epoll_wait 处理数据所需的时间,需注意的是,应用程 序可通过在调用 epoll_wait 时设置 }(hj hhhNhNubj)}(h``max_events``h]h max_events}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 来控制获取的数据量。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM*hj ubah}(h]h ]h"]h$]h&]uh1jhj ubj)}(hsysfs 参数或每个 NAPI 的配置参数 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs`` 可以设置为较低值。它们将用于在忙轮询未找到数据时延迟 IRQs。 h]h)}(hsysfs 参数或每个 NAPI 的配置参数 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs`` 可以设置为较低值。它们将用于在忙轮询未找到数据时延迟 IRQs。h](h+sysfs 参数或每个 NAPI 的配置参数 }(hj hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 和 }(hj hhhNhNubj)}(h``napi_defer_hard_irqs``h]hnapi_defer_hard_irqs}(hj- hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhW 可以设置为较低值。它们将用于在忙轮询未找到数据时延迟 IRQs。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM0hj ubah}(h]h ]h"]h$]h&]uh1jhj ubj)}(h必须将 ``prefer_busy_poll`` 标志设置为 true。如前文所述,可使用 ``EPIOCSPARAMS`` ioctl操作来完成此设置。 h]h)}(h必须将 ``prefer_busy_poll`` 标志设置为 true。如前文所述,可使用 ``EPIOCSPARAMS`` ioctl操作来完成此设置。h](h 必须将 }(hjO hhhNhNubj)}(h``prefer_busy_poll``h]hprefer_busy_poll}(hjW hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjO ubh4 标志设置为 true。如前文所述,可使用 }(hjO hhhNhNubj)}(h``EPIOCSPARAMS``h]h EPIOCSPARAMS}(hji hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjO ubh! ioctl操作来完成此设置。}(hjO hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM3hjK ubah}(h]h ]h"]h$]h&]uh1jhj ubj)}(hJ应用程序按照上述方式使用 epoll 触发 NAPI 数据包处理。 h]h)}(hI应用程序按照上述方式使用 epoll 触发 NAPI 数据包处理。h]hI应用程序按照上述方式使用 epoll 触发 NAPI 数据包处理。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM6hj ubah}(h]h ]h"]h$]h&]uh1jhj ubeh}(h]h ]h"]h$]h&]j j! j" hj# j$ uh1jhj ubah}(h]h ]h"]h$]h&]uh1j hhhM*hj hhubh)}(h如上所述,只要后续对 epoll_wait 的调用向用户空间返回事件,``irq-suspend-timeout`` 就会被推迟并且 IRQ 会被禁用。这允许应用程序在无干扰的情况下处理数据。h](hQ如上所述,只要后续对 epoll_wait 的调用向用户空间返回事件,}(hj hhhNhNubj)}(h``irq-suspend-timeout``h]hirq-suspend-timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhf 就会被推迟并且 IRQ 会被禁用。这允许应用程序在无干扰的情况下处理数据。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM8hj hhubh)}(h一旦 epoll_wait 的调用没有找到任何事件,IRQ 挂起会被自动禁用,并且 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs`` 缓解机制将开始起作用。h](h\一旦 epoll_wait 的调用没有找到任何事件,IRQ 挂起会被自动禁用,并且 }(hj hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 和 }(hj hhhNhNubj)}(h``napi_defer_hard_irqs``h]hnapi_defer_hard_irqs}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh" 缓解机制将开始起作用。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM;hj hhubh)}(h预期是 ``irq-suspend-timeout`` 的设置值会远大于 ``gro_flush_timeout``,因为 ``irq-suspend-timeout`` 应在一个用户空间处理周期内暂停中断请求。h](h 预期是 }(hj hhhNhNubj)}(h``irq-suspend-timeout``h]hirq-suspend-timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 的设置值会远大于 }(hj hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh ,因为 }(hj hhhNhNubj)}(h``irq-suspend-timeout``h]hirq-suspend-timeout}(hj) hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh= 应在一个用户空间处理周期内暂停中断请求。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM>hj hhubh)}(h虽然严格来说不必通过 ``napi_defer_hard_irqs`` 和 ``gro_flush_timeout`` 来执行 IRQ 挂起, 但强烈建议这样做。h](h虽然严格来说不必通过 }(hjA hhhNhNubj)}(h``napi_defer_hard_irqs``h]hnapi_defer_hard_irqs}(hjI hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjA ubh 和 }(hjA hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj[ hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjA ubh4 来执行 IRQ 挂起, 但强烈建议这样做。}(hjA hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMAhj hhubh)}(hX^中断请求挂起会使系统在轮询模式和由中断驱动的数据包传输模式之间切换。在网络繁忙期间,``irq-suspend-timeout`` 会覆盖 ``gro_flush_timeout``,使系统保持忙轮询状态,但是当 epoll 未发现任何事件时,``gro_flush_timeout`` 和 ``napi_defer_hard_irqs`` 的设置将决定下一步的操作。h](h~中断请求挂起会使系统在轮询模式和由中断驱动的数据包传输模式之间切换。在网络繁忙期间,}(hjs hhhNhNubj)}(h``irq-suspend-timeout``h]hirq-suspend-timeout}(hj{ hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjs ubh 会覆盖 }(hjs hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjs ubhO,使系统保持忙轮询状态,但是当 epoll 未发现任何事件时,}(hjs hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjs ubh 和 }(hjs hhhNhNubj)}(h``napi_defer_hard_irqs``h]hnapi_defer_hard_irqs}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjs ubh( 的设置将决定下一步的操作。}(hjs hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMDhj hhubh)}(h9有三种可能的网络处理和数据包交付循环:h]h9有三种可能的网络处理和数据包交付循环:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMHhj hhubj)}(hhh](j)}(h:硬中断 -> 软中断 -> NAPI 轮询;基本中断交付h]h)}(hj h]h:硬中断 -> 软中断 -> NAPI 轮询;基本中断交付}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMJhj ubah}(h]h ]h"]h$]h&]uh1jhj hhhhhNubj)}(h<定时器 -> 软中断 -> NAPI 轮询;延迟的 IRQ 处理h]h)}(hj h]h<定时器 -> 软中断 -> NAPI 轮询;延迟的 IRQ 处理}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMKhj ubah}(h]h ]h"]h$]h&]uh1jhj hhhhhNubj)}(h.epoll -> 忙轮询 -> NAPI 轮询;忙循环 h]h)}(h-epoll -> 忙轮询 -> NAPI 轮询;忙循环h]h-epoll -> 忙轮询 -> NAPI 轮询;忙循环}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMLhj ubah}(h]h ]h"]h$]h&]uh1jhj hhhhhNubeh}(h]h ]h"]h$]h&]j j! j" hj# )uh1jhj hhhhhMJubh)}(he循环 2 可以接管循环 1,如果设置了 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``。h](h0循环 2 可以接管循环 1,如果设置了 }(hj' hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj/ hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj' ubh 和 }(hj' hhhNhNubj)}(h``napi_defer_hard_irqs``h]hnapi_defer_hard_irqs}(hjA hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj' ubh。}(hj' hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMNhj hhubh)}(hu如果设置了 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``,循环 2 和 3 将互相“争夺”控制权。h](h如果设置了 }(hjY hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hja hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjY ubh 和 }(hjY hhhNhNubj)}(h``napi_defer_hard_irqs``h]hnapi_defer_hard_irqs}(hjs hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjY ubh3,循环 2 和 3 将互相“争夺”控制权。}(hjY hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMPhj hhubh)}(hw在繁忙时期,``irq-suspend-timeout`` 用作循环 2 的定时器,这基本上使网络处理倾向于循环 3。h](h在繁忙时期,}(hj hhhNhNubj)}(h``irq-suspend-timeout``h]hirq-suspend-timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhN 用作循环 2 的定时器,这基本上使网络处理倾向于循环 3。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMRhj hhubh)}(hi如果不设置 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``,循环 3 无法从循环 1 接管。h](h如果不设置 }(hj hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 和 }(hj hhhNhNubj)}(h``napi_defer_hard_irqs``h]hnapi_defer_hard_irqs}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh',循环 3 无法从循环 1 接管。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMThj hhubh)}(h因此,建议设置 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``,因为若不这样做,设置 ``irq-suspend-timeout`` 可能不会有明显效果。h](h因此,建议设置 }(hj hhhNhNubj)}(h``gro_flush_timeout``h]hgro_flush_timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uth1jhj ubh 和 }(hj hhhNhNubj)}(h``napi_defer_hard_irqs``h]hnapi_defer_hard_irqs}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh",因为若不这样做,设置 }(hj hhhNhNubj)}(h``irq-suspend-timeout``h]hirq-suspend-timeout}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh 可能不会有明显效果。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMVhj hhubj>)}(h.. _threaded_zh_CN:h]h}(h]h ]h"]h$]h&]jthreaded-zh-cnuh1j=hMdhj hhhhubeh}(h]id11ah ]h"] irq挂起ah$]h&]uh1jjhj7hhhhhMubjk)}(hhh](jp)}(h 线程化NAPIh]h 线程化NAPI}(hj7hhhNhNubah}(h]h ]h"]h$]h&]uh1johj4hhhhhM\ubh)}(hX7线程化 NAPI 是一种操作模式,它使用专用的内核线程而非软件中断上下文来进行 NAPI 处理。这种配置 是针对每个网络设备的,并且会影响该设备的所有 NAPI 实例。每个 NAPI 实例将生成一个单独的线程 (称为 ``napi/${ifc-name}-${napi-id}`` )。h](hX线程化 NAPI 是一种操作模式,它使用专用的内核线程而非软件中断上下文来进行 NAPI 处理。这种配置 是针对每个网络设备的,并且会影响该设备的所有 NAPI 实例。每个 NAPI 实例将生成一个单独的线程 (称为 }(hjEhhhNhNubj)}(h``napi/${ifc-name}-${napi-id}``h]hnapi/${ifc-name}-${napi-id}}(hjMhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjEubh )。}(hjEhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM^hj4hhubh)}(hX)建议将每个内核线程固定到单个 CPU 上,这个 CPU 与处理中断的 CPU 相同。请注意,中断请求(IRQ) 和 NAPI 实例之间的映射关系可能并不简单(并且取决于驱动程序)。NAPI 实例 ID 的分配顺序将与内 核线程的进程 ID 顺序相反。h]hX)建议将每个内核线程固定到单个 CPU 上,这个 CPU 与处理中断的 CPU 相同。请注意,中断请求(IRQ) 和 NAPI 实例之间的映射关系可能并不简单(并且取决于驱动程序)。NAPI 实例 ID 的分配顺序将与内 核线程的进程 ID 顺序相反。}(hjehhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMbhj4hhubh)}(ho线程化 NAPI 是通过向网络设备的 sysfs 目录中的 ``threaded`` 文件写入 0 或 1 来控制的。h](h>线程化 NAPI 是通过向网络设备的 sysfs 目录中的 }(hjshhhNhNubj)}(h ``threaded``h]hthreaded}(hj{hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjsubh% 文件写入 0 或 1 来控制的。}(hjshhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMfhj4hhubhrubric)}(h脚注h]h脚注}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj4hhhhhMiubhfootnote)}(h0NAPI 最初在 2.4 Linux 中被称为 New API。h](hlabel)}(hhh]h1}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhNhNubh)}(hjh]h0NAPI 最初在 2.4 Linux 中被称为 New API。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMjhjubeh}(h]jah ]h"]1ah$]h&]jajKjjuh1jhhhMjhj4hhubeh}(h](j+id12eh ]h"]( 线程化napithreaded_zh_cneh$]h&]uh1jjhj7hhhhhM\j}jj!sj}j+j!subeh}(h]id7ah ]h"] 用户apiah$]h&]uh1jjhjlhhhhhKubeh}(h]napiah ]h"]napiah$]h&]uh1jjhhhhhhhK ubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(joN generatorN datestampN source_linkN source_urlN toc_backlinksentryfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerj error_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourcehnj _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}refids}(jI]j?aj]jaj]jaj+]j!aj]jaunameids}(jjj4j1jjIjjj,j)jjjjjjjjj,j)jjjjj?jj>j;j9 j6 j~ j{ j1j.jj+jjjju nametypes}(jj4jjj,jjjjj,jjj?j>j9 j~ j1jjjuh}(jjljjj1jjIjJjjJj)jjj/jjjjjjj)jjj7jjjjj;jj6 jDj{ j< j.j j+j4jj4jju footnote_refs} citation_refs} autofootnotes]jaautofootnote_refs]jasymbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}jK sRparse_messages]transform_messages](hsystem_message)}(hhh]h)}(hhh]h4Hyperlink target "drv-ctrl-zh-cn" is not referenced.}hjxsbah}(h]h ]h"]h$]h&]uh1hhjuubah}(h]h ]h"]h$]h&]levelKtypeINFOsourcehnjlineK*uh1jsubjt)}(hhh]h)}(hhh]h5Hyperlink target "drv-sched-zh-cn" is not referenced.}hjsbah}(h]h ]h"]h$]h&]uh1hhjubah}(h]h ]h"]h$]h&]levelKtypejsourcehnjlineKiuh1jsubjt)}(hhh]h)}(hhh]h0Hyperlink target "poll-zh-cn" is not referenced.}hjsbah}(h]h ]h"]h$]h&]uh1hhjubah}(h]h ]h"]h$]h&]levelKtypejsourcehnjlineKuh1jsubjt)}(hhh]h)}(hhh]h4Hyperlink target "threaded-zh-cn" is not referenced.}hjsbah}(h]h ]h"]h$]h&]uh1hhjubah}(h]h ]h"]h$]h&]levelKtypejsourcehnjlineMduh1jsube transformerN include_log]4Documentation/translations/zh_CN/networking/napi.rst(NNNNta decorationNhhub.