- 浏览: 661192 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
sztime:
可以在文本框上绑定事件来禁用回车键, 我就是这样做的.在IE中 ...
form 回车自动提交问题 -
damoqiongqiu:
非常好的文章,很透彻不过有一句话小僧腆着脸补充一下:“1111 ...
为什么要用补码来做存储 -
wuyizhong:
原来如此啊。
form 回车自动提交问题 -
luliangy:
谢楼主~!
用C语言扩展Python的功能 -
kwong:
很有用,谢谢
火狐和IE 对css 样式解释的差异
不知道在什幺时候,Linux 出现了 module 这种东西,的确,它是 Linux 的一大革新。有了 module 之后,写 device driver 不再是一项恶梦,修改 kernel 也不再是一件痛苦的事了。因为你不需要每次要测试 driver 就重新 compile kernel 一次。那简直是会累死人。Module 可以允许我们动态的改变 kernel,加载 device driver,而且它也能缩短我们 driver development 的时间。在这篇文章里,我将要跟各位介绍一下 module 的原理,以及如何写一个 module。
module 翻译成中文就是模块,不过,事实上去翻译这个字一点都没意义。在讲模块之前,我先举一个例子。相信很多人都用过 RedHat。在 RedHat 里,我们可以执行 sndconfig,它可以帮我们 config 声卡。config 完之后如果捉得到你的声卡,那你的声卡马上就可以动了,而且还不用重新激活计算机。这是怎幺做的呢 ? 就是靠module。module 其实是一般的程序。但是它可以被动态载到 kernel 里成为 kernel的一部分。载到 kernel 里的 module 它具有跟 kernel 一样的权力。可以 access 任何 kernel 的 data structure。你听过 kdebug 吗 ? 它是用来 debug kernel 的。它就是先将它本身的一个 module 载到 kernel 里,而在 user space 的 gdb 就可以经由跟这个 module 沟通,得知 kernel 里的 data structure 的值,除此之外,还可以经由载到 kernel 的 module 去更改 kernel 里 data structure。
我们知道,在写 C 程序的时候,一个程序只能有一个 main。Kernel 本身其实也是一个程序,它本身也有个 main,叫 start_kernel()。当我们把一个 module 载到 kernel 里的时候,它会跟 kernel 整合在一起,成为 kernel 的一部分。请各位想想,那 module 可以有 main 吗 ? 答案很明显的,是 No。理由很简单。一个程序只能有一个 main。在使用 module 时,有一点要记住的是 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 之前必须要先加载 module B。否则 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,我好象试过,但是忘了)
刚才我们说到 kernel 本身会 export 出一些 function 或 variable 来让 module 使用,但是,我们不是万能的,我们怎幺知道 kernel 有开放那里东西让我们使用呢 ? Linux 提供一个 command,叫 ksyms,你只要执行 ksyms -a 就可以知道 kernel 或目前载到 kernel 里的 module 提供了那些 function 或 variable。底下是我的系统的情形:
c0216ba0 drive_info_R744aa133
c01e4a44 boot_cpu_data_R660bd466
c01e4ac0 EISA_bus_R7413793a
c01e4ac4 MCA_bus_Rf48a2c4c
c010cc34 __verify_write_R203afbeb
. . . . .
在 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,这个字符串是根据目前 kernel 的版本再做些 encode 得出来的结果。为什幺额外需要这一个字符串呢 ?
Linux 不知道从那个版本以来,就多了一个 config 的选项,叫做 Set version number in symbols of module。这是为了避免对系统造成不稳定。我们知道 Linux 的 kernel 更新的很快。在 kernel 更新的过程,有时为了效率起见,会对某些旧有的 data structure 或 function 做些改变,而且一变可能有的 variable 被拿掉,有的 function 的 prototype 跟原来的都不太一样。如果这种情形发生的时候,那可能以前 2.0.33 版本的 module 拿到 2.2.1 版本的 kernel 使用,假设原来 module 使用了 2.0.33 kernel 提供的变量叫 A,但是到了 2.2.1 由于某些原因必须把 A 都设成 NULL。那当此 module 用在 2.2.1 kernel 上时,如果它没去检查 A 的值就直接使用的话,就会造成系统的错误。也许不会整个系统都死掉,但是这个 module 肯定是很难发挥它的功能。为了这个原因,Linux 就在 compile module 时,把 kernel 版本的号码 encode 到各个 exported function 和 variable 里。
所以,刚才也许我们不应该讲 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 的话,那我们写程序时,是不是用到这个 funnction 的地方都改成 driver_info_R744aa133 就可以了。答案是 Yes。但是,如果每个 function 都要你这样写,你不会觉得很烦吗 ? 比方说,我们在写 driver 时,很多人都会用到 printk 这个 function。这是 kernel 所提供的 function。它的功能跟 printf 很像。用法也几乎都一样。是 debug 时很好用的东西。如果我们 module 里用了一百次 printk,那是不是我们也要打一百次的 printk_Rdd132261 呢 ? 当然不是,聪明的人马上会想到用 #define printk printk_Rdd132261 就好了嘛。所以啰,Linux 很体贴的帮我们做了这件事。
如果各位的系统有将 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 这样的名字了。而这些 ver 档会自动帮我们做好 #define 的动作。可是,我们可以发现这个目录有很多很多的 ver 檔。有时候,我们怎幺知道我们要呼叫的 function 是在那个 ver 档里有定义呢 ? Linux 又帮我们做了一件事。/usr/src/linux/include/linux/modversions.h 这个档案已经将全部的 ver 档都加进来了。所以在我们的 module 里只要 include 这个档,那名字的问题都解决了。但是,在此,我们奉劝各位一件事,不要将 modversions.h 这个档在 module 里 include 进来,如果真的要,那也要加上以下数行:
#ifdef MODVERSIONS
#include
#endif
加 入这三行的原因是,避免这个 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__ 这个 constant。很多跟 kernel 有关的 header file,都必须要定义这个 constant 才能 include 的。所以建议你最好将它定义起来。另外还有一个 -DMODVERSIONS。这个 constant 我刚才忘了讲。刚才我们说要解决 fucntion 或 variable 名字 encode 的方式就是要 include modversions.h,其实除此之外,你还必须定义 MODVERSIONS 这个 constant。再来就是 MODULE 这个 constant。其实,只要是你要写 module 就一定要定义这个变量。而且你还要 include module.h 这个档案,因为 _set_ver 就是定义在这里的。
讲到这里,相信各位应该对 module 有一些认识了,以后遇到 module unresolved 应该不会感到困惑了,应该也有办法解决了。
刚 才讲的都是使用别人的 function 上遇到的名字 encode 问题。但是,如果我们自己的 module 想要 export 一些东西让别的 module 使用呢。很简单。在 default 上,在你的 module 里所有的 global variable 和 function 都会被认定为你要 export 出去的。所以,如果你的 module 里有 10 个 global variable,经由 ksyms,你可以发现这十个 variable 都会被 export 出去。这当然是个很方便的事啦,但是,你知道,有时候我们根本不想把所有的 variable 都 export 出去,万一有个 module 没事乱改我们的 variable 怎幺办呢 ? 所以,在很多时候,我们都只会限定几个必要的东西 export 出去。在 2.2.1 之前的 kernel (不是很确定) 可以利用 register_symtab 来帮我们。但是,现在更新的版本早就出来了。所以,在此,我会介绍 kernel 2.2.1 里所提供的。kernel 2.2.1 里提供了一个 macro,叫做 EXPORT_SYMBOL,这是用来帮我们选择要 export 的 variable 或 function。比方说,我要 export 一个叫 full 的 variable,那我只要在 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,感觉有点麻烦。
发表评论
-
strlcpy 的历史
2012-11-05 18:51 700strlcpy 并不属于 ANSI C,至今也还不是标准 ... -
c中的移位操作
2012-04-12 18:18 981位移位运算符是将数据看成二进制数,对其进行向左或向 ... -
为什么要用补码来做存储
2012-04-12 18:02 2230看了些补码的知识,摘抄了些,自己整理了些。 顺便带着两个 ... -
google test 使用
2009-01-03 13:59 3551安装: 下载Google C++ Testi ... -
string 的 data() 和c_str()
2008-12-24 01:17 1244data 是字符数组,里面有 '\0 '当然也不会 ... -
C/C++ unit testing tools (18 found)
2008-12-19 01:27 1976C/C++ unit testing tools (18 f ... -
climits中的符号常量
2008-12-10 11:43 1147climits中的符号常量 符号常量 ... -
ICE初次
2008-09-02 01:48 1885按照某人的说法:跨平台的C++网络编程ICE才是王道。于是,我 ... -
对 pthread_cond_wait 的错误理解
2008-08-28 15:52 2095在线程的调度中经常会用到 pthread_cond_wait ... -
环形缓冲
2008-08-27 17:20 956/** * Copyright (c) 2008, ×××研 ... -
Linux下C语言编程的 RPC远程调用编程
2008-07-17 15:38 3414在查看libc6-dev软件包提供的工具(用 dpkg -L ... -
C++中重载操作符时什么时候定义成友元,什么时候定义为成员方法
2008-07-10 18:05 3545在C++语言中,可以用关键字operator加上运算符来表示函 ... -
c++中的存储类型
2008-07-06 00:46 1921存储类型是从变量的存 ... -
libevent 一个time server
2008-05-19 11:20 1302#include <netinet/in.h> # ... -
自己动手改写komodo sourcetree插件 对C/C++的支持
2008-04-16 22:11 1396其实很简单了, 找到sourcetree.js 的 ... -
GNU C的 __attribute__ 机制
2008-04-15 15:34 771GNU C 的一大特色(却不被初学者所知)就是 __attri ... -
dynamic_cast、static_cast、const_cast 和 reinterp
2008-04-10 15:16 1385dynamic_cast、static_cast、co ... -
static_cast、dynamic_cast、reinterpret_cast、和const_c
2008-04-10 15:14 6237static_cast、dynamic_cast、reinte ... -
关于Linux下C/C++程序编译
2007-08-23 18:15 3683在编译之前我们需要 ... -
使用 setfill、setw 和 setprecision 基数的示例
2007-08-23 19:00 2727使用 setfill、setw 和 setprecision ...
相关推荐
这篇文档是关于小学六年级英语下册Module 4 Unit 1的教学教案,主题为"I’m making Daming’s birthday card.",旨在帮助学生掌握特定的英语语句和词汇,同时培养他们的语言运用能力。 教学目标主要包含三个方面: ...
这份"A Module1 Getting to know you测试题精选"是针对3A模块第一单元“认识你”的英语测试,旨在考察学生的基础听力和书写技能。测试主要分为两大部分:听力(40%)和书写(60%),涵盖了词汇理解、句子识别、问答...
第五题需要学生根据图片圈出正确的单词并抄写,这考察了学生对单词形态的认识和书写规范。第六题是单选题,检验学生对名词复数、代词、量词以及动词短语搭配的掌握。例如,"sweets"的复数形式,动词短语"turn on the...
这份文档是针对新外研版(WY)小学英语三年级上册Module 5的一个单元测试卷,主要测试学生对英语中数字的听说读写能力,以及与数字相关的词汇和基本的数学运算。测试内容分为以下几个部分: 1. 听录音圈出相应数量...
I like it very much." 这是一个开放性的写作练习,目的是提高学生的描述能力和创造力,同时加深对植物特性的认识。 总结来说,这份练习涵盖了英语的基础语法、词汇、读写、听力和口语技能的综合训练,对于提升三...
这个练习让学生熟悉并正确书写人物名字、名词和动词短语,如"Mr Li"(李老师)、"leaf"(叶子)、"plant"(植物)、"look at"(看)以及描述植物特征的词组"some roots"(一些根)和"a big trunk"(一个大树干)。...
黑板书写将展示本单元的主题及重点词汇,以帮助学生复习和记忆。 课后作业是完成工作簿中的练习,包括Ex. 1, 2, 3 & 4,以巩固课堂所学知识。 这堂课的设计旨在全方位提升学生的语言技能,并结合实际生活情境,使...
这篇资料是针对三年级英语下册Module1 Unit1的内容,主题为"It's the ABC song",主要涉及了字母的书写、基础语法、词汇理解和句子翻译等练习。以下是详细的知识点解析: 一、字母填充练习 这部分旨在让学生熟悉并...
- **acquaintance**: 表示认识的人,通常指的是不太熟悉的朋友,动词acquaint用于使人认识或了解某事。 - **part**: 作为动词时指分手或分离,名词parting则表示分裂或分离的状态。 - **shadow**: 指的是物体投下...
在第二课时,教学内容转向书写练习,教授大写字母和小写字母的使用规则,并通过游戏和小组活动加深对字母的认识。此外,还有默写字母接力赛,增强学生的记忆力和团队协作能力。 在第三课时,学生将尝试使用"What’s...
- 掌握单词和目标语句的书写,初步培养在图片和例句提示下写句子的能力。 3. 情感态度目标: - 培养学生对英语学习的兴趣,鼓励他们积极投入,享受学习英语的过程。 - 希望学生愿意在日常生活中运用英语进行交流...
模板使用QWeb语言书写,通过`<t>`标签组织结构,并利用`t-esc`、`t-if`等指令进行动态内容渲染。 #### 二、Owl核心概念实践 为了更好地理解Owl框架的工作原理,接下来我们将通过一系列练习加深对Owl核心概念的认识...
- **模块化规范写法**:模块默认私有,通过 `exports` 或 `module.exports` 提供公共接口。 4. **Npm & Yarn** - **npm**:Node.js 的包管理器,用于安装、发布、管理依赖。 - **nrm**:用于快速切换 npm 的镜像...
这篇文档是针对外研版(三起)五年级英语下册Module 1的测试题,主要涵盖语法、词汇和句型的理解与应用。测试内容包括动词过去式的书写、单项选择题、句子翻译以及选词填空等部分,旨在检验学生对一般过去时态的理解...
首先,我们应当认识到,AngularJS框架使用的是MVC模式中的ViewModel来连接视图层和模型层。在这一层中,所有数据和函数都可以被HTML模板直接引用。当我们遇到一个深度嵌套的对象时,例如: ```javascript $scope....
- 模块化:ES6模块系统(import/export)和CommonJS(require/module.exports)。 2. **React基础**: - 组件:React的核心是组件化,了解组件的声明、props和state,以及如何通过props传递数据。 - 虚拟DOM:...
- **书写规范建议**:为了增强代码的可读性和维护性,建议遵循一定的代码格式和命名规则。 **3.4 数据类型** - **线网类型**:用于描述电路中的连线,如wire、tri等。 - **寄存器类型**:用于存储数据,如reg、...
2.2.4 加入CPU和IP模块...............................................................................................12 2.2.5 指定基地址....................................................................