linux设备驱动学习0.docx

上传人:b****1 文档编号:3501233 上传时间:2023-05-05 格式:DOCX 页数:9 大小:20.49KB
下载 相关 举报
linux设备驱动学习0.docx_第1页
第1页 / 共9页
linux设备驱动学习0.docx_第2页
第2页 / 共9页
linux设备驱动学习0.docx_第3页
第3页 / 共9页
linux设备驱动学习0.docx_第4页
第4页 / 共9页
linux设备驱动学习0.docx_第5页
第5页 / 共9页
linux设备驱动学习0.docx_第6页
第6页 / 共9页
linux设备驱动学习0.docx_第7页
第7页 / 共9页
linux设备驱动学习0.docx_第8页
第8页 / 共9页
linux设备驱动学习0.docx_第9页
第9页 / 共9页
亲,该文档总共9页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

linux设备驱动学习0.docx

《linux设备驱动学习0.docx》由会员分享,可在线阅读,更多相关《linux设备驱动学习0.docx(9页珍藏版)》请在冰点文库上搜索。

linux设备驱动学习0.docx

linux设备驱动学习0

Linux设备驱动程序学习(0)

-设备驱动介绍&Hello,world!

模块

设备驱动程序的作用

设备驱动程序就是这个进入Linux内核世界的大门。

设备驱动程序在Linux内核中扮演着特殊的角色。

它是一个独立的“黑盒子”,使某个特定硬件响应一个定义好的内部编程接口,这些接口完全隐藏了设备的工作细节。

用户的操作通过一组标准化的调用执行,而这些调用独立于特定的驱动程序。

将这些调用映射到作用于实际硬件的设备特有操作上,则是设备驱动程序的任务。

设备驱动的分类

字符设备:

字符(char)设备是个能够像字节流(类似文件)一样被访问的设备。

字符设备驱动程序通常至少要实现open、close、read和write系统调用。

块设备:

一个块设备驱动程序主要通过传输固定大小的数据来访问设备。

块设备和字符设备的区别仅仅在于内核内部管理数据的方式,也就是内核及驱动程序之间的软件接口,而这些不同对用户程序是透明的。

在内核中,和字符驱动程序相比,块驱动程序具有完全不同的接口。

网络接口:

任何网络事务都经过一个网络接口形成,即一个能够和其他主机交换数据的设备。

它可以是个硬件设备,但也可能是个纯软件设备。

访问网络接口的方法仍然是给它们分配一个唯一的名字(比如eth0),但这个名字在文件系统中不存在对应的节点。

内核和网络设备驱动程序间的通信,完全不同于内核和字符以及块驱动程序之间的通信,内核调用一套和数据包传输相关的函数而不是read、write等。

驱动模块的特点

(1)驱动模块运行在内核空间,运行时不能依赖于任何标准C库等应用层的库、模块,所以在写驱动时所调用的函数只能是作为内核一部分的函数,即使用“EXPORT_SYMBOL”导出的函数。

insmod使用公共内核符号表来解析模块中未定义的符号。

公共内核符号表中包含了所有的全局内核项(即函数和变量的地址),这是实现模块化驱动程序所必须的。

Linux使用模块层叠技术,我们可以将模块划分为多个层,通过简化每个层可缩短开发周期。

如果一个模块需要向其他模块导出符号,则使用下面的宏:

EXPORT_SYMBOL(name);

EXPORT_SYMBOL_GPL(name);

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

(2)驱动模块和应用程序的一个重要不同是:

应用程序退出时可不管资源释放或者其他的清除工作,但模块的退出函数必须仔细撤销初始化函数所作的一切,否则,在系统重新引导之前某些东西就会残留在系统中。

(3)处理器的多种工作模式(级别)其实就是为了操作系统的用户空间和内核空间设计的。

在Unix类的操作系统中只用到了两个级别:

最高和最低级别。

(4)要十分注意驱动程序的并发处理。

(5)内核API中具有双下划线(__)的函数,通常是接口的底层组件,应慎用。

(6)内核代码不能实现浮点数运算。

参考资料:

 

--[endif]-->模块结构介绍

利用Linux设备驱动程序的第一个例程:

HelloWorld模块了解内核驱动模块的结构。

#include 

#include  

static int hello_init(void)

{

    printk(KERN_ALERT "Hello,TekkamanNinja!

\n");

    return 0;

}

static void hello_exit(void)

{

    printk(KERN_ALERT "Goodbye,TekkamanNinja!

\nLoveLinux!

LoveARM!

LoveKeKe!

\n");

}

module_init(hello_init);

module_exit(hello_exit);

 

MODULE_LICENSE("DualBSD/GPL");

 

--[if!

supportLists]-->1.  

--[endif]-->所有模块代码中都包含一下两个头文件:

#include 

#include 

--[if!

supportLists]-->2.  

--[endif]-->所有模块代码都应该指定所使用的许可证:

MODULE_LICENSE("DualBSD/GPL");

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

MODULE_AUTHOR("");

MODULE_DESCRIPTION("");

MODULE_VERSION("");

MODULE_ALIAS("");

MODULE_DEVICE_TABLE("");

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

--[if!

supportLists]-->3.  

--[endif]-->初始化和关闭

初始化的实际定义通常如下:

static int __init initialization_function(void)

{

/*初始化代码*/

}

module_init(initialization_function)

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

static int __exit cleanup_function(void)

{

/*清除代码*/

}

module_exit(cleanup_function)

 4.  一个简单的Makefile文件:

KERNELDIR = /home/tekkaman/working/SBC2440/linux-2.6.22.2

PWD :

= $(shellpwd)

INSTALLDIR=/home/tekkaman/working/rootfs/lib/modules

CROSS_COMPILE    =arm-9tdmi-linux-gnu-

CC    = $(CROSS_COMPILE)gcc

obj-m :

= hello.o

.PHONY:

 modulesmodules_installclean

modules:

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

modules_install:

cphello.ko$(INSTALLDIR)

clean:

rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

obj-m :

= hello.o

代表了我们要构造的模块名为hell.ko,make会在该目录下自动找到hell.c文件进行编译。

如果 hello.o是由其他的源文件生成(比如file1.c和file2.c)的,则在下面加上(注意红色字体的对应关系):

hello-objs :

=file1.ofile2.o......

 

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

-C$(KERNELDIR) 指定了内核源代码的位置,其中保存有内核的顶层makefile文件。

M=$(PWD) 指定了模块源代码的位置

modules目标指向obj-m变量中设定的模块。

--[if!

supportLists]-->5.  

--[endif]-->编译模块

make modules、make modules_install。

[root@Tekkaman-NinjaHelloworld]#makemodules

make-C/home/tekkaman/working/SBC2440/linux-2.6.22.2M=/home/tekkaman/working/Linuxdriver/Helloworldmodules

make[1]:

Enteringdirectory`/home/tekkaman/working/SBC2440/linux-2.6.22.2'

 CC[M] /home/tekkaman/working/Linuxdriver/Helloworld/hello.o

 Buildingmodules,stage2.

 MODPOST1modules

 CC     /home/tekkaman/working/Linuxdriver/Helloworld/hello.mod.o

 LD[M] /home/tekkaman/working/Linuxdriver/Helloworld/hello.ko

make[1]:

Leavingdirectory`/home/tekkaman/working/SBC2440/linux-2.6.22.2'

[root@Tekkaman-NinjaHelloworld]#makemodules_install

cphello.ko/home/tekkaman/working/rootfs/lib/modules

[root@Tekkaman-NinjaHelloworld]# 

--[if!

supportLists]-->6.  

--[endif]-->在开发板上的操作:

[Tekkaman2440@SBC2440V4]#cd/lib/modules/

[Tekkaman2440@SBC2440V4]#ls

cs89x0.ko hello.ko p80211.koprism2_usb.ko 

[Tekkaman2440@SBC2440V4]#insmodhello.ko 

Hello,TekkamanNinja!

[Tekkaman2440@SBC2440V4]#lsmod 

ModuleSizeUsedbyNottainted 

hello13760 

[Tekkaman2440@SBC2440V4]#rmmodhello 

Goodbye,TekkamanNinja!

LoveLinux!

LoveARM!

LoveKeKe!

 

[Tekkaman2440@SBC2440V4]#lsmod 

ModuleSizeUsedbyNottainted 

[Tekkaman2440@SBC2440V4]#

Linux内核模块的初始化出错处理一般使用“goto”语句。

通常情况下很少使用“goto”,但在出错处理是(可能是唯一的情况),它却非常有用。

在大二学习C语言时,老师就建议不要使用“goto”,并说很少会用到。

在这里也是我碰到的第一个建议使用“goto”的地方。

“在追求效率的代码中使用goto语句仍是最好的错误恢复机制。

”--《Linux设备驱动程序(第3版)》以下是初始化出错处理的推荐代码示例:

structsomething*item1;

structsomethingelse*item2;

intstuff_ok;

voidmy_cleanup(void)

{

   if(item1)

      release_thing(item1);

   if(item2)

      release_thing2(item2);

   if(stuff_ok)

      unregister_stuff();

   return;

}

int__initmy_init(void)

{

   interr=-ENOMEM;

   item1=allocate_thing(arguments);

   item2=allocate_thing2(arguments2);

   if(!

item2||!

item2)

      gotofail;

   err=register_stuff(item1,item2);

   if(!

err)

      stuff_ok=1;

   else

      gotofail;

   return0;/*success*/

 fail:

      my_cleanup();

      returnerr;

}

模块参数

内核允许对驱动程序指定参数,而这些参数可在装载驱动程序模块时改变。

以下是我的实验程序:

#include

#include

#include

MODULE_LICENSE("DualBSD/GPL");

staticchar*whom="TekkamanNinja";

staticinthowmany=1;

staticintTNparam[]={1,2,3,4};

staticintTNparam_nr=4;

module_param(howmany,int,S_IRUGO);

module_param(whom,charp,S_IRUGO);

module_param_array(TNparam,int,&TNparam_nr,S_IRUGO);

staticinthello_init(void)

{

    inti;

    for(i=0;i

        printk(KERN_ALERT"(%d)Hello,%s!

\n",i,whom);

    for(i=0;i<8;i++)

        printk(KERN_ALERT"TNparam[%d]:

%d\n",i,TNparam[i]);

    return0;

}

staticvoidhello_exit(void)

{

    printk(KERN_ALERT"Goodbye,TekkamanNinja!

\nLoveLinux!

LoveARM!

LoveKeKe!

\n");

}

module_init(hello_init);

module_exit(hello_exit);

实验结果是:

[Tekkaman2440@SBC2440V4]#cd/lib/modules/

[Tekkaman2440@SBC2440V4]#ls

cs89x0.kohello.koprism2_usb.ko

hello-param.kop80211.ko

[Tekkaman2440@SBC2440V4]#insmodhello-param.kohowmany=2whom="KeKe"TNparam=4,3,2,1

(0)Hello,KeKe!

(1)Hello,KeKe!

TNparam[0]:

4

TNparam[1]:

3

TNparam[2]:

2

TNparam[3]:

1

TNparam[4]:

1836543848

TNparam[5]:

7958113

TNparam[6]:

1836017783

TNparam[7]:

0

[Tekkaman2440@SBC2440V4]#insmodhello-param.kohowmany=2whom="KeKe" TNparam=4,3,2,1,5,6,7,8

TNparam:

canonlytake4arguments

hello_param:

`4'invalidforparameter`TNparam'

insmod:

cannotinsert'hello-param.ko':

Invalidparameters(-1):

Invalidargument

[Tekkaman2440@SBC2440V4]# 

我这个实验除了对参数的改变进行实验外,我的一个重要的目的是测试“module_param_array(TNparam,int,&TNparam_nr,S_IRUGO);”中&TNparam_nr对输入参数数目的限制作用。

经过我的实验,表明&TNparam_nr并没有对输入参数的数目起到限制作用。

真正起到限制作用的是“staticintTNparam[]={1,2,3,4};”本身定义的大小,我将程序进行修改:

staticintTNparam[]={1,2,3,4}; 

改为staticintTNparam[]={1,2,3,4,5,6,7,8};

其他都不变。

编译后再进行实验,其结果是:

[Tekkaman2440@SBC2440V4]#insmodhello-param.kohowmany=2whom="KeKe"TNparam=4,3,2,1,5,6,7,8

(0)Hello,KeKe!

(1)Hello,KeKe!

TNparam[0]:

4

TNparam[1]:

3

TNparam[2]:

2

TNparam[3]:

1

TNparam[4]:

5

TNparam[5]:

6

TNparam[6]:

7

TNparam[7]:

8

[Tekkaman2440@SBC2440V4]#

(15)“#include” 最重要的头文件之一。

包含驱动程序使用的大部分内核API的定义,包括睡眠函数以及各种变量声明。

(16)“#include”包含所构造内核版本信息的头文件。

在学习过程中找到了几篇很好的参考文档:

(1)第一章模块(Modules)URL:

(2)《从2.4到2.6:

Linux内核可装载模块机制的改变对设备驱动的影响》

URL:

(3)《Linux2.6内核驱动移植参考》

URL:

以上就是我对《Linux设备驱动程序(第3版)》的《第二章构造和运行模块》的学习总结。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 党团工作 > 入党转正申请

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2