Chinaunix首页 | 论坛 | 博客
  • 博客访问: 252150
  • 博文数量: 52
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 625
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-03 08:39
文章分类
文章存档

2011年(4)

2010年(5)

2009年(6)

2008年(37)

我的朋友

分类: LINUX

2008-03-27 21:45:17

说明:
    这是我做的第一个内核模块的编程,其中的每一步我都认真检查过了,最终也按照其要求成功的加载和注销模块。

项目: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的输出可以在终端上显示,也可以存放在日志文件中。在下一个项目中,考虑系统的任务变量时会再提到这个问题。

阅读(1107) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~