<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop &#187; 队列</title>
	<atom:link href="https://zohead.com/archives/tag/queue/feed/" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>Linux kernel kfifo分析</title>
		<link>https://zohead.com/archives/linux-kernel-kfifo/</link>
		<comments>https://zohead.com/archives/linux-kernel-kfifo/#comments</comments>
		<pubDate>Mon, 11 Jun 2012 17:32:15 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[代码分析]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[FIFO]]></category>
		<category><![CDATA[kfifo]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[队列]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=226</guid>
		<description><![CDATA[本文同步自（如浏览不正常请点击跳转）：https://zohead.com/archives/linux-kernel-kfifo/ kfifo 是 Linux kernel 中的一个通用队列实现，对于 kernel 中常见的 FIFO 队列应用还是很有用的，本文主要简单介绍分析下 Linux kernel kfifo。实际上 ChinaUnix 上有个 kfifo 的分析文章，但已经比较老（基于 Linux 2.6.10），而且我现在用的 2.6.34 版本 kernel 中 kfifo 实现有很多改动，故自己简单写下，ChinaUnix 上的 kfifo 介绍帖子在这里： http://b [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（如浏览不正常请点击跳转）：<a href="https://zohead.com/archives/linux-kernel-kfifo/" target="_blank">https://zohead.com/archives/linux-kernel-kfifo/</a></p>
<p>kfifo 是 Linux kernel 中的一个通用队列实现，对于 kernel 中常见的 FIFO 队列应用还是很有用的，本文主要简单介绍分析下 Linux kernel kfifo。实际上 ChinaUnix 上有个 kfifo 的分析文章，但已经比较老（基于 Linux 2.6.10），而且我现在用的 2.6.34 版本 kernel 中 kfifo 实现有很多改动，故自己简单写下，ChinaUnix 上的 kfifo 介绍帖子在这里：</p>
<p><a href="http://bbs.chinaunix.net/thread-1994832-1-1.html" target="_blank">http://bbs.chinaunix.net/thread-1994832-1-1.html</a></p>
<p>kfifo 定义在 include/linux/kfifo.h 头文件中，我们经常使用的就是 kfifo 结构，看看它的定义：</p>
<pre class="brush: cpp; title: include/linux/kfifo.h; notranslate">
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) */
};
</pre>
<p>kfifo 也像其它队列那样提供了两个主要操作：入队列（in） 和 出队列（out），对应于上面结构中的 in 和 out 两个偏移量，in 偏移量为下次入队列的位置，out 为下次出队列的位置，很容易也能想到 out 值必须小于等于 in 值，当 out 值等于 in 值时表示队列为空，kfifo 中 buffer 为队列的空间，size 为空间大小，必须为 2 的 k 次幂值（原因在下面说明）。当然如果 in 值等于队列长度了，就表示队列已经满了。</p>
<p>先看看 kfifo 最简单的一些操作实现，在 kernel/kfifo.c 文件中：</p>
<pre class="brush: cpp; title: kernel/kfifo.c; notranslate">
static void _kfifo_init(struct kfifo *fifo, void *buffer,
		unsigned int size)
{
	fifo-&gt;buffer = buffer;
	fifo-&gt;size = size;

	kfifo_reset(fifo);
}

/**
 * kfifo_init - initialize a FIFO using a preallocated buffer
 * @fifo: the fifo to assign the buffer
 * @buffer: the preallocated buffer to be used.
 * @size: the size of the internal buffer, this has to be a power of 2.
 *
 */
void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size)
{
	/* size must be a power of 2 */
	BUG_ON(!is_power_of_2(size));

	_kfifo_init(fifo, buffer, size);
}
EXPORT_SYMBOL(kfifo_init);

/**
 * kfifo_alloc - allocates a new FIFO internal buffer
 * @fifo: the fifo to assign then new buffer
 * @size: the size of the buffer to be allocated, this have to be a power of 2.
 * @gfp_mask: get_free_pages mask, passed to kmalloc()
 *
 * This function dynamically allocates a new fifo internal buffer
 *
 * The size will be rounded-up to a power of 2.
 * The buffer will be release with kfifo_free().
 * Return 0 if no error, otherwise the an error code
 */
int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask)
{
	unsigned char *buffer;

	/*
	 * round up to the next power of 2, since our 'let the indices
	 * wrap' technique works only in this case.
	 */
	if (!is_power_of_2(size)) {
		BUG_ON(size &gt; 0x80000000);
		size = roundup_pow_of_two(size);
	}

	buffer = kmalloc(size, gfp_mask);
	if (!buffer) {
		_kfifo_init(fifo, NULL, 0);
		return -ENOMEM;
	}

	_kfifo_init(fifo, buffer, size);

	return 0;
}
EXPORT_SYMBOL(kfifo_alloc);

/**
 * kfifo_free - frees the FIFO internal buffer
 * @fifo: the fifo to be freed.
 */
void kfifo_free(struct kfifo *fifo)
{
	kfree(fifo-&gt;buffer);
	_kfifo_init(fifo, NULL, 0);
}
EXPORT_SYMBOL(kfifo_free);
</pre>
<p>调用 kfifo_alloc 可以自动分配空间并初始化，你也可以调用 kfifo_init 函数使用自己的空间来初始化队列，可以看到这两个函数中都用 is_power_of_2 做了检查队列空间的操作。kfifo_free 释放队列，它会调用 _kfifo_init 函数（参数为 NULL 和 0 清空队列），调用 kfifo_reset 可以重置队列（将 in 和 out 都设为 0）。你也可以用 DECLARE_KFIFO 和 INIT_KFIFO 静态定义一个 kfifo 队列，尽管这不太会被用到。</p>
<p>调用 kfifo_in 函数将数据加入队列，kfifo_out 将数据从队列中取出并从队列中删除（增加 out 值），Linux 还提供了 kfifo_out_peek 函数从队列中取数据但并不删除（不增加 out 值）。kfifo_in 中会先调用 __kfifo_in_data 将输入加入队列，然后调用 __kfifo_add_in 增加 in 的值，kfifo_out 相反则调用 __kfifo_out_data 和 __kfifo_add_out 函数取出数据并删除。</p>
<p>kfifo 中同时提供了 kfifo_from_user 函数用户将用户空间的数据加入到队列中，它会先调用 __kfifo_from_user_data 将用户空间的数据加入队列，再调用 __kfifo_add_in 增加 in 的值。看看 __kfifo_from_user_data 的实现：</p>
<pre class="brush: cpp; title: kernel/kfifo.c; notranslate">
static inline int __kfifo_from_user_data(struct kfifo *fifo,
	 const void __user *from, unsigned int len, unsigned int off,
	 unsigned *lenout)
{
	unsigned int l;
	int ret;

	/*
	 * Ensure that we sample the fifo-&gt;out index -before- we
	 * start putting bytes into the kfifo.
	 */

	smp_mb();

	off = __kfifo_off(fifo, fifo-&gt;in + off);

	/* first put the data starting from fifo-&gt;in to buffer end */
	l = min(len, fifo-&gt;size - off);
	ret = copy_from_user(fifo-&gt;buffer + off, from, l);
	if (unlikely(ret)) {
		*lenout = ret;
		return -EFAULT;
	}
	*lenout = l;

	/* then put the rest (if any) at the beginning of the buffer */
	ret = copy_from_user(fifo-&gt;buffer, from + l, len - l);
	*lenout += ret ? ret : len - l;
	return ret ? -EFAULT : 0;
}
</pre>
<p>可以看到 __kfifo_from_user_data 中是直接调用 copy_from_user 将用户空间的数据拷贝到 kfifo 队列的空间中。相应的也有 kfifo_to_user 函数将队列中的数据取出到用户空间的地址，他就调用 copy_to_user 将队列中数据拷贝到用户空间。</p>
<p>需要注意的是 __kfifo_from_user_data 中用到的 __kfifo_off 函数：</p>
<pre class="brush: cpp; title: include/linux/kfifo.h; notranslate">
static inline unsigned int __kfifo_off(struct kfifo *fifo, unsigned int off)
{
	return off &amp; (fifo-&gt;size - 1);
}
</pre>
<p>__kfifo_off 是根据指定的偏移量得到索引值，由这里也可以看出为什么队列的大小为什么必须是 2 的 k 次幂值，否则无法得到正确的值。而且从代码中可以看到 __kfifo_from_user_data、__kfifo_in_n、__kfifo_in_rec 等函数中都用到了 __kfifo_off 函数指定加入队列时的偏移量。</p>
<p>另外从 include/linux/kfifo.h 中你也可以看到新的 kfifo 实现中默认 EXPORT 了非常多的 API 函数给 kernel 开发者使用。</p>
<p>以上为个人分析结果，有任何问题欢迎指正哦 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/linux-kernel-kfifo/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
