1. 有关ring buffer的理解

1)  ring buffer位首尾相接的buffer,即类似生活中的圆形跑道;

2)  空闲空间+数据空间=ring buffer大小

3)  ring buffer的读写,类似生活中在圆形跑道上的追赶游戏,领跑者位write,追赶着为read

4)  如果read跑的太快,追上write,追赶者read要停下来,否则游戏结束。即保证没有数据空间时,不再从ring buffer中读取数据;

5)  如果write跑的太快,反过来套圈要超过read,此时领跑者write也要停下来。即保证没有空闲空间时,不再往ring buffer中写入数据;

6) 所以,read和write之间的距离总是介于开区间(0, buffer大小)

2. linux2.6内核,kfifo的理解

假设buffer的大小为size,读指针为in,写指针为out

1) 在计算机中,整型数据加到最大值后溢出会回卷到0,从头开始)

2)buffer的长度必须是2的n次幂

3) buffer空闲空间和数据空间的大小

1> 空闲空间的大小=size-in+out

2> 空闲空间的大小=in-out

2.2 对数据空间大小计算的理解

本设计总能保证in前out前面的,in跑出unsigned int边界溢出后回卷。

因为buffer的大小是2的n次幂,而unsigned int也是2的n次幂(32位机器上,n=32),一般buffer大小不会超过unsigned int大小,即unsigned int被分成m个整块(m>=1)

第1种情况:

out+数据空间=in

空闲空间=size-数据控件=size-(in-out)=size-in+out

第2种情况:(in跑到unsigned int的边界后,溢出了)

out+数据空间=in,这个等式仍然成立。

所以:空闲空间=size-in+out,亦成立

2.3 写操作分析(读操作类似,不再赘述)

2.3.1 基本情况

设落在ring buffer内写指针为__in,读指针为__out,需要写入的空间大小为len, 其中

1. __in = fifo->in % (fifo->size - 1)  (读写指针都是从0开始算起)

2. __out = fifo->out % (fifo->size - 1)

3. __size = fifo->size

4.  len <= 空闲空间大小

2.3.2 写指针没有回卷

这种情况下,需要写两块buffer,做两次拷贝动作,设需要写入的大小为len,第一块空闲空间大小为left1,第二块为left2,需要第一次拷贝的大小为len1,第二次拷贝的大小为len2,len1 + len2 = len:

1. left1 = _size-__in;

2. len1 = min(len, left1) = min(len, _size-__in);

3. left2 = __out;

4. len2 = len - len1

2.3.3 写指针回卷

这种情况下,需要写一块buffer,做一次拷贝动作:

1. left1 = __out - __in <= __size - __in;

2. 而写入长度len <= 空闲空间大小,所以len <= left1 <= __size - __in,所以len1 = len, len1 = min(len, __size - __in)仍然成立

3. left2 = 0;

4. len2 = 0 = len -len1

2.3.4 两种特殊情况一般化

总结以上两种情形,第一块空闲空间大小为left1,第二块为left2,需要第一次拷贝的大小为len1,第二次拷贝的大小为len2,len1 + len2 = len,则通用情况如下:

1. len <= 空闲空间大小

2. len1 = min(len, _size-__in);

3. len2 = len -len1

附:linux2.6内核,kfifo的实现代码

点击(此处)折叠或打开

  1. unsigned int __kfifo_put(struct kfifo *fifo,
  2. unsigned char *buffer, unsigned int len)
  3. {
  4. unsigned int l;
  5. len = min(len, fifo->size - fifo->in + fifo->out);
  6. /* 前提条件写入大小len不超过空闲空间大小 */
  7. smp_mb();
  8. /* 第一块写入空闲空间,大小为min(len, size-in) */
  9. l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
  10. memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
  11. /* 第二块写入空闲空间,大小为len-min(len, size-in) */
  12. memcpy(fifo->buffer, buffer + l, len - l);
  13. /*
  14. * Ensure that we add the bytes to the kfifo -before-
  15. * we update the fifo->in index.
  16. */
  17. smp_wmb();
  18. fifo->in += len;
  19. return len;
  20. }
  21. unsigned int __kfifo_get(struct kfifo *fifo,
  22. unsigned char *buffer, unsigned int len)
  23. {
  24. unsigned int l;
  25. len = min(len, fifo->in - fifo->out);
  26. /*
  27. * Ensure that we sample the fifo->in index -before- we
  28. * start removing bytes from the kfifo.
  29. */
  30. smp_rmb();
  31. /* first get the data from fifo->out until the end of the buffer */
  32. l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
  33. memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
  34. /* then get the rest (if any) from the beginning of the buffer */
  35. memcpy(buffer + l, fifo->buffer, len - l);
  36. /*
  37. * Ensure that we remove the bytes from the kfifo -before-
  38. * we update the fifo->out index.
  39. */
  40. smp_mb();
  41. fifo->out += len;
  42. return len;
  43. }

【笔记】Linux内核中的循环缓冲区的更多相关文章

  1. (笔记)Linux内核中内存相关的操作函数

    linux内核中内存相关的操作函数 1.kmalloc()/kfree() static __always_inline void *kmalloc(size_t size, gfp_t flags) ...

  2. (笔记)Linux内核中ioremap映射的透彻理解

    几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器.状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址.根据CPU体系结构的不同,CPU对IO端口的编址方式有两种: (1)I ...

  3. Linux内核中流量控制

    linux内核中提供了流量控制的相关处理功能,相关代码在net/sched目录下:而应用层上的控制是通过iproute2软件包中的tc来实现, tc和sched的关系就好象iptables和netfi ...

  4. Linux内核中内存cache的实现【转】

    Linux内核中内存cache的实现 转自:http://blog.chinaunix.net/uid-127037-id-2919545.html   本文档的Copyleft归yfydz所有,使用 ...

  5. TCP/IP协议栈在Linux内核中的运行时序分析

    网络程序设计调研报告 TCP/IP协议栈在Linux内核中的运行时序分析 姓名:柴浩宇 学号:SA20225105 班级:软设1班 2021年1月 调研要求 在深入理解Linux内核任务调度(中断处理 ...

  6. 【转】在linux内核中读写文件 -- 不错

    原文网址:http://blog.csdn.net/tommy_wxie/article/details/8194276 1. 序曲 在用户态,读写文件可以通过read和write这两个系统调用来完成 ...

  7. Linux内核中常见内存分配函数(一)

    linux内核中采 用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表. * 页全局目录(Page Global Dir ...

  8. Linux内核中的list用法和实现分析

    这些天在思考知识体系的完整性,发现总是对消息队列的实现不满意,索性看看内核里面的链表实现形式,这篇文章就当做是学习的i笔记吧.. 内核代码中有很多的地方使用了list,而这个list的用法又跟我们平时 ...

  9. Linux内核中链表实现

    关于双链表实现,一般教科书上定义一个双向链表节点的方法如下: struct list_node{ stuct list_node *pre; stuct list_node *next; ElemTy ...

随机推荐

  1. ping 不通。无法访问目标主机

    台式机 使用无线网卡  又登录了VPN 有时候访问不了局域网内的主机 解决方案  添加路由即可 window+R 打开运行 输入cmd  然后输入 route add 10.16.1.89 10.13 ...

  2. QR 分解

    将学习到什么 介绍了平面旋转矩阵,Householder 矩阵和 QR 分解以入相关性质.   预备知识 平面旋转与 Householder 矩阵是特殊的酉矩阵,它们在建立某些基本的矩阵分解过程中起着 ...

  3. spring源码学习之容器的基本实现

    最近想拿出一部分时间来学习一下spring的源码,还特意买了一本书结合来看,当然主要是学习并跟着作者的思路来踏上学习spring的源码的道路,特意在此记录一下,<spring源码深度解析> ...

  4. Bootstrap历练实例:弹出提示信息的样式按钮

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  5. shell脚本,计算学生分数的题目。

    1.计算学生平均分数的值是多少? 2.计算每门课都大于80分的学生姓名.3.计算每门课都小于90分的学生姓名.

  6. java在线聊天项目0.6版 解决客户端关闭后异常问题 dis.readUTF()循环读取已关闭的socket

    服务端对try catch finally重新进行了定义,当发生异常,主动提示,或关闭出现异常的socket 服务器端代码修改如下: package com.swift; import java.io ...

  7. 【搜索 技巧】Letter gaps

    需要一定技巧的搜索题 题目描述 记得做过这样一道题 Ponder This Challenge: In the string CABACB, each letter appears exactly t ...

  8. Python基础:字典(dict)与集合(set)

    查找场景下与列表的性能对比 字典与集合之所以高效的原因是:内部结构都是一张哈希表. 平均情况下插入.查找和删除的时间复杂度为 O(1). 假设有数量100,000的产品列表: import time ...

  9. Python中摘要算法MD5,SHA1讲解

    摘要算法又称哈希算法.散列算法.它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示).摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要di ...

  10. DOM tiny-demo

    <script type="text/javascript" language="javascript">var i = 4; function a ...