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值)