Sword 内核队列一
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函数里面的长度计算均是正确的。
Sword 内核队列一的更多相关文章
- Sword 内核队列二
#ifndef __GTC_FIFO_H_ #define __GTC_FIFO_H_ #ifndef GTC_MAX #define GTC_MAX(a,b) ((a) > (b) ? (a) ...
- 2017-2018-1 20179205《Linux内核原理与设计》第四周作业
<Linux内核原理与分析> 视频学习及实验操作 Linux内核源代码 视频中提到了三个我们要重点专注的目录下的代码,一个是arch目录下的x86,支持不同cpu体系架构的源代码:第二个是 ...
- 《Linux内核设计与实现》读书笔记(六)- 内核数据结构
内核数据结构贯穿于整个内核代码中,这里介绍4个基本的内核数据结构. 利用这4个基本的数据结构,可以在编写内核代码时节约大量时间. 主要内容: 链表 队列 映射 红黑树 1. 链表 链表是linux内核 ...
- 【旧文章搬运】Windows内核常见数据结构(基本类型)
原文发表于百度空间,2008-7-23 ========================================================================== 学内核从基 ...
- 初探内核之《Linux内核设计与实现》笔记上
内核简介 本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核 原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单.2. 高效 ...
- 队列Queue:任务间的消息读写,安排起来~
摘要:本文通过分析鸿蒙轻内核队列模块的源码,掌握队列使用上的差异. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十三 消息队列Queue>,作者:zhushy . 队列(Queue)是 ...
- 【windwos 操作系统】关键的Windows内核数据结构一览(上)
文章作者:r00tk1t 发布时间:2018年01月08日 - 21时56分 最后更新:2020年10月20日 - 21时01分 原始链接:https://r00tk1ts.github.io/201 ...
- 《IO 系统性能》笔记
近期项目中涉及和别人谈存储架构及选型,将IOPS相关的内容学习下.参考网上资料<IO 系统性能之一:衡量性能的几个指标> 1. 基本概念 1)读写IO操作:对应磁盘的存数据.取数据: 2) ...
- Linux Rootkit Learning
目录 . 学习Rootkit需要了解的基础知识 . 挂钩(HOOKING) . 直接内核对象操作 . LSM框架(Linux Security Module)于LKM安全 . rootkit检测技术及 ...
随机推荐
- 封装、property特性及绑定与非绑定方法
1.封装 (1)什么是封装? 封:属性对外是隐藏的,但对内是开放的: 装:申请一个名称空间,往里面装入一系列名字/属性 (2)为什么要封装? 封装数据属性的目的 首先定义属性的目的就是为了给类外部的使 ...
- Jupyter Notebook 快捷键和技巧
Jupyter Notebook 有两种键盘输入模式. 编辑模式,允许你往单元中键入代码或文本,这时的单元框线是绿色的. 命令模式,键盘输入运行程序命令:这时的单元框线是蓝色. 命令模式 ...
- Seaborn入门
Seaborn入门 Seaborn是基于matplotlib的python数据可视化库,提供更高层次的API封装,使用起来更加方便快捷. displot displot()集成了hist直方图和kde ...
- itchat分析微信好友的个性签名
itchat分析微信好友的个性签名 itchat是一个开源的微信个人号python接口(公众号.企业号接口为itchatmp).使用它可以非常优雅地操纵个人微信号.文档链接 七夕到了,博主也要自娱自乐 ...
- JAVA自学笔记05
JAVA自学笔记05 1.方法 1)方法就是完成特定功能的代码块,类似C语言中的函数. 2)格式: 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2,-){ 函数体; return ...
- Hibernate(9)_双向n对n
1.概述 ①双向 n-n 关联需要两端都使用集合属性 ②双向n-n关联必须使用连接表 ③集合属性应增加 key 子元素用以映射外键列, 集合元素里还应增加many-to-many子元素关联实体类 ④在 ...
- 1、html基础认识&常用标签(1)
从今天期我们进入前端的学习,先学习html,没有任何复杂难懂的逻辑需要烧脑,只需要记忆.练习.练习.练习. 本篇导航: HTML初识 常用标签介绍 <body>内常用标签 一.HTML初识 ...
- .Net core下的配置设置(一)——Configuration
ASP.NET Core 中提供了一个Configuration 包,用以应用配置基于配置提供程序建立的键值对.这里以json文件配置的方式,简单的介绍一下它的用法. 首先定义一个配置文件appset ...
- mybatis foreach中collection的三种用法
原文:https://www.cnblogs.com/xiemingjun/p/9800999.html foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合. foreach ...
- Unity2017灯光烘焙知识点
去研究一下灯光探针,性能可以提升不少.