sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/core-api/this_cpu_opsmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget)/translations/zh_TW/core-api/this_cpu_opsmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget)/translations/it_IT/core-api/this_cpu_opsmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget)/translations/ja_JP/core-api/this_cpu_opsmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget)/translations/ko_KR/core-api/this_cpu_opsmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget)/translations/sp_SP/core-api/this_cpu_opsmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhcomment)}(h!SPDX-License-Identifier: GPL-2.0+h]h!SPDX-License-Identifier: GPL-2.0+}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhV/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/core-api/this_cpu_ops.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/core-api/this_cpu_ops.rst h]h)}(h'Documentation/core-api/this_cpu_ops.rsth]h'Documentation/core-api/this_cpu_ops.rst}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h翻译h]h翻译}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhKubh)}(h/周彬彬 Binbin Zhou h]h)}(h.周彬彬 Binbin Zhou h](h周彬彬 Binbin Zhou <}(hj hhhNhNubh reference)}(hzhoubinbin@loongson.cnh]hzhoubinbin@loongson.cn}(hj*hhhNhNubah}(h]h ]h"]h$]h&]refurimailto:zhoubinbin@loongson.cnuh1j(hj ubh>}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hjubah}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h校译h]h校译}(hjShhhNhNubah}(h]h ]h"]h$]h&]uh1hhjPhhhKubh)}(h*吴想成 Wu Xiangcheng h]h)}(h)吴想成 Wu Xiangcheng h](h吴想成 Wu Xiangcheng <}(hjehhhNhNubj))}(hbobwxc@email.cnh]hbobwxc@email.cn}(hjmhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:bobwxc@email.cnuh1j(hjeubh>}(hjehhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hjaubah}(h]h ]h"]h$]h&]uh1hhjPubeh}(h]h ]h"]h$]h&]uh1hhhhK hhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(hthis_cpu操作h]hthis_cpu操作}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(hhh](h)}(hhh](h)}(h作者h]h作者}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubh)}(h"Christoph Lameter, 2014年8月4日h]h)}(hjh]h"Christoph Lameter, 2014年8月4日}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hhh](h)}(h作者h]h作者}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubh)}(hPranith Kumar, 2014年8月2日 h]h)}(hPranith Kumar, 2014年8月2日h]hPranith Kumar, 2014年8月2日}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]h ]h"]h$]h&]uh1hhjhhhhhKubh)}(hthis_cpu操作是一种优化访问与当前执行处理器相关的每CPU变量的方法。这是通过使用段寄 存器(或专用寄存器,cpu在其中永久存储特定处理器的每CPU区域的起始)来完成的。h]hthis_cpu操作是一种优化访问与当前执行处理器相关的每CPU变量的方法。这是通过使用段寄 存器(或专用寄存器,cpu在其中永久存储特定处理器的每CPU区域的起始)来完成的。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hthis_cpu操作将每CPU变量的偏移量添加到处理器特定的每CPU基址上,并将该操作编码到对 每CPU变量进行操作的指令中。h]hthis_cpu操作将每CPU变量的偏移量添加到处理器特定的每CPU基址上,并将该操作编码到对 每CPU变量进行操作的指令中。}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h这意味着在偏移量的计算和对数据的操作之间不存在原子性问题。因此,没有必要禁用抢占 或中断来确保处理器在计算地址和数据操作之间不被改变。h]h这意味着在偏移量的计算和对数据的操作之间不存在原子性问题。因此,没有必要禁用抢占 或中断来确保处理器在计算地址和数据操作之间不被改变。}(hj0hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hXK读取-修改-写入操作特别值得关注。通常处理器具有特殊的低延迟指令,可以在没有典型同 步开销的情况下运行,但仍提供某种宽松的原子性保证。例如,x86可以执行RMW(读取, 修改,写入)指令,如同inc/dec/cmpxchg,而无需锁前缀和相关的延迟损失。h]hXK读取-修改-写入操作特别值得关注。通常处理器具有特殊的低延迟指令,可以在没有典型同 步开销的情况下运行,但仍提供某种宽松的原子性保证。例如,x86可以执行RMW(读取, 修改,写入)指令,如同inc/dec/cmpxchg,而无需锁前缀和相关的延迟损失。}(hj>hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX对没有锁前缀的变量的访问是不同步的,也不需要同步,因为我们处理的是当前执行的处理 器所特有的每CPU数据。只有当前的处理器可以访问该变量,因此系统中的其他处理器不存在 并发性问题。h]hX对没有锁前缀的变量的访问是不同步的,也不需要同步,因为我们处理的是当前执行的处理 器所特有的每CPU数据。只有当前的处理器可以访问该变量,因此系统中的其他处理器不存在 并发性问题。}(hjLhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK#hjhhubh)}(h请注意,远程处理器对每CPU区域的访问是特殊情况,可能会影响通过 ``this_cpu_*`` 的本 地RMW操作的性能和正确性(远程写操作)。h](h[请注意,远程处理器对每CPU区域的访问是特殊情况,可能会影响通过 }(hjZhhhNhNubhliteral)}(h``this_cpu_*``h]h this_cpu_*}(hjdhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjZubhA 的本 地RMW操作的性能和正确性(远程写操作)。}(hjZhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK'hjhhubh)}(h8this_cpu操作的主要用途是优化计数器操作。h]h8this_cpu操作的主要用途是优化计数器操作。}(hj|hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK*hjhhubh)}(hu定义了以下具有隐含抢占保护的this_cpu()操作。可以使用这些操作而不用担心抢占和中断::h]ht定义了以下具有隐含抢占保护的this_cpu()操作。可以使用这些操作而不用担心抢占和中断:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK,hjhhubh literal_block)}(hXthis_cpu_read(pcp) this_cpu_write(pcp, val) this_cpu_add(pcp, val) this_cpu_and(pcp, val) this_cpu_or(pcp, val) this_cpu_add_return(pcp, val) this_cpu_xchg(pcp, nval) this_cpu_cmpxchg(pcp, oval, nval) this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) this_cpu_sub(pcp, val) this_cpu_inc(pcp) this_cpu_dec(pcp) this_cpu_sub_return(pcp, val) this_cpu_inc_return(pcp) this_cpu_dec_return(pcp)h]hXthis_cpu_read(pcp) this_cpu_write(pcp, val) this_cpu_add(pcp, val) this_cpu_and(pcp, val) this_cpu_or(pcp, val) this_cpu_add_return(pcp, val) this_cpu_xchg(pcp, nval) this_cpu_cmpxchg(pcp, oval, nval) this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) this_cpu_sub(pcp, val) this_cpu_inc(pcp) this_cpu_dec(pcp) this_cpu_sub_return(pcp, val) this_cpu_inc_return(pcp) this_cpu_dec_return(pcp)}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhK.hjhhubj)}(hhh](j)}(hthis_cpu操作的内部工作h]hthis_cpu操作的内部工作}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhK@ubh)}(hX在x86上,fs:或gs:段寄存器包含每CPU区域的基址。这样就可以简单地使用段覆盖,将每CPU 相对地址重定位到处理器适当的每CPU区域。所以对每CPU基址的重定位是通过段寄存器前缀 在指令中编码完成的。h]hX在x86上,fs:或gs:段寄存器包含每CPU区域的基址。这样就可以简单地使用段覆盖,将每CPU 相对地址重定位到处理器适当的每CPU区域。所以对每CPU基址的重定位是通过段寄存器前缀 在指令中编码完成的。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKBhjhhubh)}(h例如::h]h例如:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKFhjhhubj)}(h5DEFINE_PER_CPU(int, x); int z; z = this_cpu_read(x);h]h5DEFINE_PER_CPU(int, x); int z; z = this_cpu_read(x);}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKHhjhhubh)}(h产生的单指令为::h]h产生的单指令为:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKMhjhhubj)}(hmov ax, gs:[x]h]hmov ax, gs:[x]}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKOhjhhubh)}(h而不是像每CPU操作那样,先是一系列的地址计算,然后从该地址获取。在this_cpu_ops之前, 这样的序列还需要先禁用/启用抢占功能,以防止内核在计算过程中将线程移动到不同的处理 器上。h]h而不是像每CPU操作那样,先是一系列的地址计算,然后从该地址获取。在this_cpu_ops之前, 这样的序列还需要先禁用/启用抢占功能,以防止内核在计算过程中将线程移动到不同的处理 器上。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKQhjhhubh)}(h请思考下面this_cpu操作::h]h请思考下面this_cpu操作:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKUhjhhubj)}(hthis_cpu_inc(x)h]hthis_cpu_inc(x)}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKWhjhhubh)}(h2这将产生如下单指令(无锁前缀!)::h]h1这将产生如下单指令(无锁前缀!):}(hj)hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKYhjhhubj)}(h inc gs:[x]h]h inc gs:[x]}hj7sbah}(h]h ]h"]h$]h&]hhuh1jhhhK[hjhhubh)}(hD而不是在没有段寄存器的情况下所需要的以下操作::h]hC而不是在没有段寄存器的情况下所需要的以下操作:}(hjEhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK]hjhhubj)}(hOint *y; int cpu; cpu = get_cpu(); y = per_cpu_ptr(&x, cpu); (*y)++; put_cpu();h]hOint *y; int cpu; cpu = get_cpu(); y = per_cpu_ptr(&x, cpu); (*y)++; put_cpu();}hjSsbah}(h]h ]h"]h$]h&]hhuh1jhhhK_hjhhubh)}(hX请注意,这些操作只能用于为特定处理器保留的每CPU数据。如果不在上下文代码中禁用抢占, ``this_cpu_inc()`` 将仅保证每CPU的某一个计数器被正确地递增,但不能保证操作系统不 会在this_cpu指令执行的前后直接移动该进程。一般来说,这意味着每个处理器的单个计数 器的值是没有意义的。所有每CPU计数器的总和才是唯一有意义的值。h](h|请注意,这些操作只能用于为特定处理器保留的每CPU数据。如果不在上下文代码中禁用抢占, }(hjahhhNhNubjc)}(h``this_cpu_inc()``h]hthis_cpu_inc()}(hjihhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjaubhX+ 将仅保证每CPU的某一个计数器被正确地递增,但不能保证操作系统不 会在this_cpu指令执行的前后直接移动该进程。一般来说,这意味着每个处理器的单个计数 器的值是没有意义的。所有每CPU计数器的总和才是唯一有意义的值。}(hjahhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKghjhhubh)}(hXC每CPU变量的使用是出于性能的考虑。如果多个处理器同时处理相同的代码路径,可以避免缓 存行跳转。每个处理器都有自己的每CPU变量,因此不会发生并发缓存行更新。为这种优化必 须付出的代价是,当需要计数器的值时要将每CPU计数器相加。h]hXC每CPU变量的使用是出于性能的考虑。如果多个处理器同时处理相同的代码路径,可以避免缓 存行跳转。每个处理器都有自己的每CPU变量,因此不会发生并发缓存行更新。为这种优化必 须付出的代价是,当需要计数器的值时要将每CPU计数器相加。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKlhjhhubeh}(h]id1ah ]h"]this_cpu操作的内部工作ah$]h&]uh1jhjhhhhhK@ubj)}(hhh](j)}(h特殊的操作h]h特殊的操作}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKrubj)}(hy = this_cpu_ptr(&x)h]hy = this_cpu_ptr(&x)}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKvhjhhubh)}(hX3使用每CPU变量的偏移量(&x!),并返回属于当前执行处理器的每CPU变量的地址。 ``this_cpu_ptr`` 避免了通用 ``get_cpu``/``put_cpu`` 序列所需的多个步骤。没有可用 的处理器编号。相反,本地每CPU区域的偏移量只是简单地添加到每CPU偏移量上。h](hf使用每CPU变量的偏移量(&x!),并返回属于当前执行处理器的每CPU变量的地址。 }(hjhhhNhNubjc)}(h``this_cpu_ptr``h]h this_cpu_ptr}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh 避免了通用 }(hjhhhNhNubjc)}(h ``get_cpu``h]hget_cpu}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh/}(hjhhhNhNubjc)}(h ``put_cpu``h]hput_cpu}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh 序列所需的多个步骤。没有可用 的处理器编号。相反,本地每CPU区域的偏移量只是简单地添加到每CPU偏移量上。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKxhjhhubh)}(hX请注意,这个操作通常是在抢占被禁用后再在代码段中使用。然后该指针用来访问临界区中 的本地每CPU数据。当重新启用抢占时,此指针通常不再有用,因为它可能不再指向当前处理 器的每CPU数据。h]hX请注意,这个操作通常是在抢占被禁用后再在代码段中使用。然后该指针用来访问临界区中 的本地每CPU数据。当重新启用抢占时,此指针通常不再有用,因为它可能不再指向当前处理 器的每CPU数据。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK|hjhhubeh}(h]id2ah ]h"]特殊的操作ah$]h&]uh1jhjhhhhhKrubj)}(hhh](j)}(h每CPU变量和偏移量h]h每CPU变量和偏移量}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(h每CPU变量相对于每CPU区域的起始点是有偏移的。它们没有地址,尽管代码里看起来像有一 样。不能直接对偏移量解引用,必须用处理器每CPU区域基指针加上偏移量,以构成有效地址。h]h每CPU变量相对于每CPU区域的起始点是有偏移的。它们没有地址,尽管代码里看起来像有一 样。不能直接对偏移量解引用,必须用处理器每CPU区域基指针加上偏移量,以构成有效地址。}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h因此,在每CPU操作的上下文之外使用x或&x是无效的,这种行为通常会被当作一个空指针的 解引用来处理。h]h因此,在每CPU操作的上下文之外使用x或&x是无效的,这种行为通常会被当作一个空指针的 解引用来处理。}(hj/hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hDEFINE_PER_CPU(int, x);h]hDEFINE_PER_CPU(int, x);}hj=sbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjhhubh)}(h在每CPU操作的上下文中,上面表达式说明x是一个每CPU变量。大多数this_cpu操作都需要一 个cpu变量。h]h在每CPU操作的上下文中,上面表达式说明x是一个每CPU变量。大多数this_cpu操作都需要一 个cpu变量。}(hjKhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hint __percpu *p = &x;h]hint __percpu *p = &x;}hjYsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjhhubh)}(h{&x和p是每CPU变量的偏移量。 ``this_cpu_ptr()`` 使用每CPU变量的偏移量,这让它看起来 有点奇怪。h](h%&x和p是每CPU变量的偏移量。 }(hjghhhNhNubjc)}(h``this_cpu_ptr()``h]hthis_cpu_ptr()}(hjohhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjgubhD 使用每CPU变量的偏移量,这让它看起来 有点奇怪。}(hjghhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]cpuah ]h"]每cpu变量和偏移量ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(h每CPU结构体字段的操作h]h每CPU结构体字段的操作}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(h#假设我们有一个每CPU结构::h]h"假设我们有一个每CPU结构:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hm); z = this_cpu_inc_return(ps->n);h]hRstruct s __percpu *ps = &p; this_cpu_dec(ps->m); z = this_cpu_inc_return(ps->n);}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjhhubh)}(hx如果我们后面不使用 ``this_cpu ops`` 来操作字段,则指针的计算可能需要使用 ``this_cpu_ptr()``::h](h如果我们后面不使用 }(hjhhhNhNubjc)}(h``this_cpu ops``h]h this_cpu ops}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh8 来操作字段,则指针的计算可能需要使用 }(hjhhhNhNubjc)}(h``this_cpu_ptr()``h]hthis_cpu_ptr()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(h=struct s *pp; pp = this_cpu_ptr(&p); pp->m--; z = pp->n++;h]h=struct s *pp; pp = this_cpu_ptr(&p); pp->m--; z = pp->n++;}hj&sbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjhhubeh}(h]id3ah ]h"]每cpu结构体字段的操作ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(hthis_cpu ops的变体h]hthis_cpu ops的变体}(hj?hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj<hhhhhKubh)}(hXthis_cpu的操作是中断安全的。一些架构不支持这些每CPU的本地操作。在这种情况下,该操 作必须被禁用中断的代码所取代,然后做那些保证是原子的操作,再重新启用中断。当然这 样做是很昂贵的。如果有其他原因导致调度器不能改变我们正在执行的处理器,那么就没有 理由禁用中断了。为此,我们提供了以下__this_cpu操作。h]hXthis_cpu的操作是中断安全的。一些架构不支持这些每CPU的本地操作。在这种情况下,该操 作必须被禁用中断的代码所取代,然后做那些保证是原子的操作,再重新启用中断。当然这 样做是很昂贵的。如果有其他原因导致调度器不能改变我们正在执行的处理器,那么就没有 理由禁用中断了。为此,我们提供了以下__this_cpu操作。}(hjMhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj<hhubh)}(hX这些操作不能保证并发中断或抢占。如果在中断上下文中不使用每CPU变量并且调度程序无法 抢占,那么它们是安全的。如果在操作进行时仍有中断发生,并且中断也修改了变量,则无 法保证RMW操作是安全的::h]hX这些操作不能保证并发中断或抢占。如果在中断上下文中不使用每CPU变量并且调度程序无法 抢占,那么它们是安全的。如果在操作进行时仍有中断发生,并且中断也修改了变量,则无 法保证RMW操作是安全的:}(hj[hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj<hhubj)}(hX__this_cpu_read(pcp) __this_cpu_write(pcp, val) __this_cpu_add(pcp, val) __this_cpu_and(pcp, val) __this_cpu_or(pcp, val) __this_cpu_add_return(pcp, val) __this_cpu_xchg(pcp, nval) __this_cpu_cmpxchg(pcp, oval, nval) __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) __this_cpu_sub(pcp, val) __this_cpu_inc(pcp) __this_cpu_dec(pcp) __this_cpu_sub_return(pcp, val) __this_cpu_inc_return(pcp) __this_cpu_dec_return(pcp)h]hX__this_cpu_read(pcp) __this_cpu_write(pcp, val) __this_cpu_add(pcp, val) __this_cpu_and(pcp, val) __this_cpu_or(pcp, val) __this_cpu_add_return(pcp, val) __this_cpu_xchg(pcp, nval) __this_cpu_cmpxchg(pcp, oval, nval) __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) __this_cpu_sub(pcp, val) __this_cpu_inc(pcp) __this_cpu_dec(pcp) __this_cpu_sub_return(pcp, val) __this_cpu_inc_return(pcp) __this_cpu_dec_return(pcp)}hjisbah}(h]h ]h"]h$]h&]hhuh1jhhhKhj<hhubh)}(h将增加x,并且不会回退到在无法通过地址重定位和同一指令中的读取-修改-写入操作实现原 子性的平台上禁用中断的代码。h]h将增加x,并且不会回退到在无法通过地址重定位和同一指令中的读取-修改-写入操作实现原 子性的平台上禁用中断的代码。}(hjwhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj<hhubeh}(h] this-cpu-opsah ]h"]this_cpu ops的变体ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(h0&this_cpu_ptr(pp)->n 对比 this_cpu_ptr(&pp->n)h]h0&this_cpu_ptr(pp)->n 对比 this_cpu_ptr(&pp->n)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(h第一个操作使用偏移量并形成一个地址,然后再加上n字段的偏移量。这可能会导致编译器产 生两条加法指令。h]h第一个操作使用偏移量并形成一个地址,然后再加上n字段的偏移量。这可能会导致编译器产 生两条加法指令。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h第二个操作先加上两个偏移量,然后进行重定位。恕我直言,第二种形式看起来更干净,而 且更容易与 ``()`` 结合。第二种形式也与 ``this_cpu_read()`` 和大家的使用方式一致。h](h第二个操作先加上两个偏移量,然后进行重定位。恕我直言,第二种形式看起来更干净,而 且更容易与 }(hjhhhNhNubjc)}(h``()``h]h()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh 结合。第二种形式也与 }(hjhhhNhNubjc)}(h``this_cpu_read()``h]hthis_cpu_read()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh" 和大家的使用方式一致。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]#this-cpu-ptr-pp-n-this-cpu-ptr-pp-nah ]h"]0&this_cpu_ptr(pp)->n 对比 this_cpu_ptr(&pp->n)ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(h远程访问每CPU数据h]h远程访问每CPU数据}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(h每CPU数据结构被设计为由一个CPU独占使用。如果您按预期使用变量,则 ``this_cpu_ops()`` 保证是 ``原子的`` ,因为没有其他CPU可以访问这些数据结构。h](h^每CPU数据结构被设计为由一个CPU独占使用。如果您按预期使用变量,则 }(hjhhhNhNubjc)}(h``this_cpu_ops()``h]hthis_cpu_ops()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh 保证是 }(hjhhhNhNubjc)}(h ``原子的``h]h 原子的}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh: ,因为没有其他CPU可以访问这些数据结构。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX$在某些特殊情况下,您可能需要远程访问每CPU数据结构。通常情况下,进行远程读访问是安 全的,这经常是为了统计计数器值。远程写访问可能会出现问题,因为this_cpu操作没有锁 语义。远程写可能会干扰this_cpu RMW操作。h]hX$在某些特殊情况下,您可能需要远程访问每CPU数据结构。通常情况下,进行远程读访问是安 全的,这经常是为了统计计数器值。远程写访问可能会出现问题,因为this_cpu操作没有锁 语义。远程写可能会干扰this_cpu RMW操作。}(hj)hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h除非绝对必要,否则强烈建议不要对每CPU数据结构进行远程写访问。请考虑使用IPI来唤醒 远程CPU,并对其每CPU区域进行更新。h]h除非绝对必要,否则强烈建议不要对每CPU数据结构进行远程写访问。请考虑使用IPI来唤醒 远程CPU,并对其每CPU区域进行更新。}(hj7hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hK要远程访问每CPU数据结构,通常使用 ``per_cpu_ptr()`` 函数::h](h1要远程访问每CPU数据结构,通常使用 }(hjEhhhNhNubjc)}(h``per_cpu_ptr()``h]h per_cpu_ptr()}(hjMhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjEubh 函数:}(hjEhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hODEFINE_PER_CPU(struct data, datap); struct data *p = per_cpu_ptr(&datap, cpu);h]hODEFINE_PER_CPU(struct data, datap); struct data *p = per_cpu_ptr(&datap, cpu);}hjesbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjhhubh)}(h?这清楚地表明,我们正准备远程访问每CPU区域。h]h?这清楚地表明,我们正准备远程访问每CPU区域。}(hjshhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hC您还可以执行以下操作以将datap偏移量转换为地址::h]hB您还可以执行以下操作以将datap偏移量转换为地址:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj)}(h&struct data *p = this_cpu_ptr(&datap);h]h&struct data *p = this_cpu_ptr(&datap);}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhMhjhhubh)}(h`但是,将通过this_cpu_ptr计算的指针传递给其他cpu是不寻常的,应该避免。h]h`但是,将通过this_cpu_ptr计算的指针传递给其他cpu是不寻常的,应该避免。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h远程访问通常只用于读取另一个cpu的每CPU数据状态。由于this_cpu操作宽松的同步要求, 写访问可能会导致奇特的问题。h]h远程访问通常只用于读取另一个cpu的每CPU数据状态。由于this_cpu操作宽松的同步要求, 写访问可能会导致奇特的问题。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h下面的情况说明了写入操作的一些问题,由于两个每CPU变量共享一个缓存行,但宽松的同步 仅应用于更新缓存行的一个进程。h]h下面的情况说明了写入操作的一些问题,由于两个每CPU变量共享一个缓存行,但宽松的同步 仅应用于更新缓存行的一个进程。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM hjhhubh)}(h考虑以下示例::h]h考虑以下示例:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM hjhhubj)}(h_struct test { atomic_t a; int b; }; DEFINE_PER_CPU(struct test, onecacheline);h]h_struct test { atomic_t a; int b; }; DEFINE_PER_CPU(struct test, onecacheline);}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhMhjhhubh)}(hXs如果一个处理器远程更新字段 ``a`` ,而本地处理器将使用this_cpu ops来更新字段 ``b`` , 会发生什么情况,这一点值得注意。应避免在同一缓存行内同时访问数据。此外,可能还需 要进行代价高昂的同步。在这种情况下,通常建议使用IPI,而不是远程写入另一个处理器的 每CPU区域。h](h(如果一个处理器远程更新字段 }(hjhhhNhNubjc)}(h``a``h]ha}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubh; ,而本地处理器将使用this_cpu ops来更新字段 }(hjhhhNhNubjc)}(h``b``h]hb}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jbhjubhX , 会发生什么情况,这一点值得注意。应避免在同一缓存行内同时访问数据。此外,可能还需 要进行代价高昂的同步。在这种情况下,通常建议使用IPI,而不是远程写入另一个处理器的 每CPU区域。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h即使在远程写很少的情况下,请记住远程写将从最有可能访问它的处理器中逐出缓存行。如 果处理器唤醒时发现每CPU区域缺少本地缓存行,其性能和唤醒时间将受到影响。h]h即使在远程写很少的情况下,请记住远程写将从最有可能访问它的处理器中逐出缓存行。如 果处理器唤醒时发现每CPU区域缺少本地缓存行,其性能和唤醒时间将受到影响。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id4ah ]h"]远程访问每cpu数据ah$]h&]uh1jhjhhhhhKubeh}(h]this-cpuah ]h"]this_cpu操作ah$]h&]uh1jhhhhhhhKubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(jN 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_handlerjVerror_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourceh _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}nameids}(j0j-jjj j jjj9j6jjjjj(j%u nametypes}(j0jj jj9jjj(uh}(j-jjjj jjjj6jjj<jjj%ju footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}jdKsRparse_messages]transform_messages] transformerN include_log]:Documentation/translations/zh_CN/core-api/this_cpu_ops.rst(NNNNta decorationNhhub.