论Linux Kernel Module之设备驱动程序Word格式文档下载.docx
- 文档编号:5822509
- 上传时间:2023-05-05
- 格式:DOCX
- 页数:28
- 大小:33.10KB
论Linux Kernel Module之设备驱动程序Word格式文档下载.docx
《论Linux Kernel Module之设备驱动程序Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《论Linux Kernel Module之设备驱动程序Word格式文档下载.docx(28页珍藏版)》请在冰点文库上搜索。
3.5设备的中断和轮询处理
4设备驱动程序接口
第4章Linux设备驱动程序的实现…………………………30
1PCI驱动程序实现的关键数据结构
1.1pci_driver结构
1.2pci_dev结构
2.写驱动程序的fuctoin问题
2.1function的encode问题
2.2function的export问题
2.3两个常用function
3设备驱动程序中的一些具体问题
3.1I/OPort
3.2内存操作
3.3中断处理
4RealtekFastEthernetDriverrtl8139网卡驱动3个模块的改写
4.1设备指明模块
4.2数据读写和控制信息模块
4.3中断处理模块
参考文献……………………………………………………….25
附录…………………………………………………………….26
致谢……………………………………………………………28
【摘要】驱动程序编写方法。
由Linux设备驱动程序编写方式着手,转而研究设备驱动程序的KernelModule程序。
在了解了Linux下的驱动设备类型之后,从Linux下设备驱动程序的功能,组成部分,结构,接口4个方面分析了设备驱动程的框架。
在参看了许多的书籍和网络论坛的文章,资料,具体讨论了Linux下设备驱动程序的实现,具体研究了驱动程序的数据结构,function问题和编写程序时各个部分可能出现的难点,疑点问题。
并根据DonaldBecker1999-2000年编写的RealtekFastEthernetDriverrtl8139网卡驱动Linux版(内核版本2.0.24)改写了其中的3个模块:
设备指明模块,数据读写和控制信息模块与中断处理模块。
【关键词】KernelLinux内核
KernelModuleLinux的内核模块
CharacterDevices字符设备
BlockDevices块设备
Funtion函数
前言
Linux是最受欢迎的自由电脑操作系统内核。
它是一个用C语言写成,符合POSIX标准的类Unix操作系统。
Linux最早是由芬兰黑客LinusTorvalds为尝试在英特尔x86架构上提供自由免费的类Unix操作系统而开发的。
该计划开始于1991年,从LinusTorvalds当时在Usenet新闻组comp.os.minix所登载了一分著名的贴子,标志了Linux计划的正式开始。
在计划的早期有一些Minix黑客提供了协助,而今天全球无数程序员正在为该计划无偿提供帮助。
技术上说Linux是一个内核。
“内核”指的是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。
一个内核不是一套完整的操作系统。
一套基于Linux内核的完整操作系统叫作Linux操作系统,或是GNU/Linux。
今天Linux是一个一体化内核系统。
设备驱动程序可以完全访问硬件。
Linux内的设备驱动程序可以方便地以模块的形式设置,并在系统运行期间可直接装载或卸载。
第1章Linux设备驱动程序编写方式
Linux下的设备驱动程序可以按照两种方式进行编译,一种是直接静态编译成内核的一部分,另一种则是编译成可以动态加载的模块。
如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态地卸载,不利于调试,所有推荐使用模块方式。
从本质上来讲,模块也是内核的一部分,它不同于普通的应用程序,不能调用位于用户态下的C或者C++库函数,而只能调用Linux内核提供的函数,在/proc/ksyms中可以查看到内核提供的所有函数。
module的出现是Linux的一大革新。
有了module之后,写devicedriver不需要每次要测试driver就重新compilekernel一次。
避免了很多的麻烦。
Module允许我们动态的改变kernel,加载devicedriver,而且它也能缩短我们driverdevelopment的时间。
module就是模块。
module其实是一般的程序。
但是它可以被动态载到kernel里成为kernel的一部分。
载到kernel里的module它具有跟kernel一样的权力。
可以access任何kernel的datastructure。
第2章Linux下的驱动设备类型
正文格式
字符设备,Linux最简单的设备,象文件一样访问。
应用程序使用标准系统调用打开、读取、写和关闭,完全好像这个设备是一个普通文件一样。
甚至连接一个Linux系统上网的PPP守护进程使用的modem,也是这样的。
当字符设备初始化的时候,它的设备驱动程序向Linux核心登记,在chrdevs向量表增加一个device_struct数据结构条目。
这个设备的主设备标识符(例如对于tty设备是4),用作这个向量表的索引。
一个设备的主设备标识符是固定的。
Chrdevs向量表中的每一个条目,一个device_struct数据结构,包括两个元素:
一个登记的设备驱动程序的名称的指针和一个指向一组文件操作的指针。
这块文件操作本身位于这个设备的字符设备驱动程序中,每一个都处理特定的文件操作比如打开、读、写和关闭。
/proc/devices中字符设备的内容来自chrdevs向量表
当代表一个字符设备(例如/dev/cua0)的字符特殊文件打开,核心必须做一些事情,从而去掉用正确的字符设备驱动程序的文件操作例程。
和普通文件或目录一样,每一个设备特殊文件都用VFSI节点表达。
这个字符特殊文件的VFSinode(实际上所有的设备特殊文件)都包括设备的major和minor标识符。
这个VFSI节点由底层的文件系统(例如EXT2),在查找这个设备特殊文件的时候根据实际的文件系统创建。
每一个VFSI节点都联系着一组文件操作,依赖于I节点所代表的文件系统对象不同而不同。
不管代表一个字符特殊文件的VFSI节点什么时候创建,它的文件操作被设置成字符设备的缺省操作。
这只有一种文件操作:
open操作。
当一个应用程序打开这个字符特殊文件的时候,通用的open文件操作使用设备的主设备标识符作为chrdevs向量表中的索引,取出这种特殊设备的文件操作块。
它也建立描述这个字符特殊文件的file数据结构,让它的文件操作指向设备驱动程序中的操作。
然后应用程序所有的文件系统操作都被映射到字符设备的文件操作。
块设备也支持象文件一样被访问。
这种为打开的块特殊文件提供正确的文件操作组的机制和字符设备的十分相似。
Linux用blkdevs向量表维护已经登记的块设备文件。
它象chrdevs向量表一样,使用设备的主设备号作为索引。
它的条目也是device_struct数据结构。
和字符设备不同,块设备进行分类。
SCSI是其中一类,而IDE是另一类。
类向Linux核心登记并向核心提供文件操作。
一种块设备类的设备驱动程序向这种类提供和类相关的接口。
例如,SCSI设备驱动程序必须向SCSI子系统提供接口,让SCSI子系统用来对核心提供这种设备的文件操作。
每一个块设备驱动程序必须提供普通的文件操作接口和对于buffercache的接口。
每一个块设备驱动程序填充blk_dev向量表中它的blk_dev_struct数据结构。
这个向量表的索引还是设备的主设备号。
这个blk_dev_struct数据结构包括一个请求例程的地址和一个指针,指向一个request数据结构的列表,每一个都表达buffercache向设备读写一块数据的一个请求。
每一次buffercache希望读写一块数据到或从一个登记的设备的时候它就在它的blk_dev_struc中增加一个request数据结构。
图8.2显示了每一个request都有一个指针指向一个或多个buffer_head数据结构,每一个都是一个读写一块数据的请求。
这个buffer_head数据结构被锁定(buffercache),可能会有一个进程在等待这个缓冲区的阻塞进程完成。
每一个request结构都是从一个静态表,all_request表中分配的。
如果这个request增加到一个空的request列表,就调用驱动程序的request函数处理这个request队列。
否则,驱动程序只是简单地处理request队列中的每一个请求。
一旦设备驱动程序完成了一个请求,它必须把每一个buffer_head结构从request结构中删除,标记它们为最新的,然后解锁。
对于buffer_head的解锁会唤醒任何正在等待这个阻塞操作完成的进程。
这样的例子包括文件解析的时候:
必须等待EXT2文件系统从包括这个文件系统的块设备上读取包括下一个EXT2目录条目的数据块,这个进程将会在将要包括目录条目的buff_head队列中睡眠,直到设备驱动程序唤醒它。
这个request数据结构会被标记为空闲,可以被另一个块请求使用。
字符设备是以字节为单位逐个进行I/O操作的设备,在对字符设备发出读写请求时,实际的硬件I/O紧接着就发生了,一般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。
块设备则是利用一块系统内存作为缓冲区,当用户进程对设备进行读写请求时,驱动程序先查看缓冲区中的内容,如果缓冲区中的数据能满足用户的要求就返回相应的数据,否则就调用相应的请求函数来进行实际的I/O操作。
块设备主要是针对磁盘等慢速设备设计的,其目的是避免耗费过多的CPU时间来等待操作的完成。
一般说来,PCI卡通常都属于字符设备。
第2章Linux设备驱动程序的框架
1.1对设备初始化和释放
1.2把数据从内核传送到硬件和从硬件读取数据
1.3读取应用程序传送给设备文件的数据和回送应用程序请求的数据
1.4检测和处理设备出现的错误.
2设备驱动程序的组成部分
2.1自动配置和初始化子程序
自动配置和初始化子程序,负责检测所要驱动的硬件设备是否存在和是否
能正常工作。
如果该设备正常,则对这个设备及其相关的、设备驱动程序
需要的软件状态进行初始化。
这部分驱动程序仅在初始化的时候被调用一
次。
2.2服务于I/O请求的子程序
服务于I/O请求的子程序,又称为驱动程序的上半部分。
调用这部分是由于系统调用的结果。
这部分程序在执行的时候,系统仍认为是和进行调用的进程属于同一个进程,只是由用户态变成了核心态,具有进行此系统调用的用户程序的运行环境,因此可以在其中调用sleep()等与进程运行环
境有关的函数。
2.3中断服务子程序
中断服务子程序,又称为驱动程序的下半部分。
在UNIX系统中,并不是直接从中断向量表中调用设备驱动程序的中断服务子程序,而是由UNIX
系统来接收硬件中断,再由系统调用中断服务子程序。
中断可以产生在任
何一个进程运行的时候,因此在中断服务程序被调用的时候,不能依赖于
任何进程的状态,也就不能调用任何与进程运行环境有关的函数。
因为设
备驱动程序一般支持同一类型的若干设备,所以一般在系统调用中断服务
子程序的时候,都带有一个或多个参数,以唯一标识请求服务的设备。
在系统内部,I/O设备的存取通过一组固定的入口点来进行,这组入口点是
由每个设备的设备驱动程序提供的。
3.1驱动程序注册与注销
向系统增加一个驱动程序意味着要赋予它一个主设备号,这可以通过在驱动程序的初始化过程中调用register_chrdev()或者register_blkdev()来完成。
而在关闭字符设备或者块设备时,则需要通过调用unregister_chrdev()或unregister_blkdev()从内核中注销设备,同时释放占用的主设备号。
3.2设备的打开与释放
打开设备是通过调用file_operations结构中的函数open()来完成的,它是驱动程序用来为今后的操作完成初始化准备工作的。
在大部分驱动程序中,open()通常需要完成下列工作:
检查设备相关错误,如设备尚未准备好等。
如果是第一次打开,则初始化硬件设备。
识别次设备号,如果有必要则更新读写操作的当前位置指针f_ops。
分配和填写要放在file->
private_data里的数据结构。
使用计数增1。
释放设备是通过调用file_operations结构中的函数release()来完成的,这个设备方法有时也被称为close(),它的作用正好与open()相反,通常要完成下列工作:
使用计数减1。
释放在file->
private_data中分配的内存。
如果使用计算为0,则关闭设备。
3.3设备的读写操作
字符设备的读写操作相对比较简单,直接使用函数read()和write()就可以了。
但如果是块设备的话,则需要调用函数block_read()和block_write()来进行数据读写,这两个函数将向设备请求表中增加读写请求,以便Linux内核可以对请求顺序进行优化。
由于是对内存缓冲区而不是直接对设备进行操作的,因此能很大程度上加快读写速度。
如果内存缓冲区中没有所要读入的数据,或者需要执行写操作将数据写入设备,那么就要执行真正的数据传输,这是通过调用数据结构blk_dev_struct中的函数request_fn()来完成的。
3.4设备的控制操作
除了读写操作外,应用程序有时还需要对设备进行控制,这可以通过设备驱动程序中的函数ioctl()来完成。
ioctl()的用法与具体设备密切关联,因此需要根据设备的实际情况进行具体分析。
3.5设备的中断和轮询处理
对于不支持中断的硬件设备,读写时需要轮流查询设备状态,以便决定是否继续进行数据传输。
如果设备支持中断,则可以按中断方式进行操作。
Linux中的I/O子系统向内核中的其他部分提供了一个统一的标准设备接口,这是通过include/linux/fs.h中的数据结构file_operations来完成的:
structfile_operations{
structmodule*owner;
loff_t(*llseek)(structfile*,loff_t,int);
ssize_t(*read)(structfile*,char*,size_t,loff_t*);
ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);
int(*readdir)(structfile*,void*,filldir_t);
unsignedint(*poll)(structfile*,structpoll_table_struct*);
int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);
int(*mmap)(structfile*,structvm_area_struct*);
int(*open)(structinode*,structfile*);
int(*flush)(structfile*);
int(*release)(structinode*,structfile*);
int(*fsync)(structfile*,structdentry*,intdatasync);
int(*fasync)(int,structfile*,int);
int(*lock)(structfile*,int,structfile_lock*);
ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);
ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);
ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);
unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);
};
当应用程序对设备文件进行诸如open、close、read、write等操作时,Linux内核将通过file_operations结构访问驱动程序提供的函数。
例如,当应用程序对设备文件执行读操作时,内核将调用file_operations结构中的read函数。
第4章Linux设备驱动程序的实现
1PCI驱动程序实现的关键数据结构
PCI设备上有三种地址空间:
PCI的I/O空间、PCI的存储空间和PCI的配置空间。
CPU可以访问PCI设备上的所有地址空间,其中I/O空间和存储空间提供给设备驱动程序使用,而配置空间则由Linux内核中的PCI初始化代码使用。
内核在启动时负责对所有PCI设备进行初始化,配置好所有的PCI设备,包括中断号以及I/O基址,并在文件/proc/pci中列出所有找到的PCI设备,以及这些设备的参数和属性。
Linux驱动程序通常使用结构(struct)来表示一种设备,而结构体中的变量则代表某一具体设备,该变量存放了与该设备相关的所有信息。
好的驱动程序都应该能驱动多个同种设备,每个设备之间用次设备号进行区分,如果采用结构数据来代表所有能由该驱动程序驱动的设备,那么就可以简单地使用数组下标来表示次设备号。
1.1pci_driver结构
这个数据结构在文件include/linux/pci.h里,这是Linux内核版本2.4之后为新型的PCI设备驱动程序所添加的,其中最主要的是用于识别设备的d_table结构,以及用于检测设备的函数probe()和卸载设备的函数remove():
structpci_driver{
structlist_headnode;
char*name;
conststructpci_device_id*id_table;
int(*probe)(structpci_dev*dev,conststructpci_device_id*id);
void(*remove)(structpci_dev*dev);
int(*save_state)(structpci_dev*dev,u32state);
int(*suspend)(structpci_dev*dev,u32state);
int(*resume)(structpci_dev*dev);
int(*enable_wake)(structpci_dev*dev,u32state,intenable);
这个数据结构也在文件include/linux/pci.h里,它详细描述了一个PCI设备几乎所有的硬件信息,包括厂商ID、设备ID、各种资源等:
structpci_dev{
structlist_headglobal_list;
structlist_headbus_list;
structpci_bus*bus;
structpci_bus*subordinate;
void*sysdata;
structproc_dir_entry*procent;
unsignedintdevfn;
unsignedshortvendor;
unsignedshortdevice;
unsignedshortsubsystem_vendor;
unsignedshortsubsystem_device;
unsignedintclass;
u8hdr_type;
u8rom_base_reg;
structpci_driver*driver;
void*driver_data;
u64dma_mask;
u32current_state;
unsignedshortvendor_compatible[DEVICE_COUNT_COMPATIBLE];
unsignedshortdevice_compatible[DEVICE_COUNT_COMPATIBLE];
unsignedintirq;
structresourceresource[DEVICE_COUNT_RESOURCE];
structresourcedma_resource[DEVICE_COUNT_DMA];
structresourceirq_resource[DEVICE_COUNT_IRQ];
charname[80];
charslot_name[8];
intactive;
intro;
unsignedshortregs;
int(*prepare)(structpci_dev*dev);
int(*activate)(structpci_dev*dev);
int(*deactivate)(structpci_dev*dev);
2写驱动程序的fuctoin问题
2.1function的encode问题
在写C程序的时候,一
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 论Linux Kernel Module之设备驱动程序 Linux Module 设备 驱动程序
![提示](https://static.bingdoc.com/images/bang_tan.gif)