/**<br />* kfifo_init - allocates a new FIFO using a preallocated buffer<br />* @buffer: the preallocated buffer to be used.<br />* @size: the size of the internal buffer, this have to be a power of 2.<br />* @gfp_mask: get_free_pages mask, passed to kmalloc()<br />* @lock: the lock to be used to protect the fifo buffer<br />*<br />* Do NOT pass the kfifo to kfifo_free() after use! Simply free the<br />* &struct kfifo with kfree().<br />*/<br />struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,<br /> gfp_t gfp_mask, spinlock_t *lock)<br />{<br /> struct kfifo *fifo;<br /><br /> /* size must be a power of 2 */<br /> BUG_ON(size & (size - 1)); //大小必须为2的k次方(k>0)的目的在于put/get中从虚拟索引计算真实索引,size & (size - 1)是常用判断技巧<br /><br /><br /> fifo = kmalloc(sizeof(struct kfifo), gfp_mask); //分配kfifo数据结构<br /><br /> if (!fifo)<br /> return ERR_PTR(-ENOMEM);<br /><br /> fifo->buffer = buffer;<br /> fifo->size = size;<br /> fifo->in = fifo->out = 0; //当fifo->in == fifo->out 时,表示空队列<br /><br /> fifo->lock = lock;<br /><br /> return fifo;<br />}<br /><br /><br />/**<br />* kfifo_alloc - allocates a new FIFO and its internal buffer<br />* @size: the size of the internal buffer to be allocated.<br />* @gfp_mask: get_free_pages mask, passed to kmalloc()<br />* @lock: the lock to be used to protect the fifo buffer<br />*<br />* The size will be rounded-up to a power of 2.<br />*/<br />//通过调用kfifo_alloc分配队列空间,该函数会调用kfifo_init初始化kfifo结构体,并调整size的大小以适应运算<br /><br />struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock)<br />{<br /> unsigned char *buffer;<br /> struct kfifo *ret;<br /><br /> /*<br /> * round up to the next power of 2, since our 'let the indices<br /> * wrap' tachnique works only in this case.<br /> */<br /> if (size & (size - 1)) { //如果size不是2的k次方,代码将size调整最近的2^k次方附近<br /><br /> BUG_ON(size > 0x80000000);<br /> size = roundup_pow_of_two(size);<br /> }<br /><br /> buffer = kmalloc(size, gfp_mask);<br /> if (!buffer)<br /> return ERR_PTR(-ENOMEM);<br /><br /> ret = kfifo_init(buffer, size, gfp_mask, lock);<br /><br /> if (IS_ERR(ret))<br /> kfree(buffer);<br /><br /> return ret;<br />}<br /><br />/**<br />* __kfifo_put - puts some data into the FIFO, no locking version<br />* @fifo: the fifo to be used.<br />* @buffer: the data to be added.<br />* @len: the length of the data to be added.<br />*<br />* This function copies at most @len bytes from the @buffer into<br />* the FIFO depending on the free space, and returns the number of<br />* bytes copied.<br />*<br />* Note that with only one concurrent reader and one concurrent<br />* writer, you don't need extra locking to use these functions.<br />*/<br />unsigned int __kfifo_put(struct kfifo *fifo,<br /> unsigned char *buffer, unsigned int len)<br />{<br /> unsigned int l;<br /><br /> //fifo->size - fifo->in + fifo->out,这段代码计算空闲的空间<br /><br /> //in是写索引,out是读索引,而且put与get操作都是分别增加in与out的值来重新计算虚拟索引<br /><br /> //注意,out 始终不会大于 in,(in - out)是有效数据空间大小,size是总空间的大小<br /><br /> //那么空闲的空间大小就是 size - (int - out)<br /><br /> //如果请求的len大于空闲空间,就使len = size - (int - out)<br /><br /> len = min(len, fifo->size - fifo->in + fifo->out); <br /><br /> /*<br /> * Ensure that we sample the fifo->out index -before- we<br /> * start putting bytes into the kfifo.<br /> */<br /><br /> smp_mb();<br /><br /> /* first put the data starting from fifo->in to buffer end */<br /> //(fifo->in & (fifo->size - 1))这段代码计算真实的写索引偏移,笔者假设为real_in<br /><br /> //这是因为in在每次调用put之后都会增加一个len的长度<br /><br /> //由于fifo->size必定是2的k次方,而(fifo->size - 1)就是类似0x00FFFFF的值<br /><br /> //(fifo->in & (fifo->size - 1))的操作从数学角度将就是对长度fifo->size的取模运算<br /><br /> //这里能用AND运算代替取模运算得益于前面申请的空间大小为2^k次方<br /><br /> //l = min(空闲空间大小,从real_in开始到缓冲区结尾的空间)<br /><br /> l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));<br /><br /> //先从buffer中拷贝l字节到缓冲区剩余空间,l<=len,也<=从real_in开始到缓冲区结尾的空间<br /><br /> //所以这个copy可能没拷贝完,但是不会造成缓冲区越界<br /><br /> memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);<br /><br /> /* then put the rest (if any) at the beginning of the buffer */<br /> //当len > l时,拷贝buffer中剩余的内容,其实地址当然为buffer + l,而剩余的大小为len - l<br /><br /> //当len == l时,下面的memcpy啥都不干,绝对精妙的算法<br /><br /> memcpy(fifo->buffer, buffer + l, len - l);<br /><br /> /*<br /> * Ensure that we add the bytes to the kfifo -before-<br /> * we update the fifo->in index.<br /> */<br /><br /> smp_wmb();<br /><br /> //更新in(写者)的逻辑索引<br /><br /> fifo->in += len;<br /><br /> return len;<br />}<br /><br />/**<br />* __kfifo_get - gets some data from the FIFO, no locking version<br />* @fifo: the fifo to be used.<br />* @buffer: where the data must be copied.<br />* @len: the size of the destination buffer.<br />*<br />* This function copies at most @len bytes from the FIFO into the<br />* @buffer and returns the number of copied bytes.<br />*<br />* Note that with only one concurrent reader and one concurrent<br />* writer, you don't need extra locking to use these functions.<br />*/<br />unsigned int __kfifo_get(struct kfifo *fifo,<br /> unsigned char *buffer, unsigned int len)<br />{<br /> unsigned int l;<br /> //读取的大小不能超过有效空间长度<br /><br /> //经过min运算后len <= 请求的空间len, len <= size<br /><br /> len = min(len, fifo->in - fifo->out);<br /><br /> /*<br /> * Ensure that we sample the fifo->in index -before- we<br /> * start removing bytes from the kfifo.<br /> */<br /><br /> smp_rmb();<br /><br /> /* first get the data from fifo->out until the end of the buffer */<br /> //同理,fifo->out & (fifo->size - 1)等于out(读者)的虚拟索引计算出来真实索引real_out<br /><br /> //fifo->size - real_out就等于该索引到缓冲区尾部的空间大小<br /><br /> //经过min运算后,l<=len,l<=real_out至缓冲区尾部的空间大小<br /><br /> l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));<br /><br /> //从real_out开始拷贝l字节内容到buffer中<br /><br /> memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);<br /><br /> /* then get the rest (if any) from the beginning of the buffer */<br /> //如果l<len,那么从fifo->buffer的首部开始继续拷贝剩下的内容<br /><br /> //如果l == len,memcpy啥都不干<br /><br /> memcpy(buffer + l, fifo->buffer, len - l);<br /><br /> /*<br /> * Ensure that we remove the bytes from the kfifo -before-<br /> * we update the fifo->out index.<br /> */<br /><br /> smp_mb();<br /><br /> //更新out(读者)的虚拟索引<br /><br /> fifo->out += len;<br /><br /> return len;<br />}<br /> <br /><br /><br /> 从上面几个重要的函数可以看出一些特性,就是put函数能放入的数据长度永远不会大于缓冲区的长度,而fifo->in - fifo->out永远小于等于size。<br />get函数得到的数据永远小于等于size(这个是必然的)。<br /> |
|