Non-lock queues are becoming more and more popular, the use of different lock-free queues in specific situations can save lock overhead and improve program efficiency.
The implementation of a lock-free queue in the Linux kernel is concise and not simple. The core judgment part utilizes the integer overflow mechanism, this has many articles specially introduced, we did not elaborate.
Inside the note is very detailed, directly to Kfifo source code, we look at the source code comments should be able to understand. The source code is implemented on Linux, in order to cross the platform, to increase the implementation of other platforms.
#include <stdio.h> #include <stdlib.h> #include <string.h> #define __U32 unsigned long #define __U64 UN Signed Long long #if defined (__gnuc__) #define MIN (x,y) ({\ typeof (x) _x = (x); \ typeof (Y) _y = (y); \ (void) (&_x = = &_y); \ _x < _y? _x: _y; #define MAX (x,y) ({\ typeof (x) _x = (x); \ typeof (Y) _y = (y); \ (void) (&_x = = &_y ); \ _x > _y? _x: _y; #else #define MAX (A,B) ((a) > (b))? (a): (b) #define MIN (a,b) ((a) < (b))? (a): (b)) #endif #define Max_kfifo_size 0x1000 struct Kfifo {unsigned char *buffer;/* The buffer holding the DAT A */unsigned int size; /* The size of the allocated buffer */unsigned int in; /* The data is added at offset (in% size) */unsigned int out; /* The data is extracted from off.
(out% size) */}; /** * Fls-find last bit set * @x:the Word to search * * This is defined the sameWay as FFS: *-return 32..1 to indicate bit 31..0 most significant bit set *-return 0 to indicate no bits set */#if
Defined (__gnuc__) static inline int FLs (int x) {int r;
__asm__ ("Bsrl%1,%0\n\t" "Jnz 1f\n\t" "Movl $-1,%0\n" "1:": "=r" (R): "RM" (x));
return r+1;
#else static inline int FLs (int x) {int position;
int i;
if (0!= x) {for (i = (x >> 1), position = 0; I!= 0; ++position) I >>= 1;
else {position =-1;
return position+1; #endif/** * Fls64-find last bit set in a 64-bit value * @n:the value to search * * This is defined the same way As FFS: *-return 64..1 to indicate bit 63..0 most significant bit set *-return 0 to indicate no bits set */static
inline int Fls64 (__u64 x) {__u32 h = x >> 32;
if (h) return FLs (h) + 32;
return FLs (x);
Static inline unsigned fls_long (unsigned long L) {if (sizeof (l) = = 4) return FLs (L); RetUrn Fls64 (l);
Static inline unsigned long roundup_pow_of_two (unsigned long x) {return 1UL << Fls_long (x-1);} /** * * Kfifo_alloc-allocates a new FIFO and its internal buffer * * @size: The size of the internal buffer to is Allo
cated. * * @gfp_mask: Get_free_pages mask, passed to Kmalloc () * * @lock: The lock to is used to protect the FIFO buffer * * *
* The size is rounded-up to a power of 2.
* */struct KFIFO *kfifo_alloc (unsigned int size) {unsigned char *buffer;
struct KFIFO *fifo; * * * Round up to the next power of 2, since we ' let the indices * * wrap ' tachnique work
s only in the case.
* */if (size & (size-1)) {if (Size > 0x80000000);
return NULL;
Size = roundup_pow_of_two (size);
Buffer = (unsigned char *) malloc (size);
if (!buffer) return NULL; FIFO = (struct KFifo*) malloc (sizeof (struct kfifo));
if (!fifo) {free (buffer);
return NULL;
} fifo->buffer = buffer;
fifo->size = size;
Fifo->in = fifo->out = 0;
return FIFO;
/** * * Kfifo_free-frees the FIFO * * @fifo: The FIFO to is freed.
* */void Kfifo_free (struct Kfifo *fifo) {free (fifo->buffer);
Free (FIFO);
/** * __kfifo_put-puts some data into the FIFO, no locking version * @fifo: The FIFO to be used.
* @buffer: The data to be added.
* @len: The length of the data to be added. * This function is copies at most @len bytes the "@buffer into *" The FIFO depending on the free spaces, and returns the
Number of * bytes copied. * * * * and only one concurrent reader and one concurrent * writer, you don ' t need extra locking
ions.
* * unsigned int __kfifo_put (struct Kfifo *fifo, const unsigned char *buffer, unsigned int len) {
unsigned int l; len = min (len, fifo->size-fifo->in + fifo->out); /* The data starting from Fifo->in to buffer end */L = min (len, Fifo->size-(Fifo->in & (
fifo->size-1)));
memcpy (Fifo->buffer + (Fifo->in & (fifo->size-1)), buffer, L);
/* Then put the rest (if any) at the beginning of the buffer */memcpy (fifo->buffer, buffer + L, len-l);
Fifo->in = Len;
return Len;
}/** * __kfifo_get-gets some data from the FIFO, no locking version * @fifo: The FIFO to be used.
* @buffer: Where the data must be copied.
* @len: The size of the destination buffer.
* This function is copies at most @len bytes to the * @buffer and returns the number of copied bytes. * * * * and only one concurrent reader and one concurrent * writer, you don ' t need extra locking
ions. * * unsigned int __kfifo_get (struct Kfifo *fifo, unsigned char *buffer, unsigned int len) {unsigned int l;
len = min (len, fifo->in-fifo->out); /* The the data from Fifo->out until the "end of" the buffer */L = min (len, fifo->size-Fifo->out
& (Fifo->size-1));
memcpy (buffer, Fifo->buffer + (Fifo->out & (Fifo->size-1)), L);
/* Then get the rest (if no) from the beginning of the buffer */memcpy (buffer + L, Fifo->buffer, len-l);
Fifo->out = Len;
return Len;
}/** * __kfifo_reset-removes the entire FIFO contents, no locking version * @fifo: The FIFO to be emptied.
*/static inline void __kfifo_reset (struct Kfifo *fifo) {fifo->in = Fifo->out = 0;}
/** * __kfifo_len-returns The number of bytes available in the FIFO, no locking version * @fifo: The FIFO to be used. */static inline unsigned int __kfifo_len (struct Kfifo *fifo) {return fifo->in-fifo->out;}
The section used has written a class that incorporates template encapsulation and provides a way to use template type access.
Template <typename t> class Zfifo { private: kfifo* _kfifo; Public: ZFIFO () { _KFIFO
= Kfifo_alloc (max_kfifo_size); } ~ZFIFO () { &NBS P
if (NULL!= _kfifo) Kfifo_free (_KFIFO);
} BOOL Push (T data);
T get ();
}; Template <typename t> bool zfifo<t>::p ush (T data) { int len = 0; len = __kfifo_put
(_kfifo, (const unsigned char *) &data, sizeof (T));
if (len > 0) return true;
Else return false; } template <typename t> T zfifo<t>::get () { T data;
int len = __kfifo_get (_kfifo, (unsigned char *) &data, sizeof (T));
if (len > 0) return data;
Else return NULL; }
This library is generally used in places where the need for efficient processing, in order to reduce memory copies, generally use the form of pointers to operate. A simple example of use:
int main ()
{
zfifo<int*> ZF;
int a = 1;
Zf.push (&a);
printf ("a=%d\n", &a);
int* B = NULL;
b = Zf.get ();
printf ("b=%d\n", b);
return 0;
}
Places to look for:
1. The data is thread-safe when only one thread is responsible for reading and another thread is responsible for writing. The above implementation is based on this principle, when multiple threads read or multiple threads write, do not guarantee the correctness of the data.
So when used, one thread writes, one thread reads. Network applications are more commonly used, is to open a thread interface data, and then write data to the queue. A dispatch thread is then opened to read the network data and then distributed to the processing thread.
2. Data length The default macro defines a length that exceeds this length, and subsequent data is written to fail.