分类: LINUX
2011-12-16 10:47:05
转载本站文章请注明,转载自:扶凯[]
本文链接: http://www.php-oa.com/2011/09/27/linux-kernel-dev.html
个人笔记。。在不放过来都快找不到了。有空还得好好整理一下了。
调试方法注:如果是 redhat 安装的话,需要安装 kernel-devel 才能写内核模块,如果是自己编译内核,记的不要删除源码,不然没法开发模块。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | #include #include #include /* __init 的标记是内核模块的入口,这个函数加载完后就会释放内存空间 */ static int __init hello_init(void) { printk(KERN_INFO "Hello world"); /* 打印的信息会出现在 dmesg 中 释放*/ return 0; /* 返回 0 是正常 */ } /* __exit 的标记是退出内核模块,当这个模块卸载时会执行 */ static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye world"); } /* 下面这二个是宏,初始化和消除函数时使用和上面的装载卸载模块没关系。 */ module_init(hello_init); module_exit(hello_exit); |
放个编译上面模块的 Makefile
1 2 3 4 5 | obj-m := hello.o all: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean |
给 Makefile 放到上面 hello.c 的相同的目录中(如果上面写的模块代码叫 hello.c 的话)。然后使用 make 就能编译了。
insmod lsmod rmmod调用 insmod 时会给需要的模块加载进内核,会给 ko 的文件以目标代码加载。装载时会调用 module_init 指定的函数。退出也调用相应的 module_exit.
lsmod 可以显示你写的模块,其实是读 /proc/modules 。接下来我写写怎么样自己通过内核来建 proc 文件。
模块加载参数如果在模块加载时,想指定参数,也提供了相应的头文件
1 2 3 | #include static int test; module_param(test, int, 0644); |
这样以后,直接在内核模块内使用 test 的变量就行了。
模块的信息在程序中可以为模块加一些描述,发行版权声明,和作者。
1 2 3 | MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Test"); MODULE_AUTHOR("xxx"); |
在 Perl 中,模块是可以导出变量和方法到其它的模块中的。在 Linux 内核中也有这样的方法。
1 2 | EXPORT_SYMBOL(name); EXPORT_SYMBOL_GPL(name); |
这二个可以导出指定的全局变量,也可以是方法。这个要加载
其它的模块要使用这个,直接使用 extern void name(void); 就可以使用了。这些导出的函数只能内核和内核模块使用。不能用户调用,可以由 /proc/kallsyms 来查看导出的变量和方法
写个内核模块,通过 proc 可以见到一些信息,通过 proc 的读和写的功能。来实现设置和读取信息。
proc 介绍proc 是一个非常方便的用来动态的向 Linux 内核加入和禁用代码的一个方法。
proc/sys 中是用来配置内核的参数,可以通过 sysctl -w key=value
象普通文件可以支持 open,read,write,close
例如
读
1 | cat /proc/cpuinfo |
写
1 | echo fukei-name > /proc/sys/kernel/hostname |
proc 的功能实现
proc 在 c 中是一个结构体来实现的,是 struct proc_dir_entry 。它可以给读写绑定到特定的函数上。然后通过别人对 proc 中文件的操作来触发和回调相应的绑定的函数。
read_proc 和 write_proc 是这个结构体的成员,也是一种结构体。函数就注册在这个上面。有兴趣的同学可以看看 include/linux/proc_fs.h 中的 read_proc_t 和 write_proc_t 的定义。
实现起来也简单。
1 2 3 4 5 | struct proc_dir_entry *proc_entry = create_proc_entery(....); int my_read_proc() { } proc_entry->read_proc = my_read_proc(); |
在这的 create_proc_entery 会返回一个 proc_dir_entry 的结构体的引用。失败就是 NULL 。
这样,当用户空间进行 read 的系统调用时,如使用 cat proc 中的内容时。内核会调用注册到 read_proc 上的这个 my_read_proc 来实现的.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include #include #include #include #define MODULE_NAME "Memory" int my_read_proc(char *page,char **start, off_t off,int count, int *eof,void *data) { struct task_struct *tsk = current; int len; len = sprintf( page, "This module info: task %s pid %d\n",tsk->comm, tsk->pid ); return len; } struct proc_dir_entry *proc_entry; int init_module(void) { proc_entry = create_proc_entry(MODULE_NAME, 0644, NULL); if (proc_entry==NULL){ remove_proc_entry(MODULE_NAME, NULL); } proc_entry->read_proc = my_read_proc; return 0; } void cleanup_module(void) { remove_proc_entry(MODULE_NAME, NULL); // 退出和出错记的删除 } MODULE_LICENSE("GPL"); |