linux内核数据结构之kfifo

简介:

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 }
复制代码

测试结果如下所示:

相关文章
|
15天前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
15天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
16天前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
16天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
18天前
|
负载均衡 算法 Linux
深入探索Linux内核调度机制:公平与效率的平衡####
本文旨在剖析Linux操作系统内核中的进程调度机制,特别是其如何通过CFS(完全公平调度器)算法实现多任务环境下资源分配的公平性与系统响应速度之间的微妙平衡。不同于传统摘要的概览性质,本文摘要将直接聚焦于CFS的核心原理、设计目标及面临的挑战,为读者揭开Linux高效调度的秘密。 ####
32 3
|
21天前
|
负载均衡 算法 Linux
深入探索Linux内核调度器:公平与效率的平衡####
本文通过剖析Linux内核调度器的工作机制,揭示了其在多任务处理环境中如何实现时间片轮转、优先级调整及完全公平调度算法(CFS),以达到既公平又高效地分配CPU资源的目标。通过对比FIFO和RR等传统调度策略,本文展示了Linux调度器如何在复杂的计算场景下优化性能,为系统设计师和开发者提供了宝贵的设计思路。 ####
33 6
|
20天前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
22天前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
29 2
|
22天前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
45 1
|
22天前
|
算法 前端开发 Linux
深入理解Linux内核调度器:CFS与实时性的平衡####
本文旨在探讨Linux操作系统的核心组件之一——完全公平调度器(CFS)的工作原理,分析其在多任务处理环境中如何实现进程间的公平调度,并进一步讨论Linux对于实时性需求的支持策略。不同于传统摘要仅概述内容要点,本部分将简要预览CFS的设计哲学、核心算法以及它是如何通过红黑树数据结构来维护进程执行顺序,同时触及Linux内核为满足不同应用场景下的实时性要求而做出的权衡与优化。 ####
下一篇
DataWorks