说明:
这是我做的第一个内核模块的编程,其中的每一步我都认真检查过了,最终也按照其要求成功的加载和注销模块。
项目: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
005 #include
006 #include
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)点。加载或卸载模块时,内核会来寻找这些函数。
第二步:编译模块
如果你习惯使用老办法来编译内核模块(例如,从#define MODULE开始),就会发现新的方法有很大变化。即使是首次编译2.6的模块,看起来也相当简单。该模块的Makefile文件基本内容如下:
Makefile
002 # Makefile for Linux Kernel Primer module skeleton (2.6.7)
006 obj-m += hellomod.o
要注意的是,需要向编译系统特别声明该模块要编译成可加载模块。该Makefile文件的命令行调用由称为doit的脚本文件来打包,如下所示:
(新建一个脚本文件,并把它的属性改成可执行文件
如:
#vim doit
// doit脚本文件的内容为 make -C /usr/src/linux-2.6.7 SUBDIRS=$PWD modules
// 将 /usr/src/linux-2.6.7 改为本机的内核版本(我的是/usr/src/linux-headers-2.6.22-14-generic)
#chmod 744 doit
)
----------------------------------------------------------------------------doit
001 make -C /usr/src/linux-2.6.7 SUBDIRS=$PWD modules
--------------------------------------------------------------------------------
1行:
C选项告诉make程序读取Makefiles或做其他任何事之前,先要修改Linux源目录(本例中是/usr/src/linux-2.6.7)。
执行./doit后可得到与以下内容类似的输出结果:
Lkp# ./doit
make: Entering directory '/usr/src/linux-2.6.7'
CC [M] /mysource/hellomod.o
Building modules, stage 2
MODPOST
CC /mysource/hellomod.o
LD [M] /mysource/hellomod.ko
make: Leaving directory '/usr/src/linux-2.6.7'
lkp# _
如果在Linux早期的版本上编译过或创建过Linux模块,那么此处还有一个链接步骤LD,其输出模块是hellomod.ko。
第三步:运行代码
现在我们已经准备好,可以将新的模块插入到内核中啦!这可以用命令insmod来实现,如下所示:
(非root用户在使用insmod时,在前面加 sudo,否则会因没有足够权限而无法执行
如:
$sudo insmod hellomod.ko
)
lkp# insmod hellomod.ko
lsmod命令可用于检查模块是否正确插入到内核中了:
lkp# lsmod
Module Size Used by
hellomod 2696 0
lkp#
模块的输出由printk()来产生。该函数默认打印系统文件/var/log/messages的内容。快速浏览这些消息可输入如下命令:
lkp# tail /var/log/messages
(可使用”dmesg”命令来查看,我运行的时候使用”tail /var/log/messages “不能显示下面的信息,因为该初始化信息存储在/var/log/kern.log中 )
这一命令打印日志文件的最后10行内容,可以看到我们的初始化信息:
...
...
Mar 6 10:35:55 lkp1 kernel: Hello,World! from the kernel space...
使用rmmod命令,加上我们在insmod中看到的模块名,可以从内核中移除该模块(还可以看到退出时显示的信息)。如下所示:
lkp# rmmod hellomod
同样,输出的内容也在日志文件中,如下所示:
...
...
Mar 6 12:00:05 lkp1 kernel: Hello,World! from the kernel space...
根据x-系统的配置或者是否有基本命令行,printk的输出可以在终端上显示,也可以存放在日志文件中。在下一个项目中,考虑系统的任务变量时会再提到这个问题。
阅读(1134) | 评论(0) | 转发(0) |