Linux内核数据结构kfifo详解(3)

16行,前面讲到fifo->size已经2的次幂圆整,而且kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1),所以fifo->size - (fifo->in & (fifo->size - 1)) 即位 fifo->in 到 buffer末尾所剩余的长度,l取len和剩余长度的最小值,即为需要拷贝l 字节到fifo->buffer + fifo->in的位置上。

17行,拷贝l 字节到fifo->buffer + fifo->in的位置上,如果l = len,则已拷贝完成,第20行len – l 为0,将不执行,如果l = fifo->size - (fifo->in & (fifo->size - 1)) ,则第20行还需要把剩下的 len – l 长度拷贝到buffer的头部。

27行,加写内存屏障,保证in 加之前,memcpy的字节已经全部写入buffer,如果不加内存屏障,可能数据还没写完,另一个CPU就来读数据,读到的缓冲区内的数据不完全,因为读数据是通过 in – out 来判断的。

29行,注意这里 只是用了 fifo->in +=  len而未取模,这就是kfifo的设计精妙之处,这里用到了unsigned int的溢出性质,当in 持续增加到溢出时又会被置为0,这样就节省了每次in向前增加都要取模的性能,锱铢必较,精益求精,让人不得不佩服。

 

__kfifo_get是出队操作,它从buffer中取出数据,然后移动out的位置,其源代码如下:

1: unsigned int __kfifo_get(struct kfifo *fifo, 2:              unsigned char *buffer, unsigned int len) 3: { 4:    unsigned int l; 5:  6:    len = min(len, fifo->in - fifo->out); 7:  8:    /* 9:     * Ensure that we sample the fifo->in index -before- we 10:     * start removing bytes from the kfifo. 11:     */ 12:  13:    smp_rmb(); 14:  15:    /* first get the data from fifo->out until the end of the buffer */ 16:    l = min(len, fifo->size - (fifo->out & (fifo->size - 1))); 17:    memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l); 18:  19:    /* then get the rest (if any) from the beginning of the buffer */ 20:    memcpy(buffer + l, fifo->buffer, len - l); 21:  22:    /* 23:     * Ensure that we remove the bytes from the kfifo -before- 24:     * we update the fifo->out index. 25:     */ 26:  27:    smp_mb(); 28:  29:    fifo->out += len; 30:  31:    return len; 32: }

6行,可去读的长度为fifo->in – fifo->out,让读的长度取len和剩余容量中较小的,避免读越界;

13行,加读内存屏障,保证在开始取数据之前,fifo->in取到正确的值(另一个CPU可能正在改写in值)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/28d39759dbcdb0e55d273fcdd41171d4.html