sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/core-api/circular-buffersmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget-/translations/zh_TW/core-api/circular-buffersmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget-/translations/it_IT/core-api/circular-buffersmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget-/translations/ja_JP/core-api/circular-buffersmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget-/translations/ko_KR/core-api/circular-buffersmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget-/translations/sp_SP/core-api/circular-buffersmodnameN 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:spacepreserveuh1hhhhhhZ/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/core-api/circular-buffers.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/circular-buffers.rst h]h)}(h+Documentation/core-api/circular-buffers.rsth]h+Documentation/core-api/circular-buffers.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}司延腾 Yanteng Si 吴想成 Wu Xiangcheng 时奎亮 Alex Shi h]h)}(h|司延腾 Yanteng Si 吴想成 Wu Xiangcheng 时奎亮 Alex Shi h](h司延腾 Yanteng Si <}(hjehhhNhNubj))}(hsiyanteng@loongson.cnh]hsiyanteng@loongson.cn}(hjmhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:siyanteng@loongson.cnuh1j(hjeubh> 吴想成 Wu Xiangcheng <}(hjehhhNhNubj))}(hbobwxc@email.cnh]hbobwxc@email.cn}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:bobwxc@email.cnuh1j(hjeubh> 时奎亮 Alex Shi <}(hjehhhNhNubj))}(halexs@kernel.orgh]halexs@kernel.org}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:alexs@kernel.orguh1j(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)}(h环形缓冲区h]h环形缓冲区}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(hhh](h)}(hhh](h)}(h作者h]h作者}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubh)}(h#David Howells h]h)}(hjh](hDavid Howells <}(hjhhhNhNubj))}(hdhowells@redhat.comh]hdhowells@redhat.com}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:dhowells@redhat.comuh1j(hjubh>}(hjhhhNhNubeh}(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)}(h*Paul E. McKenney h]h)}(h(Paul E. McKenney h](hPaul E. McKenney <}(hj0hhhNhNubj))}(hpaulmck@linux.ibm.comh]hpaulmck@linux.ibm.com}(hj8hhhNhNubah}(h]h ]h"]h$]h&]refurimailto:paulmck@linux.ibm.comuh1j(hj0ubh>}(hj0hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj,ubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]h ]h"]h$]h&]uh1hhjhhhhhKubh)}(hWLinux 提供了许多可用于实现循环缓冲的特性。有两组这样的特性:h]hWLinux 提供了许多可用于实现循环缓冲的特性。有两组这样的特性:}(hjdhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh block_quote)}(h(1) 用于确定2次方大小的缓冲区信息的便利函数。 (2) 可以代替缓冲区中对象的生产者和消费者共享锁的内存屏障。 h]henumerated_list)}(hhh](h list_item)}(h>用于确定2次方大小的缓冲区信息的便利函数。 h]h)}(h=用于确定2次方大小的缓冲区信息的便利函数。h]h=用于确定2次方大小的缓冲区信息的便利函数。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1j}hjzubj~)}(hR可以代替缓冲区中对象的生产者和消费者共享锁的内存屏障。 h]h)}(hQ可以代替缓冲区中对象的生产者和消费者共享锁的内存屏障。h]hQ可以代替缓冲区中对象的生产者和消费者共享锁的内存屏障。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1j}hjzubeh}(h]h ]h"]h$]h&]enumtypearabicprefix(suffix)uh1jxhjtubah}(h]h ]h"]h$]h&]uh1jrhhhKhjhhubh)}(h如下所述,要使用这些设施,只需要一个生产者和一个消费者。可以通过序列化来处理多个 生产者,并通过序列化来处理多个消费者。h]h如下所述,要使用这些设施,只需要一个生产者和一个消费者。可以通过序列化来处理多个 生产者,并通过序列化来处理多个消费者。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hContents: (*) 什么是环形缓冲区? (*) 测量2次幂缓冲区 (*) 内存屏障与环形缓冲区的结合使用 - 生产者 - 消费者 (*) 延伸阅读h]hContents: (*) 什么是环形缓冲区? (*) 测量2次幂缓冲区 (*) 内存屏障与环形缓冲区的结合使用 - 生产者 - 消费者 (*) 延伸阅读}hjsbah}(h]h ]h"]h$]h&]hhuh1hhjhhhhhK/ubj)}(hhh](j)}(h什么是环形缓冲区?h]h什么是环形缓冲区?}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhK1ubh)}(hu首先,什么是环形缓冲区?环形缓冲区是具有固定的有限大小的缓冲区,它有两个索引:h]hu首先,什么是环形缓冲区?环形缓冲区是具有固定的有限大小的缓冲区,它有两个索引:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK3hjhhubjs)}(h(1) 'head'索引 - 生产者将元素插入缓冲区的位置。 (2) 'tail'索引 - 消费者在缓冲区中找到下一个元素的位置。 h]jy)}(hhh](j~)}(h='head'索引 - 生产者将元素插入缓冲区的位置。 h]h)}(h<'head'索引 - 生产者将元素插入缓冲区的位置。h]h@‘head’索引 - 生产者将元素插入缓冲区的位置。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK5hjubah}(h]h ]h"]h$]h&]uh1j}hjubj~)}(hI'tail'索引 - 消费者在缓冲区中找到下一个元素的位置。 h]h)}(hH'tail'索引 - 消费者在缓冲区中找到下一个元素的位置。h]hL‘tail’索引 - 消费者在缓冲区中找到下一个元素的位置。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK7hjubah}(h]h ]h"]h$]h&]uh1j}hjubeh}(h]h ]h"]h$]h&]jjjjjjuh1jxhjubah}(h]h ]h"]h$]h&]uh1jrhhhK5hjhhubh)}(h通常,当tail指针等于head指针时,表明缓冲区是空的;而当head指针比tail指针少一个时, 表明缓冲区是满的。h]h通常,当tail指针等于head指针时,表明缓冲区是空的;而当head指针比tail指针少一个时, 表明缓冲区是满的。}(hj?hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK9hjhhubh)}(h添加元素时,递增head索引;删除元素时,递增tail索引。tail索引不应该跳过head索引, 两个索引在到达缓冲区末端时都应该被赋值为0,从而允许海量的数据流过缓冲区。h]h添加元素时,递增head索引;删除元素时,递增tail索引。tail索引不应该跳过head索引, 两个索引在到达缓冲区末端时都应该被赋值为0,从而允许海量的数据流过缓冲区。}(hjMhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKh]h#include }hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKLhjqhhubh)}(h这些宏包括:h]h这些宏包括:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKNhjqhhubjs)}(hX (#) 测量缓冲区的剩余容量:: CIRC_SPACE(head_index, tail_index, buffer_size); 返回缓冲区[1]中可插入元素的剩余空间大小。 (#) 测量缓冲区中的最大连续立即可用空间:: CIRC_SPACE_TO_END(head_index, tail_index, buffer_size); 返回缓冲区[1]中剩余的连续空间的大小,元素可以立即插入其中,而不必绕回到缓冲 区的开头。 (#) 测量缓冲区的使用数:: CIRC_CNT(head_index, tail_index, buffer_size); 返回当前占用缓冲区[2]的元素数量。 (#) 测量缓冲区的连续使用数:: CIRC_CNT_TO_END(head_index, tail_index, buffer_size); 返回可以从缓冲区中提取的连续元素[2]的数量,而不必绕回到缓冲区的开头。 h]jy)}(hhh](j~)}(h测量缓冲区的剩余容量:: CIRC_SPACE(head_index, tail_index, buffer_size); 返回缓冲区[1]中可插入元素的剩余空间大小。 h](h)}(h 测量缓冲区的剩余容量::h]h测量缓冲区的剩余容量:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKPhjubj)}(h0CIRC_SPACE(head_index, tail_index, buffer_size);h]h0CIRC_SPACE(head_index, tail_index, buffer_size);}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKRhjubh)}(h<返回缓冲区[1]中可插入元素的剩余空间大小。h]h<返回缓冲区[1]中可插入元素的剩余空间大小。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKThjubeh}(h]h ]h"]h$]h&]uh1j}hjubj~)}(h测量缓冲区中的最大连续立即可用空间:: CIRC_SPACE_TO_END(head_index, tail_index, buffer_size); 返回缓冲区[1]中剩余的连续空间的大小,元素可以立即插入其中,而不必绕回到缓冲 区的开头。 h](h)}(h5测量缓冲区中的最大连续立即可用空间::h]h4测量缓冲区中的最大连续立即可用空间:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKWhjubj)}(h7CIRC_SPACE_TO_END(head_index, tail_index, buffer_size);h]h7CIRC_SPACE_TO_END(head_index, tail_index, buffer_size);}hj sbah}(h]h ]h"]h$]h&]hhuh1jhhhKYhjubh)}(h返回缓冲区[1]中剩余的连续空间的大小,元素可以立即插入其中,而不必绕回到缓冲 区的开头。h]h返回缓冲区[1]中剩余的连续空间的大小,元素可以立即插入其中,而不必绕回到缓冲 区的开头。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK[hjubeh}(h]h ]h"]h$]h&]uh1j}hjubj~)}(h测量缓冲区的使用数:: CIRC_CNT(head_index, tail_index, buffer_size); 返回当前占用缓冲区[2]的元素数量。 h](h)}(h测量缓冲区的使用数::h]h测量缓冲区的使用数:}(hj/hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK_hj+ubj)}(h.CIRC_CNT(head_index, tail_index, buffer_size);h]h.CIRC_CNT(head_index, tail_index, buffer_size);}hj=sbah}(h]h ]h"]h$]h&]hhuh1jhhhKahj+ubh)}(h0返回当前占用缓冲区[2]的元素数量。h]h0返回当前占用缓冲区[2]的元素数量。}(hjKhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKchj+ubeh}(h]h ]h"]h$]h&]uh1j}hjubj~)}(h测量缓冲区的连续使用数:: CIRC_CNT_TO_END(head_index, tail_index, buffer_size); 返回可以从缓冲区中提取的连续元素[2]的数量,而不必绕回到缓冲区的开头。 h](h)}(h#测量缓冲区的连续使用数::h]h"测量缓冲区的连续使用数:}(hjchhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKfhj_ubj)}(h5CIRC_CNT_TO_END(head_index, tail_index, buffer_size);h]h5CIRC_CNT_TO_END(head_index, tail_index, buffer_size);}hjqsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhhj_ubh)}(hf返回可以从缓冲区中提取的连续元素[2]的数量,而不必绕回到缓冲区的开头。h]hf返回可以从缓冲区中提取的连续元素[2]的数量,而不必绕回到缓冲区的开头。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKjhj_ubeh}(h]h ]h"]h$]h&]uh1j}hjubeh}(h]h ]h"]h$]h&]jjjjjjuh1jxhjubah}(h]h ]h"]h$]h&]uh1jrhhhKPhjqhhubh)}(h_这里的每一个宏名义上都会返回一个介于0和buffer_size-1之间的值,但是:h]h_这里的每一个宏名义上都会返回一个介于0和buffer_size-1之间的值,但是:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKlhjqhhubjs)}(hX(1) CIRC_SPACE*()是为了在生产者中使用。对生产者来说,它们将返回一个下限,因为生 产者控制着head索引,但消费者可能仍然在另一个CPU上耗尽缓冲区并移动tail索引。 对消费者来说,它将显示一个上限,因为生产者可能正忙于耗尽空间。 (2) CIRC_CNT*()是为了在消费者中使用。对消费者来说,它们将返回一个下限,因为消费 者控制着tail索引,但生产者可能仍然在另一个CPU上填充缓冲区并移动head索引。 对于生产者,它将显示一个上限,因为消费者可能正忙于清空缓冲区。 (3) 对于第三方来说,生产者和消费者对索引的写入顺序是无法保证的,因为它们是独立的, 而且可能是在不同的CPU上进行的,所以在这种情况下的结果只是一种猜测,甚至可能 是错误的。 h]jy)}(hhh](j~)}(hX6CIRC_SPACE*()是为了在生产者中使用。对生产者来说,它们将返回一个下限,因为生 产者控制着head索引,但消费者可能仍然在另一个CPU上耗尽缓冲区并移动tail索引。 对消费者来说,它将显示一个上限,因为生产者可能正忙于耗尽空间。 h](h)}(hCIRC_SPACE*()是为了在生产者中使用。对生产者来说,它们将返回一个下限,因为生 产者控制着head索引,但消费者可能仍然在另一个CPU上耗尽缓冲区并移动tail索引。h]hCIRC_SPACE*()是为了在生产者中使用。对生产者来说,它们将返回一个下限,因为生 产者控制着head索引,但消费者可能仍然在另一个CPU上耗尽缓冲区并移动tail索引。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKnhjubh)}(h]对消费者来说,它将显示一个上限,因为生产者可能正忙于耗尽空间。h]h]对消费者来说,它将显示一个上限,因为生产者可能正忙于耗尽空间。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKqhjubeh}(h]h ]h"]h$]h&]uh1j}hjubj~)}(hX4CIRC_CNT*()是为了在消费者中使用。对消费者来说,它们将返回一个下限,因为消费 者控制着tail索引,但生产者可能仍然在另一个CPU上填充缓冲区并移动head索引。 对于生产者,它将显示一个上限,因为消费者可能正忙于清空缓冲区。 h](h)}(hCIRC_CNT*()是为了在消费者中使用。对消费者来说,它们将返回一个下限,因为消费 者控制着tail索引,但生产者可能仍然在另一个CPU上填充缓冲区并移动head索引。h]hCIRC_CNT*()是为了在消费者中使用。对消费者来说,它们将返回一个下限,因为消费 者控制着tail索引,但生产者可能仍然在另一个CPU上填充缓冲区并移动head索引。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKshjubh)}(h]对于生产者,它将显示一个上限,因为消费者可能正忙于清空缓冲区。h]h]对于生产者,它将显示一个上限,因为消费者可能正忙于清空缓冲区。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKvhjubeh}(h]h ]h"]h$]h&]uh1j}hjubj~)}(h对于第三方来说,生产者和消费者对索引的写入顺序是无法保证的,因为它们是独立的, 而且可能是在不同的CPU上进行的,所以在这种情况下的结果只是一种猜测,甚至可能 是错误的。 h]h)}(h对于第三方来说,生产者和消费者对索引的写入顺序是无法保证的,因为它们是独立的, 而且可能是在不同的CPU上进行的,所以在这种情况下的结果只是一种猜测,甚至可能 是错误的。h]h对于第三方来说,生产者和消费者对索引的写入顺序是无法保证的,因为它们是独立的, 而且可能是在不同的CPU上进行的,所以在这种情况下的结果只是一种猜测,甚至可能 是错误的。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKxhjubah}(h]h ]h"]h$]h&]uh1j}hjubeh}(h]h ]h"]h$]h&]jjjjjjuh1jxhjubah}(h]h ]h"]h$]h&]uh1jrhhhKnhjqhhubeh}(h]id3ah ]h"]测量2次幂缓冲区ah$]h&]uh1jhjhhhhhKEubj)}(hhh](j)}(h-内存屏障与环形缓冲区的结合使用h]h-内存屏障与环形缓冲区的结合使用}(hj/hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj,hhhhhK}ubh)}(hQ通过将内存屏障与环形缓冲区结合使用,可以避免以下需求:h]hQ通过将内存屏障与环形缓冲区结合使用,可以避免以下需求:}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj,hhubjs)}(h(1) 使用单个锁来控制对缓冲区两端的访问,从而允许同时填充和清空缓冲区;以及 (2) 使用原子计数器操作。 h]jy)}(hhh](j~)}(hj使用单个锁来控制对缓冲区两端的访问,从而允许同时填充和清空缓冲区;以及 h]h)}(hi使用单个锁来控制对缓冲区两端的访问,从而允许同时填充和清空缓冲区;以及h]hi使用单个锁来控制对缓冲区两端的访问,从而允许同时填充和清空缓冲区;以及}(hjVhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjRubah}(h]h ]h"]h$]h&]uh1j}hjOubj~)}(h使用原子计数器操作。 h]h)}(h使用原子计数器操作。h]h使用原子计数器操作。}(hjnhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjjubah}(h]h ]h"]h$]h&]uh1j}hjOubeh}(h]h ]h"]h$]h&]jjjjjjuh1jxhjKubah}(h]h ]h"]h$]h&]uh1jrhhhKhj,hhubh)}(h这有两个方面:填充缓冲区的生产者和清空缓冲区的消费者。在任何时候,只应有一个生产 者在填充缓冲区,同样的也只应有一个消费者在清空缓冲区,但双方可以同时操作。h]h这有两个方面:填充缓冲区的生产者和清空缓冲区的消费者。在任何时候,只应有一个生产 者在填充缓冲区,同样的也只应有一个消费者在清空缓冲区,但双方可以同时操作。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj,hhubj)}(hhh](j)}(h 生产者h]h 生产者}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(h生产者看起来像这样::h]h生产者看起来像这样:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hX9spin_lock(&producer_lock); unsigned long head = buffer->head; /* spin_unlock()和下一个spin_lock()提供必要的排序。 */ unsigned long tail = READ_ONCE(buffer->tail); if (CIRC_SPACE(head, tail, buffer->size) >= 1) { /* 添加一个元素到缓冲区 */ struct item *item = buffer[head]; produce_item(item); smp_store_release(buffer->head, (head + 1) & (buffer->size - 1)); /* wake_up()将确保在唤醒任何人之前提交head */ wake_up(consumer); } spin_unlock(&producer_lock);h]hX9spin_lock(&producer_lock); unsigned long head = buffer->head; /* spin_unlock()和下一个spin_lock()提供必要的排序。 */ unsigned long tail = READ_ONCE(buffer->tail); if (CIRC_SPACE(head, tail, buffer->size) >= 1) { /* 添加一个元素到缓冲区 */ struct item *item = buffer[head]; produce_item(item); smp_store_release(buffer->head, (head + 1) & (buffer->size - 1)); /* wake_up()将确保在唤醒任何人之前提交head */ wake_up(consumer); } spin_unlock(&producer_lock);}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjhhubh)}(h这将表明CPU必须在head索引使其对消费者可用之前写入新项目的内容,同时CPU必须在唤醒 消费者之前写入修改后的head索引。h]h这将表明CPU必须在head索引使其对消费者可用之前写入新项目的内容,同时CPU必须在唤醒 消费者之前写入修改后的head索引。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX请注意,wake_up()并不保证任何形式的屏障,除非确实唤醒了某些东西。因此我们不能依靠 它来进行排序。但是数组中始终有一个元素留空,因此生产者必须产生两个元素,然后才可 能破坏消费者当前正在读取的元素。同时,消费者连续调用之间成对的解锁-加锁提供了索引 读取(指示消费者已清空给定元素)和生产者对该相同元素的写入之间的必要顺序。h]hX请注意,wake_up()并不保证任何形式的屏障,除非确实唤醒了某些东西。因此我们不能依靠 它来进行排序。但是数组中始终有一个元素留空,因此生产者必须产生两个元素,然后才可 能破坏消费者当前正在读取的元素。同时,消费者连续调用之间成对的解锁-加锁提供了索引 读取(指示消费者已清空给定元素)和生产者对该相同元素的写入之间的必要顺序。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id5ah ]h"] 生产者ah$]h&]uh1jhj,hhhhhKubj)}(hhh](j)}(h 消费者h]h 消费者}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(h消费者看起来像这样::h]h消费者看起来像这样:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hXspin_lock(&consumer_lock); /* 读取该索引处的内容之前,先读取索引 */ unsigned long head = smp_load_acquire(buffer->head); unsigned long tail = buffer->tail; if (CIRC_CNT(head, tail, buffer->size) >= 1) { /* 从缓冲区中提取一个元素 */ struct item *item = buffer[tail]; consume_item(item); /* 在递增tail之前完成对描述符的读取。 */ smp_store_release(buffer->tail, (tail + 1) & (buffer->size - 1)); } spin_unlock(&consumer_lock);h]hXspin_lock(&consumer_lock); /* 读取该索引处的内容之前,先读取索引 */ unsigned long head = smp_load_acquire(buffer->head); unsigned long tail = buffer->tail; if (CIRC_CNT(head, tail, buffer->size) >= 1) { /* 从缓冲区中提取一个元素 */ struct item *item = buffer[tail]; consume_item(item); /* 在递增tail之前完成对描述符的读取。 */ smp_store_release(buffer->tail, (tail + 1) & (buffer->size - 1)); } spin_unlock(&consumer_lock);}hj sbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjhhubh)}(h这表明CPU在读取新元素之前确保索引是最新的,然后在写入新的尾指针之前应确保CPU已完 成读取该元素,这将擦除该元素。h]h这表明CPU在读取新元素之前确保索引是最新的,然后在写入新的尾指针之前应确保CPU已完 成读取该元素,这将擦除该元素。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX请注意,使用READ_ONCE()和smp_load_acquire()来读取反向(head)索引。这可以防止编译 器丢弃并重新加载其缓存值。如果您能确定反向(head)索引将仅使用一次,则这不是必须 的。smp_load_acquire()还可以强制CPU对后续的内存引用进行排序。类似地,两种算法都使 用smp_store_release()来写入线程的索引。这记录了我们正在写入可以并发读取的内容的事 实,以防止编译器破坏存储,并强制对以前的访问进行排序。h]hX请注意,使用READ_ONCE()和smp_load_acquire()来读取反向(head)索引。这可以防止编译 器丢弃并重新加载其缓存值。如果您能确定反向(head)索引将仅使用一次,则这不是必须 的。smp_load_acquire()还可以强制CPU对后续的内存引用进行排序。类似地,两种算法都使 用smp_store_release()来写入线程的索引。这记录了我们正在写入可以并发读取的内容的事 实,以防止编译器破坏存储,并强制对以前的访问进行排序。}(hj(hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id6ah ]h"] 消费者ah$]h&]uh1jhj,hhhhhKubeh}(h]id4ah ]h"]-内存屏障与环形缓冲区的结合使用ah$]h&]uh1jhjhhhhhK}ubj)}(hhh](j)}(h 延伸阅读h]h 延伸阅读}(hjIhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjFhhhhhKubh)}(hY关于Linux的内存屏障设施的描述,请查看Documentation/memory-barriers.txt。h]hY关于Linux的内存屏障设施的描述,请查看Documentation/memory-barriers.txt。}(hjWhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjFhhubeh}(h]id7ah ]h"] 延伸阅读ah$]h&]uh1jhjhhhhhKubeh}(h]id1ah ]h"]环形缓冲区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_handlerjerror_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}(jrjojnjkj)j&jCj@jjj;j8jjjgu nametypes}(jrjnj)jCjj;jjuh}(jojjkjj&jqj@j,jjj8jjgjFu footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}jKsRparse_messages]transform_messages] transformerN include_log]>Documentation/translations/zh_CN/core-api/circular-buffers.rst(NNNNta decorationNhhub.