kfifo无锁队列分析

来源:互联网 发布:ubuntu虚拟机怎么联网 编辑:程序博客网 时间:2024/05/21 09:20

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

1. 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 */ };

 

buffer, 用于存放数据的缓存

size,      buffer空间的大小,在初化时,将它向上扩展成2的幂

lock,      如果使用不能保证任何时间最多只有一个读线程和写线程,需要使用该lock实施同步。

in, out,  和buffer一起构成一个循环队列。 in指向buffer中队头,而且out指向buffer中的队尾,它的结构如示图如下:

 

2. kfifo_alloc 分配kfifo内存和初始化工作

struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock) {     unsigned char *buffer;     struct kfifo *ret;     /*      * round up to the next power of 2, since our 'let the indices      * wrap' tachnique works only in this case.      */     if (size & (size - 1)) {         BUG_ON(size > 0x80000000);         size = roundup_pow_of_two(size);     }     buffer = kmalloc(size, gfp_mask);     if (!buffer)         return ERR_PTR(-ENOMEM);     ret = kfifo_init(buffer, size, gfp_mask, lock);     if (IS_ERR(ret))         kfree(buffer);     return ret; } 


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

kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1),这里需要注意:kfifo->size需要大于kfifo->in。

 

3. __kfifo_put和__kfifo_get,巧妙的入队和出队操作,无锁并发

unsigned int __kfifo_put(struct kfifo *fifo,              unsigned char *buffer, unsigned int len) {     unsigned int l;     len = min(len, fifo->size - fifo->in + fifo->out);  //min(参数长度,未使用的队列长度)    /*      * Ensure that we sample the fifo->out index -before- we      * start putting bytes into the kfifo.      */     smp_mb();     /* first put 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);     /*      * Ensure that we add the bytes to the kfifo -before-      * we update the fifo->in index.      */     smp_wmb();     fifo->in += len;     return len; }unsigned int __kfifo_get(struct kfifo *fifo,              unsigned char *buffer, unsigned int len) {     unsigned int l;     len = min(len, fifo->in - fifo->out);     /*      * Ensure that we sample the fifo->in index -before- we      * start removing bytes from the kfifo.      */     smp_rmb();     /* first get 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 any) from the beginning of the buffer */     memcpy(buffer + l, fifo->buffer, len - l);     /*      * Ensure that we remove the bytes from the kfifo -before-      * we update the fifo->out index.      */     smp_mb();     fifo->out += len;     return len; } 


__kfifo_put是入队操作,它先将数据放入buffer里面,最后才修改in参数;__kfifo_get是出队操作,它先将数据从buffer中移走,最后才修改out。

 

优化屏障和内存屏障

 http://blog.csdn.net/xujianqun/article/details/7800813

 

问题:关键在于锁,比方说put正在改fifo->in的值,但是此时get在用这个值作判断,这时保证不会出错吗?



重磅:无锁编程系统讲解:

http://blog.csdn.net/linux_bug/article/category/5825885


简明代码:

http://blog.csdn.net/linux_bug/article/details/48653851

 

0 0
原创粉丝点击