1.gfifo概述

gfifo是一个First In First Out数据结构,它采用环形循环队列的数据结构来实现;它提供一个无边界的字节流服务,最重要的一点是,它使用并行无锁编程技术,即当它用于只有一个入队线程和一个出队线程的场情时,两个线程可以并发操作,而不需要任何加锁行为,就可以保证gfifo的线程安全。

struct gfifo {unsigned char *buffer;    /* the buffer holding the data */unsigned int size;    /* the size of the allocated buffer */unsigned int in;    /* data is added at offset (in % size) */unsigned int out;    /* data is extracted from off. (out % size) */
};

这是gfifo的数据结构,gfifo主要提供了两个操作,gfifo_put(入队操作)和gfifo_get(出队操作)。 它的各个数据成员如下:

buffer, 用于存放数据的缓存

size, buffer空间的大小,在初化时,将它向上扩展成2的幂

in, out, 和buffer一起构成一个循环队列。 in指向buffer中队头,而且out指向buffer中的队尾,它的结构如示图如下:

2. gfifo_alloc 分配gfifo内存和初始化工作

gfifo->size的值总是在调用者传进来的size参数的基础上向2的幂扩展,这是linux内核一贯的做法。这样的好处不言而喻--对gfifo->size取模运算可以转化为与运算,如下:

gfifo->in % gfifo->size 可以转化为 gfifo->in & (gfifo->size – 1)

在gfifo_alloc函数中,使用size & (size – 1)来判断size 是否为2幂,如果条件为真,则表示size不是2的幂,然后调用roundup_pow_of_two将之向上扩展为2的幂。

3. gfifo_put和gfifo_get,巧妙的入队和出队操作,无锁并发

gfifo_put是入队操作,它先将数据放入buffer里面,最后才修改in参数;
gfifo_get是出队操作,它先将数据从buffer中移走,最后才修改out。
in和out两者各司其职。计算机科学家已经证明,当只有一个读经程和一个写线程并发操作时,
不需要任何额外的锁,就可以确保是线程安全的,也即gfifo使用了无锁编程技术,以提高kernel的并发。

这里以gfifo入队为例(出队原理相同)

gfifo每次入队,gfifo->in只是简单地gfifo->in += len,因此gfifo->in总是一直增大(意思是gfifo->in会比size大),直到unsigned in最大值时,又会绕回到0这一起始端。但始终满足gfifo->out < gfifo->in,除非gfifo->in回绕到了0的那一端,即使如此,代码中计算长度的性质仍然是保持的。

我们先用简单的例子来形象说明这些性质吧:
当有数据入队时,那么in的值可能超过gfifo->size的值,那么我们使用另一个虚拟的方框来表示in变化后,在buffer内对gfifo->size取模的值。如下图如标:

当用户调用gfifo_put函数,入队的数据使gfifo的内存关系,引起上述两图的变化时,要拷贝两次内存。

因为入队数据,一部存放在gfifo->buffer的尾部,另一部分存放在gfifo->buffer的头部,计算公式非常简单。

rest = gfifo->size – gfifo->in & (gfifo->size – 1) 表示in下标到buffer末尾,还有多少空间(该空间大小未必就是合理大小,说不定比实际可用空间要大)。

如果len表示需要拷贝的长度的话,那么len - rest则表示有多少字节需要拷贝到buffer开始之处。

这样,我们读gfifo_put代码就很容易了。

len = min(len, fifo->size - (fifo->in - fifo->out));

fifo->in – fifo->out表示队列里面已使用的空间大小,fifo->size - (fifo->in – fifo->out)表示队列未使用的空间,

因此len = min(…),取两者之小,表示实际要拷贝的字节数。

拷贝len个字符数,fifo->in到buffer末尾所剩的空间是多少,这里面计算:

rest = min(len, fifo->size - (fifo->in & (fifo->size - 1)));

memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, rest);

拷贝到buffer开始之处
memcpy(fifo->buffer, buffer + l, len - rest);

rest表示len或fifo->in到buffer末尾所剩的空进行间大小的最小值,因为需要拷rest字节到fifo->buffer + fifo->in的位置上;
那么剩下要拷贝到buffer开始之处的长度为len – rest,当然,此值可能会为0,为0 时,memcpy函数不进行任何拷贝。

所有的拷贝完成后(可能是一次,也可能是两次memcpy),fifo->in 直接 += len,不需要取模运算。

当fifo->in超过了unsigned int的最大值时,而回绕到0这一端,上述的计算公式仍然正确

因为fifo->size的大小是2的幂,而unsigned int空间的大小是2 ^ 32,后者刚好是前者的倍数。如果从上述两个图的方式来描述,则表示unsigned int空间的数轴上刚好可以划成一定数量个gfifo->size大小方框,没有长度多余。这样,fifo->in 对fifo->size取模后,刚好落后对应的位置上。

现在假设往gfifo加入数据后,使用fifo->in < fifo->out关系,如下:

假设gfifo中数据的长度为leftdata,那么fifo->in和fifo->out有这样的关系:fifo->in = fifo->out + leftdata,并且fifo->in < fifo->out

这说明fifo->in 回绕到0这一段了,尽管如此,fifo->in和fifo->out的差距还是保持的,没有变化。即fifo->in – fifo->out仍然是leftdata

那么此时的可用空间是 fifo->size – leftdata = fifo->size - (fifo->in – fifo->out) = fifo->size – fifo->in + fifo->out。

因此无论fifo->in和fifo->out谁大谁小,计算fifo剩余空间大小的公式fifo->size – fifo->in + fifo->out都正确,

故可以保证gfifo_put函数里面的长度计算均是正确的。

转载于:https://www.cnblogs.com/zhanggaofeng/p/10638788.html

Sword 内核队列一相关推荐

  1. LiteOS内核源码分析:消息队列Queue

    本文分享自华为云社区<LiteOS内核源码分析系列十 消息队列Queue>,原文作者:zhushy . 队列(Queue)是一种常用于任务间通信的数据结构.任务能够从队列里面读取消息,当队 ...

  2. linux内核数据结构实现--链表、队列和哈希

    C是面向过程的语言,但是linux内核却用C实现了一套面向对象的设计模式,linux内核中处处体现着面向对象的思想. 1. 内核链表和list_entry 1.1 普通链表实现 我们在语法书上学到的链 ...

  3. Windows 内核数据结构学习总结

    <Windows内核编程>---基本数据结构 驱动对象: 每个驱动程序都会有唯一的驱动对象与之对应,并且这个驱动对象是在驱动加载时被内核中的对象管理程序所创建的.驱动对象用 DRIVER_ ...

  4. 《Linux内核设计与实现》读书笔记(六)- 内核数据结构

    内核数据结构贯穿于整个内核代码中,这里介绍4个基本的内核数据结构. 利用这4个基本的数据结构,可以在编写内核代码时节约大量时间. 主要内容: 链表 队列 映射 红黑树 1. 链表 链表是linux内核 ...

  5. 队列Queue:任务间的消息读写,安排起来~

    摘要:本文通过分析鸿蒙轻内核队列模块的源码,掌握队列使用上的差异. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十三 消息队列Queue>,作者:zhushy . 队列(Queue)是 ...

  6. Linux内核中的同步原语:自旋锁,信号量,互斥锁,读写信号量,顺序锁

    Linux内核中的同步原语 自旋锁,信号量,互斥锁,读写信号量,顺序锁 rtoax 2021年3月 在英文原文基础上,针对中文译文增加5.10.13内核源码相关内容. 1. Linux 内核中的同步原 ...

  7. 初探内核之《Linux内核设计与实现》笔记上

    内核简介  本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核   原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单. 2. 高 ...

  8. 嵌入式块设备内核驱动

    一,块设备简介(linux设备被分为字符设备.块设备.网络设备) 1,块设备特征 块设备是针对存储设备的,比如SD卡.EMMC.NAND Flash.Nor Flash.SPI Flash.U盘.机械 ...

  9. Windows程序员初学Linux内核(附Linux内核各版本历史纪年表)

    我是荔园微风,作为一名在IT界整整25年的老兵,最近受邀给年轻人讲了一场Windows内核和Linux内核相关的讲座.大家听得非常认真.下面我把其中一些PPT放上来和大家分享. Windows内核(右 ...

最新文章

  1. java restful接口开发实例_Spring Boot 中 10 行代码构建 RESTful 风格应用!
  2. 阿里云网盘,开放申请啦!非会员下载 10MB/s!有图有真相!
  3. PHP-Zend引擎剖析之Hello World(二)
  4. 2.7 HBase架构深入剖析
  5. 河北工程大学科信学院计算机二级,河北工程大学科信学院计算机科学与技术专业...
  6. mysql5.5对应的hibernate_Hibernate和Mysql5.5创建表出错——type=InnDB
  7. MyEclipse优化设置(最详细版本)
  8. 26条C++的经典语录,哪几句戳中你的心!
  9. Java学习----方法的重载
  10. 前端学习(1601):状态提升
  11. 构建自己的服务器有什么好处呢?
  12. java定义整形输出_java程序命令行接受字符转换为整形并相加输出
  13. C++新特性探究(13.6):右值引用再探究
  14. Android launcher 壁纸 wallpaper
  15. 05Struts2表单
  16. 文字不间断横向滚动 代码 IE FireFox兼容代码
  17. scala 返回值_Scala系列3-Scala函数
  18. c4d打开没反应_(图文+视频)C4D野教程:总结四种制作漩涡样条的方法
  19. 如何同步修改git项目名称和本地项目名称
  20. 微擎 人人商城 对接京东vop 对接京东商品,同步商品 地址,库存,价格,上下架等。(一) 同步商品...

热门文章

  1. PyTorch 1.6 发布:原生支持自动混合精度训练并进入稳定阶段
  2. 尤雨溪介绍 Vue 3:语法不变、TS 支持很好、2.0 系列还会发一个版本
  3. js中==和===区别
  4. bootstraptable 怎么在特定行添加数据_同等权限下多任职之间数据权限的实例
  5. uni-app的事件传参
  6. element tree树组件default-expand-all属性设置默认展开的节点
  7. linux停止rpc服务,linux下安装启动rpc服务
  8. 计算机建筑材料考试试题,建筑材料试题(建筑材料期中试题及)
  9. angularjs中使用ng-repeat渲染最后一个li的时候设置不同样式
  10. Javascript中 != 和 !==的区别