1. Preface
A ring buffer was used in the recent project, and the code was changed by the Kfifo of the Linux kernel. Buffers are often used in the file system to alleviate the CPU's read and write memory and the speed of reading and writing disks through buffers. For example, when a process a produces data to another process B, process B needs to process the data passed on by processes and writes to the file, and if B is not finished, a delays sending. To ensure that process a reduces the wait time, a buffer can be used between A and B, and a each time the data is stored in the buffer and B is taken in each buffer. This is a typical producer and consumer model in which data in the buffer satisfies the FIFO feature and can therefore be implemented in a queue. The Linux kernel's kfifo is exactly a ring queue that can be used as a ring buffer. Producers and consumers use buffers as shown:
The detailed introduction of the ring buffer and the implementation method can refer to Http://en.wikipedia.org/wiki/Circular_buffer, introduced in very detailed, listed the implementation of the ring queue of several methods. The inconvenience of the ring queue is how to determine whether the queue is empty or full. There are three ways to implement this on Wikipedia.
2. Linux kernel Kfifo
The Kfifo design is very ingenious, the code is very concise, for the queue and out of the handling of the unexpected. First look at the data structure of 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 provides the following methods:
1 // Create a kfifo based on the given buffer
2 struct kfifo * kfifo_init (unsigned char * buffer, unsigned int size,
3 gfp_t gfp_mask, spinlock_t * lock);
4 // Assign buffer and kfifo for a given size
5 struct kfifo * kfifo_alloc (unsigned int size, gfp_t gfp_mask,
6 spinlock_t * lock);
7 // Free up kfifo space
8 void kfifo_free (struct kfifo * fifo)
9 // Add data to kfifo
10 unsigned int kfifo_put (struct kfifo * fifo,
11 const unsigned char * buffer, unsigned int len)
12 // take data from kfifo
13 unsigned int kfifo_put (struct kfifo * fifo,
14 const unsigned char * buffer, unsigned int len)
15 // Get buffer size of data in kfifo
16 unsigned int kfifo_len (struct kfifo * fifo)
Defines the purpose of the spin lock in order to prevent multi-process/thread concurrency using KFIFO. Because in and out change at each get and out. The source code for initializing and creating Kfifo is as follows:
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 }
In Kfifo_init and Kfifo_calloc, the value of Kfifo->size is always extended to a power of 2 on the basis of the size parameter passed in by the caller, which is the kernel's usual practice. The benefits are self-evident--the kfifo->size modulo operation can be translated into operations, such as:kfifo->in% kfifo->size can be converted to Kfifo->in & (kfifo->size– 1)
The clever thing about Kfifo is that in and out are defined as unsigned types, both in and out are incremented when put and get, and overflow when the maximum is reached, so that it is recycled from 0 onwards. The put and get codes are as follows:
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 // When fifo-> in == fifo-> out, buffer is empty
20 if (fifo-> in == fifo-> out)
21 fifo-> in = fifo-> out = 0;
22 spin_unlock_irqrestore (fifo-> lock, flags);
23 return ret;
twenty four }
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 empty length
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; // Every time it accumulates, it overflows after reaching the maximum value, it will automatically turn to 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 // length of buffer with data
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; // Every time it accumulates, it overflows after reaching the maximum value, and it automatically turns to 0
90 return len;
91}
Both put and get are locked during calls to the __put and __get procedures to prevent concurrency. You can see from the code that both put and get call two times memcpy, which is for boundary conditions. For example: Blue indicates idle, red indicates occupancy.
(1) Empty Kfifo,
(2) after put a buffer
(3) Get a buffer after
(4) When the buffer length of the put is beyond the length of the in to the end, then move the rest to the head
3. Test procedure
After writing a ring_buffer in Kfifo, the existing thread mutex is concurrency controlled. The ring_buffer of the design are as follows:
1 / ** @ brief imitates the ring buffer written by linux kfifo
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 // Determine whether x is a power of 2
17 #define is_power_of_2 (x) ((x)! = 0 && (((x) & ((x)-1)) == 0))
18 // Take the minimum of a and b
19 #define min (a, b) (((a) <(b))? (A): (b))
20
21 struct ring_buffer
twenty two {
23 void * buffer; // buffer
24 uint32_t size; // size
25 uint32_t in; // entry position
26 uint32_t out; // Exit position
27 pthread_mutex_t * f_lock; // mutex lock
28};
29 // initialize the buffer
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 // Free the buffer
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 // length of the buffer
70 uint32_t __ring_buffer_len (const struct ring_buffer * ring_buf)
71 {
72 return (ring_buf-> in-ring_buf-> out);
73}
74
75 // Retrieve data from the buffer
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 // Store data in the buffer
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 // No data in 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
Use multithreaded simulation for producers and consumers to write test programs as follows:
1 / ** @ brief ring buffer test program, create two threads, one producer and one consumer.
2 * The producer inputs data into the buffer every 1 second, and the consumer fetches data every 2 seconds.
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}
The test results are as follows:
4. References
http://blog.csdn.net/linyt/article/details/5764312
Http://en.wikipedia.org/wiki/Circular_buffer
http://yiphon.diandian.com/post/2011-09-10/4918347
Kfifo ring queue for the Linux kernel