今天在看linux内核时候,发现内核中的循环缓冲区的设计与实现非常的完美。让后就想自己仿照着也写一个,用于加深理解。

linux内核中对struct kfifo结构体的各种操作的源代码存放在:

/kernel/kfifo.c 和 /include/linux/kfifo.h这两个文件之中。

struct kfifo

{

unsigned char *buffer;

unsigned int size;  // 用于记录缓存区的大小;

unsigned int in;    // 用于记录下一到达缓存区的数据的存放位置;

unsigned int out;   // 用于记录下一次从缓存区提取的数据的位置;

spinlock_t   *lock; // 用于防止并发的对这个缓存区进行读操作或写操作;

};

通过这个struct kfifo结构体来对一个循环缓存区进行各种信息的统计;

先介绍几个在对这个缓存区进行各种操作时,需要用到的几个非常简单的函数,当然这几个函数是我自己在写循环缓存队列时用到的。

1. 测试一个数是否是 2的幂数

int my_is_power_of_2(int n)

{

return ( n!=0  &&  ( n & (n - 1) ) == 0 );

}

2. 如果一个数不是2的幂数的话,找出大于这个数的最近的2的幂数

int my_power_of_2(int n)

{

unsigned int i = 0;

if(n)

{

while(n != 0)

{

n = n >> 1;

i++;

}

return (1 << i);

}

}

此函数的用法: 如果 n = 12 ,不是2的幂数,my_power_of_2(n)返回 16;

n = 17 ,也不是2的幂数, my_power_of_2(n)返回 32;

3.这是一个宏定义,用于取两个数中最小的数;

#define min(m, n) (m) < (n) ? (m) : (n)

min(2,3) min()返回的是 2;

min(10, 4) min()返回的是 4;

开始对循环缓存队列进行各种操作的函数:

1.循环缓存队列的初始化:

struct kfifo * fifo_init(unsigned char *buffer, gfp_t gfp_mask, unsigned int len,

spinlock_t *spinlock)

{

struct kfifo *fifo = NULL;

fifo = kmalloc(sizeof(struct kfifo), gfp_mask);

if( fifo == NULL )

return -ENOMEM;

fifo->buffer = buffer;

fifo->size = len;

fifo->in = 0;

fifo->out = 0;

fifo->lock = spinlock;

return fifo;

}

kfifo_init()函数主要做的工作是:分配了一个struct kfifo结构体,然后对其进行初始赋值;

2.给struct kfifo结构体分配内存,用于存取缓存的数据

struct kfifo * kfifo_alloc(gfp_t gfp_mask, unsigned int len, spinlock_t *spinlock)

{

struct kfifo *ret = NULL;

unsigned char *buffer = NULL;

// 用于判断len是否是 2的幂数,如果是的话直接使用就可以了

//     如果不是的话,使用my_power_of_2()将其调整为2的幂数

if(!my_is_power_of_2(len))

len = my_power_of_2(len);

buffer = kmalloc(len, gfp_mask);

if( buffer == NULL )

return -ENOMEM;

memset(buffer, 0, len);

ret = kfifio_init(buffer, gfp_mask, len, spinlock);

if( IS_ERR(ret) ) // IS_ERR()函数主要用于判断返回值是否是像-ENOMEM这类的返回值;

kfree(buffer);

return ret;

}

3.释放循环缓存队列中的缓存;

void kfifo_free(struct kfifo *fifo)

{

kfree(fifo->buffer);

kfree(fifo);

}

4.给循环缓存队列中添加数据的操作(未加锁的版本)

int __kfifo_put(unsigned char *buffer, unsigned int len, struct kfifo *fifo)

{

unsigned int length ;

//fifo->size - fifo->in + fifo->out 这个计算出来的结果值是循环缓存队列中的未用的

//空间字节数;

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

// fifo->size - (fifo->in & (fifo->size -1))计算出来的结果是fifo->in到缓存数组

//末尾的空间大小

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

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

memcpy(fifo->buffer, buffer+length, len-length);

fifo->in +=len;

return len;

}

__kfifo_put()函数返回的是写入循环队列中的数据的字节数;因为有可能存在,当要写入的字节数大于循环队列中的空闲的空间大小。

5.从循环缓存队列中取走数据的操作(为加锁版本)

int __kfifo_get(unsigned char *buffer, int len, struct kfifo *fifo)

{

unsigned int length;

// fifo->in - fifo->out 计算出来的是缓存队列中存放的字节数;

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

length = min(len, fifo->size - (fifo->out & (fifo->size - 1))) ;

memcpy( buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), length );

memcpy( buffer+length,  fifo->buffer,  len - length);

fifo->out += len;

return len;

}

__kfifo_get()返回的是从循环队列中提取的数据的字节数;

6.循环缓存队列的重置(为加锁):

inline void __kfifo_reset(struct kfifo *fifo)

{

fifo->in = 0;

fifo->out = 0;

}

7. 循环缓存队列中的数据长度(为加锁):

inline int  __kfifo_len(struct kfifo *fifo)

{

return fifo->in - fifo->out;

}

以上的这几个函数涉及到了对循环缓存队列的读写操作,而在系统中如果同时存在多个进程对这个缓存队列进行读写操作的话,会存在数据的紊乱问题。所以这个循环缓存队列可以看成是一个临界区资源,每次只能有一个进程对其进行读写操作,不允许并发的读写操作。

所以就有了以上几个函数的加锁版本了:

1. int kfifo_put(unsigned char *buffer, unsigned int len, struct kfifo *fifo)

{

unsigned int flags;

unsigned int ret;

spin_lock_irqsave(fifo->lock, flags);

ret = __kfifo_put(buffer, len, fifo);

spin_unlock_irqstore(fifo->lock, flags);

return ret;

}

2. int kfifo_get(unsigned char *buffer, unsigned int len, struct kfifo *fifo)

{

unsigned int flags ;

unsigned int ret;

spin_lock_irqsave(fifo->lock, flags);

ret = __kfifo_get(buffer, len, fifo);

spin_unlock_irqstore(fifo->lock, flags);

return ret;

}

3.int kfifo_len(struct kfifo *fifo)

{

unsigned  int flags;

unsigned  int ret;

spin_lock_irqsave(fifo->lock, flags);

ret = __kfifo_len(fifo);

spin_unlock_irqstore(fifo->lock, flags);

return ret;

}

4. void kfifo_reset(struct kfifo *fifo)

{

unsigned int flags;

unsigned int ret;

spin_lock_irqsave(fifo->lock, flags);

__kfifo_reset(fifo);

spin_unlock_irqstore(fifo->lock, flags);

}

看完内核对struct kfifo 结构体的各种操作,感觉在内核代中,每个函数只完成一个功能,并且对循环缓存队列的读操作、写操作、重置操作都非常的简单,代码的逻辑也是非常的清晰。

以上代码是我对模仿内核代码写的,当然处理的肯定没有内核代码那么完美,比如在内核代码中对循环队列缓存区的读写操作是要加入内存屏障的。

多看、多想内核代码,对编程水平帮助很大!

linux内核计算次方,linux内核中的循环缓冲去的设计与实现相关推荐

  1. linux内核计算次方,linux内核bic和cubic实现

    BIC算法 当发生丢包的时候,当前窗口为W-max, 减小窗口后的大小为W, BIC算法就是根据这个原理在(W, W-max]区间内做二分搜索 当接近于W-max时曲线应该更平滑,当离W-max较远的 ...

  2. python调用c语言内核计算,在Linux上实现Python调用C语言函数

    一般思路 Python中内置ctypes库,需调用c编译成的.so文件来实现函数调用. 假设我们所需调用的c文件名为test.c,文件里有我们需要的函数func(x,y). 将.c文件编译成 .so文 ...

  3. Linux数组计算平均值,从数组中读取并计算平均值

    我正在尝试编写从数组中读取一行字符的代码,将这些字符分配给一个整数,然后对该行的所有整数进行平均,然后对数组中的每一行执行此操作.以下是我迄今为止:从数组中读取并计算平均值 Scanner in = ...

  4. linux线程计算,有关Linux进程与线程数目计算的问题

    问题的提出: Linux,一个进程有n个线程,每个线程又使用fork()创建了一个进程? 请问:总共有多少线程和进程? 问题的解答: Alexander Amelkin • According to ...

  5. linux date 计算时间差,linux shell date 时间运算以及时间差计算方法

    最近一段时间,在处理Shell 脚本时候,遇到时间的处理问题. 时间的加减,以及时间差的计算. 获取当前时间戳date +%s 1. 时间加减 这里处理方法,是将基础的时间转变为时间戳,然后,需要增加 ...

  6. linux nand 坏块_Linux内核中NAND Flash坏块管理

    由于NAND Flash的现有工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此在NAND芯片出厂的时候,厂家只能保证block 0不是坏块,对于其它block,则均有可 ...

  7. KSM(Kernel Samepage Merging) 剖析:Linux 内核中的内存去耦合

    简介:作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通过 ...

  8. Linux内核学习笔记——Linux中的用户组和权限管理(UID是什么?)

    目录 一.背景 进程权限 最小权限原则 二.linux系统安全模型 用户 用户组 用户和组的关系 安全上下文 进程的用户ID 函数setreuid和setregid 函数seteuid和setegid ...

  9. Linux 内核通知链随笔【中】

        关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不同子系统之间的通信,通知链只能用于内核不同子系统之间的通信.那么内核通知链到底是怎么工作的?我们如何才能用好 ...

最新文章

  1. 顶点(vertexs) 图元(primitives) 片元(fragments片断) 像素(pixels)
  2. 关于字符串属性的几道面试题目
  3. postgresql中表的继承及分区表(四)
  4. OpenCV使用Kinect和其他OpenNI兼容的深度传感器
  5. Java语言语法语义分析器设计与实现
  6. 希望我在开始第一个机器学习项目之前就了解的那些事儿
  7. python获取网页数据对电脑性能_【Python】网页数据爬取实战
  8. 拦截游戏窗口被移动_保障电脑流畅,游戏不卡,良心软件推荐
  9. [javaSE] 网络编程(浏览器客户端-自定义服务端)
  10. 梯度下降、随机梯度下降、方差减小的梯度下降(matlab实现)
  11. 笨办法学 Python · 续 练习 13:单链表
  12. smartbi v7 Linux,图表传值?花式互动?Smartbi V7.0.1新特性有没有撩到你?
  13. ArcEngine中拓扑的使用
  14. 【M31】让函数根据一个以上的对象类型来决定如何虚化
  15. Ubuntu和Win10双系统修改硬盘序列号后无法进入Win10的解决办法
  16. ps把一张图里面的文字去掉而不改变字后面的背景;把同一个颜色的改为别的颜色
  17. html鼠标经过图片有浮起效果,CSS实现鼠标滑过卡片上浮效果的示例
  18. HTML+CSS简单的淘宝首页框架布局小练(三)
  19. Day06(上)C++继承和派生
  20. “黑产“识别算法(社区检测,相似度,关联关系)

热门文章

  1. 计算机实训大纲,计算机专业实训大纲
  2. android切图倍数,【Flutter工具】fmaker:自动生成倍率切图/自动更换App图标
  3. 搭建前端监控系统(四)接口请求异常监控篇
  4. 【早知云世】当AI遇上云计算,其应用短板与长处
  5. 产品质量的基石——微软Bug管理
  6. 如何在您的笔记本上搭建View 演示环境 -7.无线配置与连接
  7. U盘autorun病毒清理
  8. 完全二叉树的判断java,判断二叉树是否为完全二叉树的实例
  9. csv导入pgsql不成功_数据科学 | pandas数据导入与导出
  10. 孪生神经网络_驾驶习惯也能识人?基于时空孪生神经网络的轨迹识别