构造和运行模块
(一)Hello World 模块
- #include <linux/init.h>
- #include <linux/module.h>
- MODULE_LICENSE("Dual BSD/GPL");
- static int hello_init(void)
- {
- printk(KERN_ALERT "Hello, world\n");
- return 0;
- }
- static void hello_exit(void)
- {
- printk(KERN_ALERT "Goodbye, cruel world\n");
- }
- module_init(hello_init);
- module_exit(hello_exit);
此段代码来自ldd3的例子,由于之前稍微接触过驱动模块等相关皮毛知识,我看到这段代码产生了疑问:初始化和卸载函数怎么没有被 __init,__exit 修饰,书中提到: __init是一种暗示,它表明该函数仅在初始化时被使用,模块装载完毕后,它会被扔到,将模块初始化函数所占的内存释放出来,以便把无用的代码所占的内存腾出来给其它模块使用。
那么这个 __init是个什么东西?在 /include/linux/init.h 中就有它的定义:
- #define __init __section(.init.text) __cold notrace
后面的 _cold notrace,是gcc编译相关的一些扩展宏,具体什么作用没仔细看,但是经过展开, __init会变成:- #define __init __attribute__ ((__section__ (".init.text")))
__attribute__ 也是GNU C的扩展,它主要设置代码的一些属性,以便代码的优化,布局,安全等更好。单从字面上看 __section__ 就知道这是对代码布局属性进行设置,一段代码最基本的段有:代码的(.text),数据段(.data),BSS段(.bss)等。其他一些扩展的段加起来超过十多个,具体多少不知道。这里就有个 .init.text 从字面意思姑且叫做:初始化代码段。ldd3上说这段在执行完后会被释放,这个段在vmlinux.lds这个内核连接脚本里面声明。 除了__init还有__initdata,内核代码中,对这段有样的说明:
- /* These macros are used to mark some functions or
- * initialized data (doesn't apply to uninitialized data)
- * as `initialization' functions. The kernel can take this
- * as hint that the function is used only during the initialization
- * phase and free up used memory resources after
- *
- * Usage:
- * For functions:
- *
- * You should add __init immediately before the function name, like:
- *
- * static void __init initme(int x, int y)
- * {
- * extern int z; z = x * y;
- * }
- *
- * If the function has a prototype somewhere, you can also add
- * __init between closing brace of the prototype and semicolon:
- *
- * extern int initialize_foobar_device(int, int, int) __init;
- *
- * For initialized data:
- * You should insert __initdata between the variable name and equal
- * sign followed by value, e.g.:
- *
- * static int init_variable __initdata = 0;
- * static char linux_logo[] __initdata = { 0x32, 0x36, ... };
- *
- * Don't forget to initialize data not at file scope, i.e. within a function,
- * as gcc otherwise puts the data into the bss section and not into the init
- * section.
- *
- * Also note, that this data cannot be "const".
- */
- /* These are for everybody (although not all archs will actually
- discard it in modules) */
需要注意的是: 如果一个函数的原型声明在别处,也可以用__init修饰:
- extern int initialize_foobar_device(int, int, int) __init;
修饰数据用 __initdata,用法:- * static int init_variable __initdata = 0;
- * static char linux_logo[] __initdata = { 0x32, 0x36, ... };
尤其注意的是一定要对这些数据初始化,并且要在函数外部,否则他们会被编译器扔到BSS段。还有就是不要用"const"修饰这类数据,这是显而易见的。 __exit,__exitdata用法同__init,__initdata类似,放于.exit.text段
对于更深的:http://blog.csdn.net/muge0913/article/details/7251326
一般模块里面的函数都申明为 static ,既然它被称之为模块,那么要尽量让它们之间的关联性减小,static就是基于此的,当然这只是我的理解。
- module_init(hello_init);
- module_exit(hello_exit);
这2个函数是必须的,模块的入口和出口都是通过它引导的。这是2个宏:- #define module_init(x) __initcall(x);
- #define module_exit(x) __exitcall(x);
- #define __define_initcall(level,fn,id) \
- static initcall_t __initcall_##fn##id __used \
- __attribute__((__section__(".initcall" level ".init"))) = fn
又是对段属性进行修改,我想肯定有相应的程序维护这些特殊地段。
虽然模块要尽量减小关联性,但是有时候不得已或者说很有必要很外部交流下,所以就有:
- #define EXPORT_SYMBOL(sym) \
- __EXPORT_SYMBOL(sym, "")
- #define EXPORT_SYMBOL_GPL(sym) \
- __EXPORT_SYMBOL(sym, "_gpl")
- #define EXPORT_SYMBOL_GPL_FUTURE(sym) \
- __EXPORT_SYMBOL(sym, "_gpl_future")
后面2个仅限对GPL下的模块,对他们深究,又是段属性:http://www.cnblogs.com/mywolrd/archive/2009/04/01/1930696.html
http://blog.sina.com.cn/s/blog_701142a40100m45h.html
之前一直以为只能对变量,貌似对函数也可以。
(二)模块参数
阅读(2093) | 评论(0) | 转发(1) |