linux5.2内核的ringbuffer(无锁环形缓冲区)原理实现与源码分析(上)

目录

        无锁环形缓冲区设计原理

        本文中使用的术语

        通用环形缓冲区概述

       主要指针

       使环形缓冲器lockless(无锁)

       嵌套写入


        无锁环形缓冲区设计原理

        阅读linux源码首先从理解原理开始,linux文档已经整理的很好了,直接查看源码目录里的Documentation/trace/ring-buffer-design.txt,以下提供本人的翻译和解释。

        本文中使用的术语

        tail - 在环形缓冲区中发生新写入的位置。

        head - 在环形缓冲区中发生新读取的位置。

        producer(生产者) - 写入环形缓冲区的任务(与writer相同)。

        writer(写者) - 与producer 相同。

        consumer(消费者) - 从缓冲区读取的任务(与writer相同)。

        reader (读者)- 与consumer相同。

        reader_page - 一个(大部分)由读者提供的在环形缓冲区外使用的页面。

        head_page - 指向读者下一步将使用的页面的指针。

        tail_page - 指向将被写入的下一页的页面的指针。

        commit_page - 指向最后一次完成非嵌套写入的页面的指针。

        cmpxchg - 硬件支持的原子操作,执行以下操作:

        (如果 A == C ,A = B)

        R = cmpxchg(A,C,B)//是指当且仅当现在的A等于C,将B写入A,然后把老的(现在)的A写入R。R获取的是之前的A的值,而不管A是否更新为B。要查看更新是否成功,可以使用R==C比较。(注:也就是说不管A的值是什么,R的值是原来A的值,判断R==C为true,说明A成功赋上B的值了,R==C为false说明失败了)

        通用环形缓冲区概述

-----------------------

        环形缓冲区可以在覆盖模式下使用,也可以在生产者/消费者模式下使用。

        生产者/消费者模式,是指生产者填满缓冲区的时候,消费者消费任何东西之前,生产者就会停止写缓冲区。这将丢失最近的事件。

        覆盖模式,是指生产者填满缓冲区的时候,消费者消费任何东西之前,生产者将覆盖旧的数据。这将丢失最早的事件。

        两个写入者(writers)不能同时写入(在相同的per-cpu缓冲区上),但是一个写入者可以打断另一个写入者,但他必须在前一个写入者可以继续写入之前完成写入。这对算法来说非常重要。写入者的行为就像一个栈。中断的方式会强制执行这种行为。

writer1 start
     <preempted> writer2 start
         <preempted> writer3 start
                     writer3 finishes
                 writer2 finishes
  writer1 finishes

        这非常像是一个写入者被一个中断抢占了,并且这个中断也在进行写操作。

        读者可以随时读。但是并没有两个读者同时在运行,同时一个读者也不能抢占/中断另一个读者。读者无法抢占/中断写入程序,它可以从缓冲区读出/消费的同时写者写入,但读者必须在另一个处理器上执行此操作。

        写者可以抢占读者,但读者不能抢占写者。读者可以读取缓冲区,同时在另一个处理器上作为写者。

        环形缓冲区由一个链表保存的页面组成。

        初始化时,为读者分配的页面不是环形缓冲区的一部分。

        head_page, tail_page 和commit_page页都初始化指向同一个页面。

        读者页面被初始化时,next指针指向head page,同时它的previous指针指向head page的前一页。

        读者有自己的页面可供使用。在启动时,此页面就是已分配好的,只是没有加到列表中。当读者想要要从缓冲区读取,如果其页面为空(如启动时),它将与head page交换页面。老的读者页面将成为环形缓冲区的一部分,head_page将被移出。插入的页面(老的reader_page)将成为新的head page。

        一旦新页面交给读者,只要一个写者离开了那一个页面读者可以做任何处理。

        一个如何交换读者页面的示例:注意,这并不是真实的在缓冲区中的head page,仅仅是演示:

+------+
  |reader|          RING BUFFER
  |page  |
  +------+
                  +---+   +---+   +---+
                  |   |-->|   |-->|   |
                  |   |<--|   |<--|   |
                  +---+   +---+   +---+
                   ^ |             ^ |
                   | +-------------+ |
                   +-----------------+


  +------+
  |reader|          RING BUFFER
  |page  |-------------------+
  +------+                   v
    |             +---+   +---+   +---+
    |             |   |-->|   |-->|   |
    |             |   |<--|   |<--|   |<-+
    |             +---+   +---+   +---+  |
    |              ^ |             ^ |   |
    |              | +-------------+ |   |
    |              +-----------------+   |
    +------------------------------------+

  +------+
  |reader|          RING BUFFER
  |page  |-------------------+
  +------+ <---------------+ v
    |  ^          +---+   +---+   +---+
    |  |          |   |-->|   |-->|   |
    |  |          |   |   |   |<--|   |<-+
    |  |          +---+   +---+   +---+  |
    |  |             |             ^ |   |
    |  |             +-------------+ |   |
    |  +-----------------------------+   |
    +------------------------------------+

  +------+
  |buffer|          RING BUFFER
  |page  |-------------------+
  +------+ <---------------+ v
    |  ^          +---+   +---+   +---+
    |  |          |   |   |   |-->|   |
    |  |  New     |   |   |   |<--|   |<-+
    |  | Reader   +---+   +---+   +---+  |
    |  |  page ----^                 |   |
    |  |                             |   |
    |  +-----------------------------+   |
    +------------------------------------+

        交换的页面可能是commit page和tail page,如果在环缓冲区中保存的内容小于在buffer page中保存的内容。


          reader page    commit page   tail page
              |              |             |
              v              |             |
             +---+           |             |
             |   |<----------+             |
             |   |<------------------------+
             |   |------+
             +---+      |
                        |
                        v
    +---+    +---+    +---+    +---+
<---|   |--->|   |--->|   |--->|   |--->
--->|   |<---|   |<---|   |<---|   |<---
    +---+    +---+    +---+    +---+

        这种情况对该算法仍然有效。

        当写入者离开这个页面时,它(页面)只是进入环形缓冲区,因为读者页面仍然指向环形缓冲区的下一个位置。

       主要指针

        reader page - 仅由读者使用的页面,不是 环形缓冲区的组成部分(可被换入)。

        head page - 环形缓冲区中要和读者交换的下一个页面。

         tail page - 下一次写入的页面。

        commit page - 上次完成写入的页面。

       

        commit page

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值