<?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>Oasis Feng &#187; Module</title>
	<atom:link href="http://blog.oasisfeng.com/tag/module/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.oasisfeng.com</link>
	<description>Challenge your imagination!</description>
	<lastBuildDate>Sun, 22 Aug 2010 18:15:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="http://superfeedr.com/hubbub"/>		<item>
		<title>深入探究MODVERSIONS的实现原理</title>
		<link>http://blog.oasisfeng.com/2008/08/12/all-about-modversions/</link>
		<comments>http://blog.oasisfeng.com/2008/08/12/all-about-modversions/#comments</comments>
		<pubDate>Mon, 11 Aug 2008 18:17:44 +0000</pubDate>
		<dc:creator>oasisfeng</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Kernel]]></category>
		<category><![CDATA[modprobe]]></category>
		<category><![CDATA[Module]]></category>

		<guid isPermaLink="false">http://blog.oasisfeng.com/?p=430</guid>
		<description><![CDATA[最近在分析Linux内核模块与内核之间的版本耦合关系时，在实验中发现了一些有趣的结果：在同一Linux版本基础上经过不同裁剪的内核，甚至是在跨度不大的两个内核版本间，内核模块可以自由互用，而且内核本身似乎具备检查这种兼容性的能力。这与之前所知的情况是不同的。在我久远的记忆中，Linux的是依靠内核模块构建时自动产生的“vermagic”标识检查是否与当前内核版本一致的，如果不一致则拒绝加载。除非在insmod/modprobe时指定参数强制忽略vermagic，但这样做的代价是如果使用错误版本的内核模块就可能导致内核崩溃。 看了一下内核这部分的源代码（所参考源码的内核版本为2.6.17.11），发现了两种不同截然的处理方式，由一个内核构建宏“CONFIG_MODVERSIONS”所控制。在它关闭的情况下，加载内核模块时，将对vermagic作全字符串的完整匹配，任何不一致均会阻止该内核模块的加载；而倘若这个宏被开启，则只有vermagic第一个空格之后的部分会参与匹配，也就是说形如“2.6.5-7.191-bigsmp”这一段内核版本标识其实并不要求一致。事实上，当上述宏开启后，除了对vermagic后半段的匹配性检查外，内核还会进行额外的“内核接口指纹”检验，才最终决定是否允许加载该内核模块。 何谓“内核接口指纹”？其实这是Linux内核在试图解决跨版本内核模块兼容性问题上的一个积极尝试。通过在内核构建流程中插入一个特殊的“内核接口指纹”提取阶段，由一个专用的GNU工具“genksyms”为内核的源代码生成“指纹”信息。这里的“指纹”其实是一组特殊的CRC，它们分别对应着内核符号表中的每一个符号。生成CRC的输入包括这个符号的完整定义，以及其中所有参数涉及的数据类型定义，包括typedef、enum、struct、union。其中typedef将展开至最原始的标准数据类型；后三种类型将包含其定义的完整组成部分（例如结构体的成员、枚举的清单），对于组成成员也将递归应用上述原则。也就是说，“指纹”信息代表了对内核接口有兼容性影响的绝大部分要素，一旦其发生变化，则意味着接口的兼容性可能已经被破坏。 除了内核本身的构建在CONFIG_MODVERSIONS打开后会包含上述指纹信息外，内核模块的构建是否包含指纹信息还依赖于构建该内核模块的内核构建环境中此宏的状态。只有当两者均包含上述指纹信息时，上述检验机制才能真正生效。想要知道当前内核是否包含了内核接口指纹，只需提取/proc/config.gz并查看其中CONFIG_MODVERSIONS的取值；而若想知晓一个内核模块是否包含指纹信息，则需要用到modprobe的下面这个参数： modprobe --dump-modversions &#60;module&#62; 其实，就如Linux在内核编译菜单中对CONFIG_MODVERSIONS解释，也用了一个“hopefully”。这个机制本身的出发点是很好的，而且也尽了最大努力去校验。但事实上，接口的兼容性很难仅仅用定义来保证，因为接口所体现的功能部分是无法在定义中反应出来的，这也是程序尚无法完全取代人类的判断能力。因此，MODVERSIONS可以作为版本不配套时的后备选择（尤其对非开源的驱动程序），但不能依赖它作为兼容性的判定依据。 &#8212; 参考文献：Kernel Symbols and CONFIG_MODVERSIONS]]></description>
			<content:encoded><![CDATA[<p>最近在分析Linux内核模块与内核之间的版本耦合关系时，在实验中发现了一些有趣的结果：在同一Linux版本基础上经过不同裁剪的内核，甚至是在跨度不大的两个内核版本间，内核模块可以自由互用，而且内核本身似乎具备检查这种兼容性的能力。这与之前所知的情况是不同的。在我久远的记忆中，Linux的是依靠内核模块构建时自动产生的“vermagic”标识检查是否与当前内核版本一致的，如果不一致则拒绝加载。除非在insmod/modprobe时指定参数强制忽略vermagic，但这样做的代价是如果使用错误版本的内核模块就可能导致内核崩溃。</p>
<p><span id="more-430"></span>看了一下内核这部分的源代码（所参考源码的内核版本为2.6.17.11），发现了两种不同截然的处理方式，由一个内核构建宏“CONFIG_MODVERSIONS”所控制。在它关闭的情况下，加载内核模块时，将对vermagic作全字符串的完整匹配，任何不一致均会阻止该内核模块的加载；而倘若这个宏被开启，则只有vermagic第一个空格之后的部分会参与匹配，也就是说形如“2.6.5-7.191-bigsmp”这一段内核版本标识其实并不要求一致。事实上，当上述宏开启后，除了对vermagic后半段的匹配性检查外，内核还会进行额外的“内核接口指纹”检验，才最终决定是否允许加载该内核模块。</p>
<p>何谓“内核接口指纹”？其实这是Linux内核在试图解决跨版本内核模块兼容性问题上的一个积极尝试。通过在内核构建流程中插入一个特殊的“内核接口指纹”提取阶段，由一个专用的GNU工具“genksyms”为内核的源代码生成“指纹”信息。这里的“指纹”其实是一组特殊的CRC，它们分别对应着内核符号表中的每一个符号。生成CRC的输入包括这个符号的完整定义，以及其中所有参数涉及的数据类型定义，包括typedef、enum、struct、union。其中typedef将展开至最原始的标准数据类型；后三种类型将包含其定义的完整组成部分（例如结构体的成员、枚举的清单），对于组成成员也将递归应用上述原则。也就是说，“指纹”信息代表了对内核接口有兼容性影响的绝大部分要素，一旦其发生变化，则意味着接口的兼容性可能已经被破坏。</p>
<p>除了内核本身的构建在CONFIG_MODVERSIONS打开后会包含上述指纹信息外，内核模块的构建是否包含指纹信息还依赖于构建该内核模块的内核构建环境中此宏的状态。只有当两者均包含上述指纹信息时，上述检验机制才能真正生效。想要知道当前内核是否包含了内核接口指纹，只需提取/proc/config.gz并查看其中CONFIG_MODVERSIONS的取值；而若想知晓一个内核模块是否包含指纹信息，则需要用到modprobe的下面这个参数：</p>
<pre>modprobe --dump-modversions &lt;module&gt;</pre>
<p>其实，就如Linux在内核编译菜单中对CONFIG_MODVERSIONS解释，也用了一个“hopefully”。这个机制本身的出发点是很好的，而且也尽了最大努力去校验。但事实上，接口的兼容性很难仅仅用定义来保证，因为接口所体现的功能部分是无法在定义中反应出来的，这也是程序尚无法完全取代人类的判断能力。因此，MODVERSIONS可以作为版本不配套时的后备选择（尤其对非开源的驱动程序），但不能依赖它作为兼容性的判定依据。</p>
<p>&#8212;<br />
参考文献：<a href="http://www.skynet.ie/~mark/home/kernel/symbols.html">Kernel Symbols and CONFIG_MODVERSIONS</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.oasisfeng.com/2008/08/12/all-about-modversions/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>在Linux中实现虚桩式内核模块</title>
		<link>http://blog.oasisfeng.com/2008/06/28/linux-stub-kernel-module/</link>
		<comments>http://blog.oasisfeng.com/2008/06/28/linux-stub-kernel-module/#comments</comments>
		<pubDate>Sat, 28 Jun 2008 12:38:46 +0000</pubDate>
		<dc:creator>oasisfeng</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Kernel]]></category>
		<category><![CDATA[ko]]></category>
		<category><![CDATA[modprobe]]></category>
		<category><![CDATA[Module]]></category>

		<guid isPermaLink="false">http://blog.oasisfeng.com/?p=316</guid>
		<description><![CDATA[最近在把以前做的一个驱动程序在线加载技术移植到SuSE的AutoYaST安装系统中时遇到了一点小麻烦。AutoYaST采用SuSE自己编写的Linuxrc作为其初期引导部分，有别于大部分常规的initrd引导部分，Linuxrc是直接用C语言编写的，而且几乎没有提供什么扩充的灵活性。如此一来，就无法像以往移植到其它环境中那样单纯修改引导脚本就可以搞定。虽说直接修改Linuxrc的源码也可以达到这个目的，但这样就增加了后期维护的复杂度。唉，还真是个头疼的问题…… 仔细分析了一下AutoYaST的设计，发现它至少还是允许增补驱动程序的，但仅仅支持最简单的用配置文件写死的insmod方式…… 好吧，只要还给了这个勉强算得上可扩充的途径，我一样能把驱动在线加载模块集成进来。由于只能固定调用某个驱动，所以必须从内核模块下手。把现有程序改造成内核模块显然是不现实的，好在Linux内核中还提供了这样一个调用用户态程序的接口：call_usermodehelper()，你可以在kmod.h中找到它。功能很简单，调用一个用户态程序，并且可以选择是否阻塞直到它执行完成。 顺着这个思路，只要写一个简单的虚桩式内核模块，在其初始化函数中调用我们真正想要执行的用户态程序即可达到目的了。但是，当我提出这个方案后，立即有同事质疑：在内核模块初始化阶段加载别的内核模块，搞不好会锁死内核吧？“搞不好”至少说明他也不肯定，为了找寻答案，我分析了一下内核中系统调用sys_init_module()的代码（以2.6.17为蓝本）。 asmlinkage long sys_init_module(void __user *umod, unsigned long len, const char __user *uargs) { ...... /* Only one module load at a time, please */ if (mutex_lock_interruptible(&#038;module_mutex) != 0) return -EINTR; 刚进入就加锁了，似乎有点不妙。紧接着开始加载内核模块： /* Do all the hard work */ mod = load_module(umod, len, uargs); if (IS_ERR(mod)) { mutex_unlock(&#038;module_mutex); return PTR_ERR(mod); } /* [...]]]></description>
			<content:encoded><![CDATA[<p>最近在把以前做的一个驱动程序在线加载技术移植到SuSE的AutoYaST安装系统中时遇到了一点小麻烦。AutoYaST采用SuSE自己编写的Linuxrc作为其初期引导部分，有别于大部分常规的initrd引导部分，Linuxrc是直接用C语言编写的，而且几乎没有提供什么扩充的灵活性。如此一来，就无法像以往移植到其它环境中那样单纯修改引导脚本就可以搞定。虽说直接修改Linuxrc的源码也可以达到这个目的，但这样就增加了后期维护的复杂度。唉，还真是个头疼的问题……</p>
<p>仔细分析了一下AutoYaST的设计，发现它至少还是允许增补驱动程序的，但仅仅支持最简单的用配置文件写死的insmod方式…… 好吧，只要还给了这个勉强算得上可扩充的途径，我一样能把驱动在线加载模块集成进来。由于只能固定调用某个驱动，所以必须从内核模块下手。把现有程序改造成内核模块显然是不现实的，好在Linux内核中还提供了这样一个调用用户态程序的接口：call_usermodehelper()，你可以在kmod.h中找到它。功能很简单，调用一个用户态程序，并且可以选择是否阻塞直到它执行完成。</p>
<p><span id="more-316"></span>顺着这个思路，只要写一个简单的虚桩式内核模块，在其初始化函数中调用我们真正想要执行的用户态程序即可达到目的了。但是，当我提出这个方案后，立即有同事质疑：在内核模块初始化阶段加载别的内核模块，搞不好会锁死内核吧？“搞不好”至少说明他也不肯定，为了找寻答案，我分析了一下内核中系统调用sys_init_module()的代码（以2.6.17为蓝本）。</p>
<pre>asmlinkage long
sys_init_module(void __user *umod,
		unsigned long len,
		const char __user *uargs)
{
	......

	/* Only one module load at a time, please */
	if (mutex_lock_interruptible(&#038;module_mutex) != 0)
		return -EINTR;</pre>
<p>刚进入就加锁了，似乎有点不妙。紧接着开始加载内核模块：</p>
<pre>	/* Do all the hard work */
	mod = load_module(umod, len, uargs);
	if (IS_ERR(mod)) {
		mutex_unlock(&#038;module_mutex);
		return PTR_ERR(mod);
	}

	/* Now sew it into the lists.  They won't access us, since
           strong_try_module_get() will fail. */
	stop_machine_run(__link_module, mod, NR_CPUS);

	/* Drop lock so they can recurse */
	mutex_unlock(&#038;module_mutex);

	blocking_notifier_call_chain(&#038;module_notify_list,
			MODULE_STATE_COMING, mod);

	/* Start the module */
	if (mod->init != NULL)
		ret = mod->init();</pre>
<p>看来这段代码的作者（或维护者）似乎早就预见到了我所面临的场景。内核模块的加载和初始化被清晰的分为两步，由于加载过程需要链接内核模块，因此必须加锁，而后续的初始化过程已经不会影响内核的完整性了，所以自然可以在解锁后进行。</p>
<p>排除这个疑虑后，我写了一个简单的用于检验上述思路的内核模块：</p>
<pre>#include &lt;linux/kernel.h&gt;
#include &lt;linux/module.h&gt;
#include &lt;linux/kmod.h&gt;

static int __init kmod_init(void)
{
        int ret;
        char *envp[] = {
                "PATH=/sbin:/bin",
                NULL
        };
        char *argv[] = {
                "/sbin/modprobe",
                "--first-time",
                "e1000",
                NULL
        };
        if ((ret = call_usermodehelper(argv[0], argv, envp, 1)) != 0)
                printk(KERN_ERR "Kmod test failed! (%d)\n", ret);

        return ret > 0 ? - ret : ret;
}

static void __exit kmod_exit(void)
{
}

module_init(kmod_init);
module_exit(kmod_exit);

MODULE_LICENSE("GPL");</pre>
<p>用insmod插入它时实际上调用了用户态的modprobe插入另一个内核模块e1000。如果不希望将这个虚桩留在内核中，也可以通过在kmod_init中返回-1将自身从内核中移除。</p>
<p>验证结果表明这个虚桩式内核模块的方案完全是可行的，而且与被加载的实际内核模块之间没有任何耦合及影响，它们可以各自独立的被卸载。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.oasisfeng.com/2008/06/28/linux-stub-kernel-module/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.766 seconds -->
