这是我做的第一个内核模块的编程,其中的每一步我都认真检查过了,最终也按照其要求成功的加载和注销模块。
项目:Hellomode
本节介绍了一些基本概念,这是理解稍后将讨论的其他Linux概念和数据结构必备的知识。该项目重点在于使用新的2.6驱动程序结构来创建一个可加载模块,并为后面的项目编译该模块。提到设备驱动程序,问题马上就变得复杂了,因此,我们只介绍Linux模块的基本结构,在后面的项目中再来介绍这个驱动程序。该模块在PPC上和x86上均可运行。
准备工作:
1....(环境) ubuntu 7.10
(kernel) 2.6.22-14-generic 可在终端输入“ uname -r ”查看,或 到/usr/src/目录下查看.下面的例子要用到.
(gcc版本) 4.1.3 可在终端输入“ gcc -v ”查看
2....安装kernel必须的开发库 ( 重要! )
#sudo apt-get install linux-kernel-devel
安装内核头文件
#sudo apt-get install linux-headers-`uname -r`
重启.
另外, gcc,make等工具是必须的,请通过查看软件包管理器确保他们的存在.
第一步:构造Linux模块的框架
我们写的第一个模块是基本的“hello world”字符设备驱动程序。首先,考虑该模块的基本代码,然后示范怎样使用新的2.6 Makefile系统(详情参见第九章),最后,分别使用insmod命令和rmmod命令加载或移除1该模块。
-----------------------------------------------------------------------
hellomod.c
001
// hello world driver for Linux 2.6
004 #include <linux/module.h>
005 #include <linux/kernel.h>
006 #include <linux/init.h>
007 #MODULE_LICENCE("GPL"); //get rid of taint message
//此处有错,应为 MODULE_LICENSE(“GPL”);
//前边不带#,且原文中的LICENCE应为LICENSE,否则编译无法通过.
009 static int __init lkp_init( void ) //注意,在int与__init(两下划线)之间要有空格 //,也可直接去掉__init
{
printk("<1>Hello,World! from the kernel space...\n");
return 0;
013 }
015 static void __exit lkp_cleanup( void ) //注意,在void与__exit(两下划线)之间要有空格 //,也可直接去掉__exit
{
printk("<1>Goodbye, World! leaving kernel space...\n");
018 }
020 module_init(lkp_init);
021 module_exit(lkp_cleanup);
-----------------------------------------------------------------------
4行:
所有模块都要使用头文件module.h,此文件必须包含进来。
5行:
头文件kernel.h包含了常用的内核函数。
6行:
头文件init.h包含了宏_init和_exit,它们允许释放内核占用的内存。建议浏览一下该文件中的代码和注释。
7行:
提示可能没有GNU公共许可证。有几个宏是在2.4版的内核中才开发的(详情参见modules.h)。
9-12行:
这是模块的初始化函数,它必需包含诸如要编译的代码、初始化数据结构等内容。11行用printk()从内核发送消息,并提示加载模块后从何处读取该消息。
15-18行:
这是模块的退出和清理函数。此处可以做所有终止该驱动程序时相关的清理工作。
20行:
这是驱动程序初始化的入口点。对于内置模块,内核在引导时调用该入口点;对于可加载模块则在该模块插入内核时才调用。
21行:
对于可加载模块,内核在此处调用cleanup_module()函数,而对于内置的模块,它什么都不做。
在该驱动程序中,仅有一个初始化(module_init)点和一个清理(cleanup_exit)点。加载或卸载模块时,内核会来寻找这些函数。
