以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 操作系统原理 』 (http://bbs.xml.org.cn/list.asp?boardid=63) ---- [推荐]内核模块编程的详细介绍 (http://bbs.xml.org.cn/dispbbs.asp?boardid=63&rootid=&id=40977) |
-- 作者:longshentailang -- 发布时间:12/8/2006 11:15:00 AM -- [推荐]内核模块编程的详细介绍 不知道在什么时候,Linux出现了module这种东西,的确,它是Linux的一大革新。有了 module 之后,写device driver 不再是一项恶梦,修改 kernel也不再是一件痛苦的事了。因为你不需要每次要测试 driver 就重新compile kernel 一次。那简直是会累死人。Module 可以允许我们动态的改变kernel,加载 devicedriver,而且它也能缩短我们 driver development 的时间。在这篇文章里,我将要跟各位介绍一下 module的原理,以及如何写一个 module。 module 我们知道,在写 C 程序的时候,一个程序只能有一个main。Kernel 本身其实也是一个程序,它本身也有个 main,叫start_kernel()。当我们把一个 module 载到kernel 里的时候,它会跟 kernel 整合在一起,成为 kernel的一部分。请各位想想,那 module 可以有 main 吗 ?答案很明显的,是 No。理由很简单。一个程序只能有一个 main。在使用 module Kernel 里有一个变量叫 module_list,每当 user将一个 module 载到 kernel 里的时候,这个 module 就会被记录在module_list 里面。当 kernel要使用到这个 module 提供的 function 时,它就会去 search 这个 list,找到module,然后再使用其提供的function 或 variable。每一个 module 都可以 export 一些 function或变量来让别人使用。除此之外,module 也可以使用已经载到 kernel 里的module 提供的 function。这种情形叫做module stack。比方说,module A 用到 module B 的东西,那在加载 module A之前必须要先加载 moduleB。否则 module A 会无法加载。除了 module 会 export 东西之外,kernel本身也会 export 一些function 或 variable。同样的,module 也可以使用 kernel 所 export出来的东西。由于大家平时都是撰写user space 的程序,所以,当突然去写 module 的时候,会把平时写程序用的function 拿到 module 里使用。像是printf 之类的东西。我要告诉各位的是,module 所使用的 function 或variable,要嘛就是自己写在 module里,要嘛就是别的 module 提供的,再不就是 kernel所提供的。你不能使用一般 libc 或 glibc所提供的function。像 printf之类的东西。这一点可能是各位要多小心的地方。(也许你可以先 link 好,再载到 刚才我们说到 kernel 本身会 export 出一些 function 或variable 来让 module 使用,但是,我们不是万能的,我们怎幺知道 kernel有开放那里东西让我们使用呢 ? Linux提供一个 command,叫 ksyms,你只要执行 ksyms -a 就可以知道 kernel或目前载到 kernel 里的 module提供了那些 function 或 variable。底下是我的系统的情形: c0216ba0 drive_info_R744aa133 在 kernel 里,有一个 symbol table 是用来记录export 出去的 function 或 variable。除此之外,也会记录着那个 module export 那些function。上面几行中,表示 kernel 提供了 drive_info 这个function/variable。所以,我们可以在kernel 里直接使用它,等载到 kernel 里时,会自动做好 link的动作。由此,我们可以知道,module 本身其实是还没做link 的一些 object code。一切都要等到 module 被加载 kernel 之后,link才会完成。各位应该可以看到drive_info 后面还接着一些奇怪的字符串。_R744aa133,这个字符串是根据目前 Linux 不知道从那个版本以来,就多了一个 config 的选项,叫做Set version number in symbols of module。这是为了避免对系统造成不稳定。我们知道 Linux 的kernel 更新的很快。在 kernel 更新的过程,有时为了效率起见,会对某些旧有的 data structure 或 function 所以,刚才也许我们不应该讲 kernel 提供了 drive_info,而应该说 kernel 提供了driver_info_R744aa133来让我们使用。这样也许各位会比较明白。也就是说,kernel 认为它提供的driver_info_R744aa133 这个东西,而不是driver_info。所以,我们可以发现有的人在加载 module时,系统都一直告诉你某个 function 无法 resolved。这就是因为 kernel 里没有你要的 function,要不然就是你的module 里使用的 function 跟 kernel encode 的结果不一样。所以无法resolve。解决方式,要嘛就是将kernel 里的 set version 选项关掉,要嘛就是将 module compile 成 kernel有办法接受的型式。 那有人就会想说,如果 kernel 认定它提供的 function 名字叫做driver_info_R744aa133 如果各位的系统有将 set version的选项打开的话,那大家可到 /usr/src/linux/include/linux/modules 这个目录底下。这个目录底下有所多的..ver档案。这些档案其实就是用来做 #define 用的。我们来看看 ksyms.ver 这个档案里,里面有一行是这样子的 : #define printk _set_ver(printk) set_ver是一个 macro,就是用来在 printk 后面加上 version number 的。有兴趣的朋友可以自行去观看这个 macro的写法。用了这些 ver 檔,我们就可以在 module 里直接使用 printk #ifdef MODVERSIONS 加入这三行的原因是,避免这个 module 在没有设定 kernel version 的系统上,将 modversions.h 这个档案 include 进来。各位可以去试试看,当你把 set version的选项关掉时,modversions.h 和 modules 这个目录都会不见。如果没有上面三行,那 compile就不会过关。所以一般来讲,modversions.h 我们会选择在 compile 时传给 gcc 使用。就像下面这个样子。 gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS main.c -include usr/src/linux/include/linux/modversions.h 在这个 command line 里,我们看到了 -D__KERNEL__,这是说要定义 __KERNEL__ 讲到这里,相信各位应该对 module 有一些认识了,以后遇到 module unresolved 应该不会感到困惑了,应该也有办法解决了。 刚才讲的都是使用别人的 function 上遇到的名字 encode 问题。但是,如果我们自己的 module 想要 export一些东西让别的 module 使用呢。很简单。在 default 上,在你的 module EXPORT_SYMBOL(full); 就会自动将 full export 出去,你马上就可以从 ksyms 里发现有 full 这个变量被 export 出去。在使用EXPORT_SYMBOL 之前,要小心一件事,就是必须在 gcc 里定义 EXPORT_SYMTAB 这个 constant,否则在compile 时会发生 parser error。所以,要使用 EXPORT_SYMBOL 的话,那 gcc应该要下: gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS -DEXPORT_SYMTAB main.c -include /usr/src/linux/include/linux/modversions.h 如果我们不想 export 任何的东西,那我们只要在 module 里下 EXPORT_NO_SYMBOLS; 就可以了。使用 EXPORT_NO_SYMBOLS 用不着定义任何的 constant。其实,如果各位使用过旧版的register_symbol 的话,一定会觉得新版的方式比较好用。至少我是这样觉得啦。因为使用 register_symbol还要先定义出自己的 symbol_table,感觉有点麻烦。 当我们使用 EXPORT_SYMBOL 把一些 function 或 variable export 出来之后,我们使用 ksyma -a 去看一些结果。我们发现 EXPORT_SYMBOL(full) 的确是把 full export出来了 : 其实,问题就在于我们没有对本身的 module 所 export 出来的 function 或 variable 的名字做 encode。想想,如果在 module 的开头。我们加入一行: c8822200 full_Rxxxxxx [my_module] Linux 里提供了一个 command,叫genksyms,就是用来帮我们产生这种 .ver 的档案的。它会从 stdin 里读取 source code,然后检查 source code 里是否有 export 的 variable 或 function。如果有,它就会自动为每个export 出来的东西产生一些define。这些 define 就是我们之前说的。等我们有了这些 define 之后,只要在我们的 module 里加入这些define,那 export 出来的 function 或 variable 就会变成上面那个样子。 假设我们的程序都放在一个叫 main.c 档案里,我们可以使用下列的方式产生这些 define。 讲了这幺多,相信各位应该都已经比较清楚 module 在 kernel 中是怎幺样一回事,也应该知道为什幺有时候 module会无法加载了。除此之外,各位应该还知道如何使自己 module export 出来的东西也具有 kernel version 的information。 接下来,要跟各位讲的就是,如何写一个 module 了。其实,写一个 module很简单的。如果你了解我上面所说的东西。那我再讲一次,再用个例子,相信大家就都会了。要写一个 由于 module 是载到 kernel 使用的,所以,可能别的module 会使用你的 module,甚至某些 process 也会使用到你的 module,为了避免 module还有人使用时就被移除,每个 module 都有一个 use count。用来记录目前有多少个 process 或 module 正在使用这个 在此,我要介绍三个 macro,是跟 module 的 use count 有关的。 这个例子很简单。其实只是示范如何使用 init_module() 以及 cleanup_module() 来写一个 module。当然,这两个 function 只是构成 module 的基本条件罢了。至于 module 里要提供的功能则是看各人的需要。
main.c main.ver: 利用 genksyms 产生出来的。 gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 > main.ver 接下来,就是要把 main.c compile 成 main.o insmod main.o 检查看 /proc/modules 里是否有 main 这个 module。如果有,表示 main 这个 module 已经载到 kernel 了。再下一个指令,看看 full export 出去的结果。
可以看到 full_R355b84b2,表示,我们已经成功的将 full 的名字加上 kernel version 的 information 了。当我们不需要这个 module 时,我们就可以下一个 command, 关于 module 的介绍已经到此告一段落了。其实,使用 module 实在是很简单的一件事。对于要发展 driver 或是增加 kernel 某些新功能的人来讲,用 module 不啻为一个方便的方式。希望这篇文章对各位能有所帮助。 |
-- 作者:lidongbin -- 发布时间:12/21/2006 5:57:00 PM -- 可以用别的语言插入语句吗? |
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
64.453ms |