1、前言

  最近项目中用到一个环形缓冲区(ring buffer),代码是由linux内核的kfifo改过来的。缓冲区在文件系统中经常用到,通过缓冲区缓解cpu读写内存和读写磁盘的速度。例如一个进程A产生数据发给另外一个进程B,进程B需要对进程A传的数据进行处理并写入文件,如果B没有处理完,则A要延迟发送。为了保证进程A减少等待时间,可以在A和B之间采用一个缓冲区,A每次将数据存放在缓冲区中,B每次冲缓冲区中取。这是典型的生产者和消费者模型,缓冲区中数据满足FIFO特性,因此可以采用队列进行实现。Linux内核的kfifo正好是一个环形队列,可以用来当作环形缓冲区。生产者与消费者使用缓冲区如下图所示:

  环形缓冲区的详细介绍及实现方法可以参考http://en.wikipedia.org/wiki/Circular_buffer,介绍的非常详细,列举了实现环形队列的几种方法。环形队列的不便之处在于如何判断队列是空还是满。维基百科上给三种实现方法。

2、linux 内核kfifo

  kfifo设计的非常巧妙,代码很精简,对于入队和出对处理的出人意料。首先看一下kfifo的数据结构:

  1. struct kfifo {
  2. unsigned char *buffer; /* the buffer holding the data */
  3. unsigned int size; /* the size of the allocated buffer */
  4. unsigned int in; /* data is added at offset (in % size) */
  5. unsigned int out; /* data is extracted from off. (out % size) */
  6. spinlock_t *lock; /* protects concurrent modifications */
  7. };

kfifo提供的方法有:

  1. //根据给定buffer创建一个kfifo
  2. struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
  3. gfp_t gfp_mask, spinlock_t *lock);
  4. //给定size分配buffer和kfifo
  5. struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask,
  6. spinlock_t *lock);
  7. //释放kfifo空间
  8. void kfifo_free(struct kfifo *fifo)
  9. //向kfifo中添加数据
  10. unsigned int kfifo_put(struct kfifo *fifo,
  11. const unsigned char *buffer, unsigned int len)
  12. //从kfifo中取数据
  13. unsigned int kfifo_put(struct kfifo *fifo,
  14. const unsigned char *buffer, unsigned int len)
  15. //获取kfifo中有数据的buffer大小
  16. unsigned int kfifo_len(struct kfifo *fifo)

定义自旋锁的目的为了防止多进程/线程并发使用kfifo。因为in和out在每次get和out时,发生改变。初始化和创建kfifo的源代码如下:

  1. struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
  2. gfp_t gfp_mask, spinlock_t *lock)
  3. {
  4. struct kfifo *fifo;
  5. /* size must be a power of 2 */
  6. BUG_ON(!is_power_of_2(size));
  7. fifo = kmalloc(sizeof(struct kfifo), gfp_mask);
  8. if (!fifo)
  9. return ERR_PTR(-ENOMEM);
  10. fifo->buffer = buffer;
  11. fifo->size = size;
  12. fifo->in = fifo->out = ;
  13. fifo->lock = lock;
  14.  
  15. return fifo;
  16. }
  17. struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock)
  18. {
  19. unsigned char *buffer;
  20. struct kfifo *ret;
  21. if (!is_power_of_2(size)) {
  22. BUG_ON(size > 0x80000000);
  23. size = roundup_pow_of_two(size);
  24. }
  25. buffer = kmalloc(size, gfp_mask);
  26. if (!buffer)
  27. return ERR_PTR(-ENOMEM);
  28. ret = kfifo_init(buffer, size, gfp_mask, lock);
  29.  
  30. if (IS_ERR(ret))
  31. kfree(buffer);
  32. return ret;
  33. }

  在kfifo_init和kfifo_calloc中,kfifo->size的值总是在调用者传进来的size参数的基础上向2的幂扩展,这是内核一贯的做法。这样的好处不言而喻--对kfifo->size取模运算可以转化为与运算,如:kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1)

kfifo的巧妙之处在于in和out定义为无符号类型,在put和get时,in和out都是增加,当达到最大值时,产生溢出,使得从0开始,进行循环使用。put和get代码如下所示:

  1. static inline unsigned int kfifo_put(struct kfifo *fifo,
  2. const unsigned char *buffer, unsigned int len)
  3. {
  4. unsigned long flags;
  5. unsigned int ret;
  6. spin_lock_irqsave(fifo->lock, flags);
  7. ret = __kfifo_put(fifo, buffer, len);
  8. spin_unlock_irqrestore(fifo->lock, flags);
  9. return ret;
  10. }
  11.  
  12. static inline unsigned int kfifo_get(struct kfifo *fifo,
  13. unsigned char *buffer, unsigned int len)
  14. {
  15. unsigned long flags;
  16. unsigned int ret;
  17. spin_lock_irqsave(fifo->lock, flags);
  18. ret = __kfifo_get(fifo, buffer, len);
  19. //当fifo->in == fifo->out时,buufer为空
  20. if (fifo->in == fifo->out)
  21. fifo->in = fifo->out = ;
  22. spin_unlock_irqrestore(fifo->lock, flags);
  23. return ret;
  24. }
  25.  
  26. unsigned int __kfifo_put(struct kfifo *fifo,
  27. const unsigned char *buffer, unsigned int len)
  28. {
  29. unsigned int l;
  30. //buffer中空的长度
  31. len = min(len, fifo->size - fifo->in + fifo->out);
  32. /*
  33. * Ensure that we sample the fifo->out index -before- we
  34. * start putting bytes into the kfifo.
  35. */
  36. smp_mb();
  37. /* first put the data starting from fifo->in to buffer end */
  38. l = min(len, fifo->size - (fifo->in & (fifo->size - )));
  39. memcpy(fifo->buffer + (fifo->in & (fifo->size - )), buffer, l);
  40. /* then put the rest (if any) at the beginning of the buffer */
  41. memcpy(fifo->buffer, buffer + l, len - l);
  42.  
  43. /*
  44. * Ensure that we add the bytes to the kfifo -before-
  45. * we update the fifo->in index.
  46. */
  47. smp_wmb();
  48. fifo->in += len; //每次累加,到达最大值后溢出,自动转为0
  49. return len;
  50. }
  51.  
  52. unsigned int __kfifo_get(struct kfifo *fifo,
  53. unsigned char *buffer, unsigned int len)
  54. {
  55. unsigned int l;
  56. //有数据的缓冲区的长度
  57. len = min(len, fifo->in - fifo->out);
  58. /*
  59. * Ensure that we sample the fifo->in index -before- we
  60. * start removing bytes from the kfifo.
  61. */
  62. smp_rmb();
  63. /* first get the data from fifo->out until the end of the buffer */
  64. l = min(len, fifo->size - (fifo->out & (fifo->size - )));
  65. memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - )), l);
  66. /* then get the rest (if any) from the beginning of the buffer */
  67. memcpy(buffer + l, fifo->buffer, len - l);
  68. /*
  69. * Ensure that we remove the bytes from the kfifo -before-
  70. * we update the fifo->out index.
  71. */
  72. smp_mb();
  73. fifo->out += len; //每次累加,到达最大值后溢出,自动转为0
  74. return len;
  75. }

  put和get在调用__put和__get过程都进行加锁,防止并发。从代码中可以看出put和get都调用两次memcpy,这针对的是边界条件。例如下图:蓝色表示空闲,红色表示占用。

(1)空的kfifo,

(2)put一个buffer后

(3)get一个buffer后

(4)当此时put的buffer长度超出in到末尾长度时,则将剩下的移到头部去

3、测试程序

仿照kfifo编写一个ring_buffer,现有线程互斥量进行并发控制。设计的ring_buffer如下所示:

  1. /**@brief 仿照linux kfifo写的ring buffer
  2. *@atuher Anker date:2013-12-18
  3. * ring_buffer.h
  4. * */
  5.  
  6. #ifndef KFIFO_HEADER_H
  7. #define KFIFO_HEADER_H
  8.  
  9. #include <inttypes.h>
  10. #include <string.h>
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <errno.h>
  14. #include <assert.h>
  15.  
  16. //判断x是否是2的次方
  17. #define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
  18. //取a和b中最小值
  19. #define min(a, b) (((a) < (b)) ? (a) : (b))
  20.  
  21. struct ring_buffer
  22. {
  23. void *buffer; //缓冲区
  24. uint32_t size; //大小
  25. uint32_t in; //入口位置
  26. uint32_t out; //出口位置
  27. pthread_mutex_t *f_lock; //互斥锁
  28. };
  29. //初始化缓冲区
  30. struct ring_buffer* ring_buffer_init(void *buffer, uint32_t size, pthread_mutex_t *f_lock)
  31. {
  32. assert(buffer);
  33. struct ring_buffer *ring_buf = NULL;
  34. if (!is_power_of_2(size))
  35. {
  36. fprintf(stderr,"size must be power of 2.\n");
  37. return ring_buf;
  38. }
  39. ring_buf = (struct ring_buffer *)malloc(sizeof(struct ring_buffer));
  40. if (!ring_buf)
  41. {
  42. fprintf(stderr,"Failed to malloc memory,errno:%u,reason:%s",
  43. errno, strerror(errno));
  44. return ring_buf;
  45. }
  46. memset(ring_buf, , sizeof(struct ring_buffer));
  47. ring_buf->buffer = buffer;
  48. ring_buf->size = size;
  49. ring_buf->in = ;
  50. ring_buf->out = ;
  51. ring_buf->f_lock = f_lock;
  52. return ring_buf;
  53. }
  54. //释放缓冲区
  55. void ring_buffer_free(struct ring_buffer *ring_buf)
  56. {
  57. if (ring_buf)
  58. {
  59. if (ring_buf->buffer)
  60. {
  61. free(ring_buf->buffer);
  62. ring_buf->buffer = NULL;
  63. }
  64. free(ring_buf);
  65. ring_buf = NULL;
  66. }
  67. }
  68.  
  69. //缓冲区的长度
  70. uint32_t __ring_buffer_len(const struct ring_buffer *ring_buf)
  71. {
  72. return (ring_buf->in - ring_buf->out);
  73. }
  74.  
  75. //从缓冲区中取数据
  76. uint32_t __ring_buffer_get(struct ring_buffer *ring_buf, void * buffer, uint32_t size)
  77. {
  78. assert(ring_buf || buffer);
  79. uint32_t len = ;
  80. size = min(size, ring_buf->in - ring_buf->out);
  81. /* first get the data from fifo->out until the end of the buffer */
  82. len = min(size, ring_buf->size - (ring_buf->out & (ring_buf->size - )));
  83. memcpy(buffer, ring_buf->buffer + (ring_buf->out & (ring_buf->size - )), len);
  84. /* then get the rest (if any) from the beginning of the buffer */
  85. memcpy(buffer + len, ring_buf->buffer, size - len);
  86. ring_buf->out += size;
  87. return size;
  88. }
  89. //向缓冲区中存放数据
  90. uint32_t __ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
  91. {
  92. assert(ring_buf || buffer);
  93. uint32_t len = ;
  94. size = min(size, ring_buf->size - ring_buf->in + ring_buf->out);
  95. /* first put the data starting from fifo->in to buffer end */
  96. len = min(size, ring_buf->size - (ring_buf->in & (ring_buf->size - )));
  97. memcpy(ring_buf->buffer + (ring_buf->in & (ring_buf->size - )), buffer, len);
  98. /* then put the rest (if any) at the beginning of the buffer */
  99. memcpy(ring_buf->buffer, buffer + len, size - len);
  100. ring_buf->in += size;
  101. return size;
  102. }
  103.  
  104. uint32_t ring_buffer_len(const struct ring_buffer *ring_buf)
  105. {
  106. uint32_t len = ;
  107. pthread_mutex_lock(ring_buf->f_lock);
  108. len = __ring_buffer_len(ring_buf);
  109. pthread_mutex_unlock(ring_buf->f_lock);
  110. return len;
  111. }
  112.  
  113. uint32_t ring_buffer_get(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
  114. {
  115. uint32_t ret;
  116. pthread_mutex_lock(ring_buf->f_lock);
  117. ret = __ring_buffer_get(ring_buf, buffer, size);
  118. //buffer中没有数据
  119. if (ring_buf->in == ring_buf->out)
  120. ring_buf->in = ring_buf->out = ;
  121. pthread_mutex_unlock(ring_buf->f_lock);
  122. return ret;
  123. }
  124.  
  125. uint32_t ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
  126. {
  127. uint32_t ret;
  128. pthread_mutex_lock(ring_buf->f_lock);
  129. ret = __ring_buffer_put(ring_buf, buffer, size);
  130. pthread_mutex_unlock(ring_buf->f_lock);
  131. return ret;
  132. }
  133. #endif

采用多线程模拟生产者和消费者编写测试程序,如下所示:

  1. /**@brief ring buffer测试程序,创建两个线程,一个生产者,一个消费者。
  2. * 生产者每隔1秒向buffer中投入数据,消费者每隔2秒去取数据。
  3. *@atuher Anker date:2013-12-18
  4. * */
  5. #include "ring_buffer.h"
  6. #include <pthread.h>
  7. #include <time.h>
  8.  
  9. #define BUFFER_SIZE 1024 * 1024
  10.  
  11. typedef struct student_info
  12. {
  13. uint64_t stu_id;
  14. uint32_t age;
  15. uint32_t score;
  16. }student_info;
  17.  
  18. void print_student_info(const student_info *stu_info)
  19. {
  20. assert(stu_info);
  21. printf("id:%lu\t",stu_info->stu_id);
  22. printf("age:%u\t",stu_info->age);
  23. printf("score:%u\n",stu_info->score);
  24. }
  25.  
  26. student_info * get_student_info(time_t timer)
  27. {
  28. student_info *stu_info = (student_info *)malloc(sizeof(student_info));
  29. if (!stu_info)
  30. {
  31. fprintf(stderr, "Failed to malloc memory.\n");
  32. return NULL;
  33. }
  34. srand(timer);
  35. stu_info->stu_id = + rand() % ;
  36. stu_info->age = rand() % ;
  37. stu_info->score = rand() % ;
  38. print_student_info(stu_info);
  39. return stu_info;
  40. }
  41.  
  42. void * consumer_proc(void *arg)
  43. {
  44. struct ring_buffer *ring_buf = (struct ring_buffer *)arg;
  45. student_info stu_info;
  46. while()
  47. {
  48. sleep();
  49. printf("------------------------------------------\n");
  50. printf("get a student info from ring buffer.\n");
  51. ring_buffer_get(ring_buf, (void *)&stu_info, sizeof(student_info));
  52. printf("ring buffer length: %u\n", ring_buffer_len(ring_buf));
  53. print_student_info(&stu_info);
  54. printf("------------------------------------------\n");
  55. }
  56. return (void *)ring_buf;
  57. }
  58.  
  59. void * producer_proc(void *arg)
  60. {
  61. time_t cur_time;
  62. struct ring_buffer *ring_buf = (struct ring_buffer *)arg;
  63. while()
  64. {
  65. time(&cur_time);
  66. srand(cur_time);
  67. int seed = rand() % ;
  68. printf("******************************************\n");
  69. student_info *stu_info = get_student_info(cur_time + seed);
  70. printf("put a student info to ring buffer.\n");
  71. ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));
  72. printf("ring buffer length: %u\n", ring_buffer_len(ring_buf));
  73. printf("******************************************\n");
  74. sleep();
  75. }
  76. return (void *)ring_buf;
  77. }
  78.  
  79. int consumer_thread(void *arg)
  80. {
  81. int err;
  82. pthread_t tid;
  83. err = pthread_create(&tid, NULL, consumer_proc, arg);
  84. if (err != )
  85. {
  86. fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n",
  87. errno, strerror(errno));
  88. return -;
  89. }
  90. return tid;
  91. }
  92. int producer_thread(void *arg)
  93. {
  94. int err;
  95. pthread_t tid;
  96. err = pthread_create(&tid, NULL, producer_proc, arg);
  97. if (err != )
  98. {
  99. fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n",
  100. errno, strerror(errno));
  101. return -;
  102. }
  103. return tid;
  104. }
  105.  
  106. int main()
  107. {
  108. void * buffer = NULL;
  109. uint32_t size = ;
  110. struct ring_buffer *ring_buf = NULL;
  111. pthread_t consume_pid, produce_pid;
  112.  
  113. pthread_mutex_t *f_lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
  114. if (pthread_mutex_init(f_lock, NULL) != )
  115. {
  116. fprintf(stderr, "Failed init mutex,errno:%u,reason:%s\n",
  117. errno, strerror(errno));
  118. return -;
  119. }
  120. buffer = (void *)malloc(BUFFER_SIZE);
  121. if (!buffer)
  122. {
  123. fprintf(stderr, "Failed to malloc memory.\n");
  124. return -;
  125. }
  126. size = BUFFER_SIZE;
  127. ring_buf = ring_buffer_init(buffer, size, f_lock);
  128. if (!ring_buf)
  129. {
  130. fprintf(stderr, "Failed to init ring buffer.\n");
  131. return -;
  132. }
  133. #if 0
  134. student_info *stu_info = get_student_info();
  135. ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));
  136. stu_info = get_student_info();
  137. ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));
  138. ring_buffer_get(ring_buf, (void *)stu_info, sizeof(student_info));
  139. print_student_info(stu_info);
  140. #endif
  141. printf("multi thread test.......\n");
  142. produce_pid = producer_thread((void*)ring_buf);
  143. consume_pid = consumer_thread((void*)ring_buf);
  144. pthread_join(produce_pid, NULL);
  145. pthread_join(consume_pid, NULL);
  146. ring_buffer_free(ring_buf);
  147. free(f_lock);
  148. return ;
  149. }

测试结果如下所示:

4、参考资料

http://blog.csdn.net/linyt/article/details/5764312

http://en.wikipedia.org/wiki/Circular_buffer

http://yiphon.diandian.com/post/2011-09-10/4918347

linux内核数据结构之kfifo的更多相关文章

  1. linux内核数据结构之kfifo【转】

    1.前言 最近项目中用到一个环形缓冲区(ring buffer),代码是由linux内核的kfifo改过来的.缓冲区在文件系统中经常用到,通过缓冲区缓解cpu读写内存和读写磁盘的速度.例如一个进程A产 ...

  2. Linux内核数据结构之kfifo详解

    本文分析的原代码版本: 2.6.24.4 kfifo的定义文件: kernel/kfifo.c kfifo的头文件: include/linux/kfifo.h kfifo是内核里面的一个First ...

  3. linux内核数据结构之链表

    linux内核数据结构之链表 1.前言 最近写代码需用到链表结构,正好公共库有关于链表的.第一眼看时,觉得有点新鲜,和我之前见到的链表结构不一样,只有前驱和后继指针,而没有数据域.后来看代码注释发现该 ...

  4. Linux内核结构体--kfifo 环状缓冲区

    转载链接:http://blog.csdn.net/yusiguyuan/article/details/41985907 1.前言 最近项目中用到一个环形缓冲区(ring buffer),代码是由L ...

  5. Linux 内核数据结构:Linux 双向链表

    Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...

  6. Linux 内核数据结构:双向链表

    Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...

  7. linux内核数据结构学习总结

    目录 . 进程相关数据结构 ) struct task_struct ) struct cred ) struct pid_link ) struct pid ) struct signal_stru ...

  8. linux内核数据结构--进程相关

    linux里面,有一个结构体task_struct,也叫“进程描述符”的数据结构,它包含了与进程相关的所有信息,它非常复杂,每一个字段都可能与一个功能相关,所以大部分细节不在我的研究范围之内,在这篇文 ...

  9. linux内核数据结构之链表【转】

    转自:http://www.cnblogs.com/Anker/p/3475643.html 1.前言 最近写代码需用到链表结构,正好公共库有关于链表的.第一眼看时,觉得有点新鲜,和我之前见到的链表结 ...

随机推荐

  1. [Java 缓存] Java Cache之 DCache的简单应用.

    前言 上次总结了下本地缓存Guava Cache的简单应用, 这次来继续说下项目中使用的DCache的简单使用. 这里分为几部分进行总结, 1)DCache介绍; 2)DCache配置及使用; 3)使 ...

  2. .net erp(办公oa)开发平台架构概要说明之表单设计器

    背景:搭建一个适合公司erp业务的开发平台.   架构概要图: 表单设计开发部署示例图    表单设计开发部署示例说明1)每个开发人员可以自己部署表单设计至本地一份(当然也可以共用一套开发环境,但是如 ...

  3. TypeScript为Zepto编写LazyLoad插件

    平时项目中使用的全部是jQuery框架,但是对于做webapp来说jQuery太过于庞大,当然你可以选择jQuery 2.*针对移动端的版本. 这里我采用移动端使用率比较多的zepto框架,他跟jqu ...

  4. 深入.NET平台和C#编程总结大全

    对于初学者的你,等到你把这个看完之后就更清楚地认知.NET和C#编程了,好了废话不多说,开始吧!                                                     ...

  5. 2Sum

    用哈希表(unordered_map)使得时间复杂度从O(n*n)降到O(n),空间复杂度从O(1)增到O(n):一边找一边插入哈希表 注意 在C++11以前要使用unordered_map需要 #i ...

  6. Android中点击事件的实现方式

    在之前博文中多次使用了点击事件的处理实现,有朋友就问了,发现了很多按钮的点击实现,但有很多博文中使用的实现方式有都不一样,到底是怎么回事.今天我们就汇总一下点击事件的实现方式. 点击事件的实现大致分为 ...

  7. git基本操作

    一.在Windows平台上安装Git,可以下载一个msysGit的安装包,点击exe即可安装运行.安装包下载地址:https://git-for-windows.github.io/备注:git命令行 ...

  8. 我的MYSQL学习心得(十二) 触发器

    我的MYSQL学习心得(十二) 触发器 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数 ...

  9. 关于apue.3e中apue.h的使用

    关于apue.3e中apue.h的使用 近来要学一遍APUE第三版,并于此开博做为记录. 先下载源文件: # url: http://http//www.apuebook.com/code3e.htm ...

  10. AngularJS过滤器filter-时间日期格式-渲染日期格式-$filter

    今天遇到了这些问题索性就 写篇文章吧 话不多说直接上栗子 不管任何是HTML格式还是JS格式必须要在  controller 里面写 // new Date() 获取当前时间 yyyy-MM-ddd ...