kfifo无锁队列分析

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值