Chinaunix首页 | 论坛 | 博客
  • 博客访问: 517474
  • 博文数量: 187
  • 博客积分: 3011
  • 博客等级: 中校
  • 技术积分: 2092
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-28 17:08
文章分类

全部博文(187)

文章存档

2011年(1)

2010年(8)

2009年(178)

我的朋友

分类: LINUX

2009-07-30 20:12:34

 

一个学习Linux设备驱动程序都会碰到的第一个例程:

/*======================================================================

    A simple kernel module: "hello world"

        

    The initial developer of the original code is Baohua Song

    . All Rights Reserved.

======================================================================*/

#include

#include

#include

 

MODULE_LICENSE("Dual BSD/GPL");

 

 

static int hello_init(void)

{

  printk(KERN_EMERG "Driver Hello World enter\n");

  return 0;

}

 

static void hello_exit(void)

{

  printk(KERN_EMERG "Unloading Driver World\n");

 

}

 

module_init(hello_init);

module_exit(hello_exit);

 

MODULE_AUTHOR("Michael@linux");

MODULE_DESCRIPTION("A simple Hello World Module");

MODULE_ALIAS("a simplest module");

 

 

一个简单的Makefile文件:

#KERNELDIR       :=/home/jimmy/kernel_2.6.18/mylinux-2.6.18_ok

KERNELDIR  :=/usr/src/linux-headers-2.6.24-16-generic

PWD             :=$(shell pwd)

#CROSS_COMPILE   =/usr/local/arm/3.4.1/bin/arm-linux-

CC          =$(CROSS_COMPILE)gcc

 

obj-m             :=hello.o

 

modules:

       make -C $(KERNELDIR) M=$(PWD) modules

clean:

       rm -rf *.o

       rm -rf *.mod.*

       rm -rf *.ko

       rm -rf *.symvers

 

 

 

学习心得:
1)驱动模块运行在内核空间,运行时不能依赖于任何函数库和模块连接,所以在写驱动时所调用的函数只能是作为内核一部分的函数。
2)驱动模块和应用程序的一个重要不同是:应用程序退出时可不管资源释放或者其他的清除工作,但模块的退出函数必须仔细撤销初始化函数所作的一切,否则,在系统重新引导之前某些东西就会残留在系统中。
3)处理器的多种工作模式(级别)其实就是为了操作系统的用户空间和内核空间设计的。在Unix类的操作系统中只用到了两个级别:最高和最低级别。
4)要十分注意驱动程序的并发处理。
5)内核API中具有双下划线(_ _)的函数,通常是接口的底层组件,应慎用。
6)内核代码不能实现浮点书运算。

 

 

 

 

 


8insmod使用公共内核符号表来解析模块中未定义的符号。公共内核符号表中包含了所有的全局内核项(即函数和变量的地址),这是实现模块化驱动程序所必须的。
9Linux使用模块层叠技术,我们可以将模块划分为多个层,通过简化每个层可缩短开发周期。如果一个模块需要向其他模块到处符号,则使用下面的宏:

EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);

符号必须在模块文件的全局变量部分导出,因为这两个宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。

10)所有模块代码中都包含一下两个头文件:

#include <linux/init.h>
#include <linux/module.h>


11)所有模块代码都应该指定所使用的许可证:

MODULE_LICENSE("Dual BSD/GPL");


此外还有可选的其他描述性定义:

MODULE_AUTHOR("");
MODULE_DESCRIPTION("");
MODULE_VERSION("");
MODULE_ALIAS("");
MODULE_DEVICE_TABLE("");


上述MODULE_声明习惯上放在文件最后。

12)初始化和关闭
初始化的实际定义通常如下:

static int _ _init initialization_function(void)
{
/*初始化代码*/
}

module_init(initialization_function)


清除函数的实际定义通常如下:

static int _ _exit cleanup_function(void)
{
/*清除代码*/
}

module_exit(cleanup_function)

 

 

 

13)模块参数:内核允许对驱动程序指定参数,而这些参数可在装载驱动程序模块时改变
以下是我的实验程序:

#include

#include

#include

 

MODULE_LICENSE("Dual BSD/GPL");

 

static char *whom = "michael@linux";

static int howmany=10;

static int nu[]={1,2,3,4,5,6,7,8};

module_param(whom,charp,S_IRUGO);

module_param(howmany,int,S_IRUGO);

module_param_array(nu,int,8,S_IRUGO);

 

 

static int hello_init(void)

{

  int i;

  for(i=0;i

  printk(KERN_EMERG "whom= %s\n",whom);

  }

  for(i=0;i<15;i++){

  printk(KERN_EMERG "nu[%d]=%d\n",i,nu[i]);

  }

  return 0;

}

 

static void hello_exit(void)

{

  printk(KERN_EMERG "Unloading Driver World\n");

 

}

 

module_init(hello_init);

module_exit(hello_exit);

 

MODULE_AUTHOR("Michael@linux");

MODULE_DESCRIPTION("A simple Hello World Module");

MODULE_ALIAS("a simplest module");

 

输出如下:

[869921.544471] whom= michael@linux

[869921.544503] whom= michael@linux

[869921.544506] whom= michael@linux

[869921.544508] whom= michael@linux

[869921.544509] whom= michael@linux

[869921.544511] whom= michael@linux

[869921.544513] whom= michael@linux

[869921.544515] whom= michael@linux

[869921.544516] whom= michael@linux

[869921.544518] whom= michael@linux

[869921.544543] nu[0]=1

[869921.544567] nu[1]=2

[869921.544579] nu[2]=3

[869921.544581] nu[3]=4

[869921.544583] nu[4]=5

[869921.544585] nu[5]=6

[869921.544586] nu[6]=7

[869921.544588] nu[7]=8

[869921.544590] nu[8]=0

[869921.544591] nu[9]=0

[869921.544593] nu[10]=0

[869921.544594] nu[11]=0

[869921.544596] nu[12]=0

[869921.544598] nu[13]=0

[869921.544599] nu[14]=0

 

 

module_param_array(nu,int,8,S_IRUGO);

这个宏里的8好像在数组里没有起到什么作用,有点疑问……

 

14#include <linux/sched.h>”  最重要的头文件之一。包含驱动程序使用的大部分内核API的定义,包括睡眠函数以及各种变量声明。

15#include <linux/version.h>包含所构造内核版本信息的头文件。

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