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

简介: 转自:http://www.cnblogs.com/Anker/p/3481373.html 1、前言   最近项目中用到一个环形缓冲区(ring buffer),代码是由linux内核的kfifo改过来的。

转自:http://www.cnblogs.com/Anker/p/3481373.html

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的数据结构:

复制代码
struct kfifo {
    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) */ spinlock_t *lock; /* protects concurrent modifications */ };
复制代码

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;  6 /* size must be a power of 2 */  7 BUG_ON(!is_power_of_2(size));  9 fifo = kmalloc(sizeof(struct kfifo), gfp_mask); 10 if (!fifo) 11 return ERR_PTR(-ENOMEM); 13 fifo->buffer = buffer; 14 fifo->size = size; 15 fifo->in = fifo->out = 0; 16 fifo->lock = lock; 17 18 return fifo; 19 } 20 struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock) 21 { 22 unsigned char *buffer; 23 struct kfifo *ret; 29 if (!is_power_of_2(size)) { 30 BUG_ON(size > 0x80000000); 31 size = roundup_pow_of_two(size); 32  } 34 buffer = kmalloc(size, gfp_mask); 35 if (!buffer) 36 return ERR_PTR(-ENOMEM); 38 ret = kfifo_init(buffer, size, gfp_mask, lock); 39 40 if (IS_ERR(ret)) 41  kfree(buffer); 43 return ret; 44 }
复制代码

  在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 = 0; 22 spin_unlock_irqrestore(fifo->lock, flags); 23 return ret; 24 } 25 26 27 unsigned int __kfifo_put(struct kfifo *fifo, 28 const unsigned char *buffer, unsigned int len) 29 { 30 unsigned int l; 31 //buffer中空的长度 32 len = min(len, fifo->size - fifo->in + fifo->out); 34 /* 35  * Ensure that we sample the fifo->out index -before- we 36  * start putting bytes into the kfifo. 37 */ 39  smp_mb(); 41 /* first put the data starting from fifo->in to buffer end */ 42 l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); 43 memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l); 45 /* then put the rest (if any) at the beginning of the buffer */ 46 memcpy(fifo->buffer, buffer + l, len - l); 47 48 /* 49  * Ensure that we add the bytes to the kfifo -before- 50  * we update the fifo->in index. 51 */ 53  smp_wmb(); 55 fifo->in += len; //每次累加,到达最大值后溢出,自动转为0 57 return len; 58 } 59 60 unsigned int __kfifo_get(struct kfifo *fifo, 61 unsigned char *buffer, unsigned int len) 62 { 63 unsigned int l; 64 //有数据的缓冲区的长度 65 len = min(len, fifo->in - fifo->out); 67 /* 68  * Ensure that we sample the fifo->in index -before- we 69  * start removing bytes from the kfifo. 70 */ 72  smp_rmb(); 74 /* first get the data from fifo->out until the end of the buffer */ 75 l = min(len, fifo->size - (fifo->out & (fifo->size - 1))); 76 memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l); 78 /* then get the rest (if any) from the beginning of the buffer */ 79 memcpy(buffer + l, fifo->buffer, len - l); 81 /* 82 * Ensure that we remove the bytes from the kfifo -before- 83 * we update the fifo->out index. 84 */ 86 smp_mb(); 88 fifo->out += len; //每次累加,到达最大值后溢出,自动转为0 90 return len; 91 }
复制代码

  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, 0, sizeof(struct ring_buffer));  47 ring_buf->buffer = buffer;  48 ring_buf->size = size;  49 ring_buf->in = 0;  50 ring_buf->out = 0;  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 = 0; 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 - 1))); 83 memcpy(buffer, ring_buf->buffer + (ring_buf->out & (ring_buf->size - 1)), 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 = 0; 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 - 1))); 97 memcpy(ring_buf->buffer + (ring_buf->in & (ring_buf->size - 1)), 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 = 0; 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 = 0; 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  19 void print_student_info(const student_info *stu_info)  20 {  21  assert(stu_info);  22 printf("id:%lu\t",stu_info->stu_id);  23 printf("age:%u\t",stu_info->age);  24 printf("score:%u\n",stu_info->score);  25 }  26  27 student_info * get_student_info(time_t timer)  28 {  29 student_info *stu_info = (student_info *)malloc(sizeof(student_info));  30 if (!stu_info)  31  {  32 fprintf(stderr, "Failed to malloc memory.\n");  33 return NULL;  34  }  35  srand(timer);  36 stu_info->stu_id = 10000 + rand() % 9999;  37 stu_info->age = rand() % 30;  38 stu_info->score = rand() % 101;  39  print_student_info(stu_info);  40 return stu_info;  41 }  42  43 void * consumer_proc(void *arg)  44 {  45 struct ring_buffer *ring_buf = (struct ring_buffer *)arg;  46  student_info stu_info;  47 while(1)  48  {  49 sleep(2);  50 printf("------------------------------------------\n");  51 printf("get a student info from ring buffer.\n");  52 ring_buffer_get(ring_buf, (void *)&stu_info, sizeof(student_info));  53 printf("ring buffer length: %u\n", ring_buffer_len(ring_buf));  54 print_student_info(&stu_info);  55 printf("------------------------------------------\n");  56  }  57 return (void *)ring_buf;  58 }  59  60 void * producer_proc(void *arg)  61 {  62  time_t cur_time;  63 struct ring_buffer *ring_buf = (struct ring_buffer *)arg;  64 while(1)  65  {  66 time(&cur_time);  67  srand(cur_time);  68 int seed = rand() % 11111;  69 printf("******************************************\n");  70 student_info *stu_info = get_student_info(cur_time + seed);  71 printf("put a student info to ring buffer.\n"); 72 ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info)); 73 printf("ring buffer length: %u\n", ring_buffer_len(ring_buf)); 74 printf("******************************************\n"); 75 sleep(1); 76 } 77 return (void *)ring_buf; 78 } 79 80 int consumer_thread(void *arg) 81 { 82 int err; 83 pthread_t tid; 84 err = pthread_create(&tid, NULL, consumer_proc, arg); 85 if (err != 0) 86 { 87 fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n", 88 errno, strerror(errno)); 89 return -1; 90 } 91 return tid; 92 } 93 int producer_thread(void *arg) 94 { 95 int err; 96 pthread_t tid; 97 err = pthread_create(&tid, NULL, producer_proc, arg); 98 if (err != 0) 99 { 100 fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n", 101 errno, strerror(errno)); 102 return -1; 103 } 104 return tid; 105 } 106 107 108 int main() 109 { 110 void * buffer = NULL; 111 uint32_t size = 0; 112 struct ring_buffer *ring_buf = NULL; 113 pthread_t consume_pid, produce_pid; 114 115 pthread_mutex_t *f_lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); 116 if (pthread_mutex_init(f_lock, NULL) != 0) 117 { 118 fprintf(stderr, "Failed init mutex,errno:%u,reason:%s\n", 119 errno, strerror(errno)); 120 return -1; 121 } 122 buffer = (void *)malloc(BUFFER_SIZE); 123 if (!buffer) 124 { 125 fprintf(stderr, "Failed to malloc memory.\n"); 126 return -1; 127 } 128 size = BUFFER_SIZE; 129 ring_buf = ring_buffer_init(buffer, size, f_lock); 130 if (!ring_buf) 131 { 132 fprintf(stderr, "Failed to init ring buffer.\n"); 133 return -1; 134 } 135 #if 0 136 student_info *stu_info = get_student_info(638946124); 137 ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info)); 138 stu_info = get_student_info(976686464); 139 ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info)); 140 ring_buffer_get(ring_buf, (void *)stu_info, sizeof(student_info)); 141 print_student_info(stu_info); 142 #endif 143 printf("multi thread test.......\n"); 144 produce_pid = producer_thread((void*)ring_buf); 145 consume_pid = consumer_thread((void*)ring_buf); 146 pthread_join(produce_pid, NULL); 147 pthread_join(consume_pid, NULL); 148 ring_buffer_free(ring_buf); 149 free(f_lock); 150 return 0; 151 }
复制代码

测试结果如下所示:

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

冷静思考,勇敢面对,把握未来!
【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
目录
相关文章
|
3天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
21 4
|
7天前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
28 6
|
6天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
27 9
|
5天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
23 6
|
6天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
22 5
|
6天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
7天前
|
存储 监控 安全
Linux内核调优的艺术:从基础到高级###
本文深入探讨了Linux操作系统的心脏——内核的调优方法。文章首先概述了Linux内核的基本结构与工作原理,随后详细阐述了内核调优的重要性及基本原则。通过具体的参数调整示例(如sysctl、/proc/sys目录中的设置),文章展示了如何根据实际应用场景优化系统性能,包括提升CPU利用率、内存管理效率以及I/O性能等关键方面。最后,介绍了一些高级工具和技术,如perf、eBPF和SystemTap,用于更深层次的性能分析和问题定位。本文旨在为系统管理员和高级用户提供实用的内核调优策略,以最大化Linux系统的效率和稳定性。 ###
|
6天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
5天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
22 2
|
8天前
|
监控 网络协议 算法
Linux内核优化:提升系统性能与稳定性的策略####
本文深入探讨了Linux操作系统内核的优化策略,旨在通过一系列技术手段和最佳实践,显著提升系统的性能、响应速度及稳定性。文章首先概述了Linux内核的核心组件及其在系统中的作用,随后详细阐述了内存管理、进程调度、文件系统优化、网络栈调整及并发控制等关键领域的优化方法。通过实际案例分析,展示了这些优化措施如何有效减少延迟、提高吞吐量,并增强系统的整体健壮性。最终,文章强调了持续监控、定期更新及合理配置对于维持Linux系统长期高效运行的重要性。 ####