• 转 Linux kernel 无锁队列


    1. struct kfifo {   
    2.     unsigned char *buffer;    /* the buffer holding the data */   
    3.     unsigned int size;    /* the size of the allocated buffer */   
    4.     unsigned int in;    /* data is added at offset (in % size) */   
    5.     unsigned int out;    /* data is extracted from off. (out % size) */   
    6.     spinlock_t *lock;    /* protects concurrent modifications */   
    7. };
    1. struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock)   
    2. {   
    3.     unsigned char *buffer;   
    4.     struct kfifo *ret;   
    5.   
    6.     /*  
    7.      * round up to the next power of 2, since our 'let the indices  
    8.      * wrap' tachnique works only in this case.  
    9.      */   
    10.     if (size & (size - 1)) {   
    11.         BUG_ON(size > 0x80000000);   
    12.         size = roundup_pow_of_two(size);   
    13.     }   
    14.   
    15.     buffer = kmalloc(size, gfp_mask);   
    16.     if (!buffer)   
    17.         return ERR_PTR(-ENOMEM);   
    18.   
    19.     ret = kfifo_init(buffer, size, gfp_mask, lock);   
    20.   
    21.     if (IS_ERR(ret))   
    22.         kfree(buffer);   
    23.   
    24.     return ret;   
    25. }   

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

    kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1)

    在kfifo_alloc函数中,使用size & (size – 1)来判断size 是否为2幂,如果条件为真,则表示size不是2的幂,然后调用roundup_pow_of_two将之向上扩展为2的幂。 这些都是很常用的技巧,只不过大家没有将它们结合起来使用而已,下面要分析的__kfifo_put和__kfifo_get则是将kfifo->size的特点发挥到了极致。

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

    __kfifo_put是入队操作,它先将数据放入buffer里面,最后才修改in参数;__kfifo_get是出队操作,它先将数据从buffer中移走,最后才修改out。你会发现in和out两者各司其职。计算机科学家已经证明,当只有一个读经程和一个写线程并发操作时,不需要任何额外的锁,就可以确保是线程安全的,也即kfifo使用了无锁编程技术,以提高kernel的并发。

    下面是__kfifo_put和__kfifo_get的代码

    1. unsigned int __kfifo_put(struct kfifo *fifo,   
    2.              unsigned char *buffer, unsigned int len)   
    3. {   
    4.     unsigned int l;   
    5.   
    6.     len = min(len, fifo->size - fifo->in + fifo->out);   
    7.   
    8.     /*  
    9.      * Ensure that we sample the fifo->out index -before- we  
    10.      * start putting bytes into the kfifo.  
    11.      */   
    12.   
    13.     smp_mb();   
    14.   
    15.     /* first put the data starting from fifo->in to buffer end */   
    16.     l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));   
    17.     memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);   
    18.   
    19.     /* then put the rest (if any) at the beginning of the buffer */   
    20.     memcpy(fifo->buffer, buffer + l, len - l);   
    21.   
    22.     /*  
    23.      * Ensure that we add the bytes to the kfifo -before-  
    24.      * we update the fifo->in index.  
    25.      */   
    26.   
    27.     smp_wmb();   
    28.   
    29.     fifo->in += len;   
    30.   
    31.     return len;   
    32. }  
    33.   
    34. unsigned int __kfifo_get(struct kfifo *fifo,   
    35.              unsigned char *buffer, unsigned int len)   
    36. {   
    37.     unsigned int l;   
    38.   
    39.     len = min(len, fifo->in - fifo->out);   
    40.   
    41.     /*  
    42.      * Ensure that we sample the fifo->in index -before- we  
    43.      * start removing bytes from the kfifo.  
    44.      */   
    45.   
    46.     smp_rmb();   
    47.   
    48.     /* first get the data from fifo->out until the end of the buffer */   
    49.     l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));   
    50.     memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);   
    51.   
    52.     /* then get the rest (if any) from the beginning of the buffer */   
    53.     memcpy(buffer + l, fifo->buffer, len - l);   
    54.   
    55.     /*  
    56.      * Ensure that we remove the bytes from the kfifo -before-  
    57.      * we update the fifo->out index.  
    58.      */   
    59.   
    60.     smp_mb();   
    61.   
    62.     fifo->out += len;   
    63.   
    64.     return len;   
    65. }   
  • 相关阅读:
    FJNUOJ Yehan’s hole(容斥求路径数 + 逆元)题解
    FJNUOJ the greed of Yehan(最长路 + 权值乘积转化)题解
    BZOJ 2956 模积和
    BZOJ 2299 向量
    codeforces 718c Sasha and Array
    BZOJ 3747 Kinoman
    BZOJ 2431 逆序对数列
    BZOJ 3289 Mato的文件管理
    BZOJ 3781 小B的询问
    BZOJ 2038 小Z的袜子(hose)
  • 原文地址:https://www.cnblogs.com/happy-pm/p/3831530.html
Copyright © 2020-2023  润新知