LINUX设备驱动开发经验总结_第1页
LINUX设备驱动开发经验总结_第2页
LINUX设备驱动开发经验总结_第3页
LINUX设备驱动开发经验总结_第4页
LINUX设备驱动开发经验总结_第5页
已阅读5页,还剩115页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

嵌入式Linux驱动开发及内核原理内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存硬件通讯块设备驱动中断处理内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动设备驱动简介

驱动是什么Driverisasoftwarelayerthatliesbetweentheapplicationsandtheactualdevice驱动程序的角色提供机制(方法),而不是策略(怎么用)隐藏在UNIX中的哲学mechanism:Whatcapabilitiesareprovided.policy:Howthesecapabilitiescanbeused.Kernel的作用Kernel可划分为下列功能单元进程管理:进程调度,资源分配,进程间通信.内存管理:其实也算是资源分配的一部分文件系统:管理,组织物理媒介上数据的方法设备控制:设备驱动(linux驱动开发所关注的)网络:实质上是进程间通信.但它不局限于一个特定的进程.它关注收/发packets,路由,地址解析...

Kernel的结构模块可加载模块(lodablemodules)module:可实时加载到内核中的代码,它可动态连接到内核(insmod,rmmod)设备驱动就是module的代表,但module还包括文件系统等等.

设备和模块的分类模块分为这些类型,每种类型的模块驱动对应类型的设备charactermodule,blockmodule,networkinterfaceothermodule

字符设备和块设备字符设备:以字节流的形式被访问的设备。e.g:/dev/console:文本控制台./dev/ttyS0:串口它通过文件系统节点被访问.e.g:/dev/tty1,/dev/lp0字符设备与一般文件(regularfile)的区别可以在一般文件中前后移动(lseek),但只能顺序访问字符设备.当然,也有特例:framegrabbers.块设备:能支持文件系统的设备传统的UNIX:只能以block(512B)为单位访问块设备 Linux:能以访问字符设备的方式访问块设备,即以字节文单位访问块设备.Linux中字符设备与块设备的区别内核内部对数据的组织和管理不同,对驱动开发者来说透明接口不同:使用两套不同的interface网络设备网络接口:能与其他主机通信的设备它可以是硬件设备,也可以是软件设备,比如lo.(参考TCP/IP详解p26)网络接口只管收发数据包,而不管这些数据包被什么协议所使用不同于字符设备和块设备,网络接口没有对应的文件系统节点.虽然可以通过类似eth0这样的"文件名"来访问网络接口,但文件系统节点中却没有针对网络接口的节点内核与网络接口之间的通信也不同于内核与字符/块设备之间的通信(read,write),它们之间使用特定的传输数据包的函数调用其他设备也有一些module不能严格地划分类型.USBmodule:它工作在内核的USB子系统之上实际的USB设备可以是字符设备,块设备,也可以是网络接口在设备驱动之外,别的功能,不论硬件和软件,在内核中都是模块化的例如文件系统内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动建立和运行模块建立开发环境ldd3例子开发环境linux驱动开发需要预先安装内核源码源码需要从官方下载或者其他发行版的官方下载直接解压到/usr/src目录下版本影响内核官方版本注意注意发行版的内部版本最新内核版本linux2.6.20/21工作队列接口变化小版本变动不会对驱动的架构造成太大影响对于不同发行版,不同内核版本要做少量移植和测试内核模块VS应用程序执行机制不同模块初始化模块退出类似事件编程使用库不一样无法使用标准库只能调用内核提供的函数用户空间VS内核空间用户空间VS内核空间应用程序运行在用户空间设备模块运行在内核空间运行模式不一样内存地址映射也不一样用户空间和内核空间的转换可能发生在进程中的系统调用时或者硬件中断系统调用虽然在内核中执行,但是依然是在进程的上下文中进行的,所以可以访问到进程中的数据。中断处理和进程是异步的了,而且不和任何进程有关系模块跨越两个空间,有两个触发入口一些函数作为系统调用的一部分执行一些函数负责中断处理内核中的并发应用程序很多时候是按照顺序来执行的内核处于并发的执行环境当中内核当中有并发的进程中断需要响应和处理内核中的服务也在运行对称多处理器导致并行模块的加载卸载和查看加载使用insmod卸载使用rmmod查看使用lsmod模块代码staticint__initinitialization_function(void){

/*initializationcodehere*/}module_init(initialization_function);模块代码staticvoid__exitcleanup_function(void){

/*Cleanupcodehere*/}module_exit(cleanup_function);如何处理加载中的失败int__initmy_init_function(void){interr;/*registrationtakesapointerandaname*/err=register_this(ptr1,"skull");if(err)gotofail_this;err=register_that(ptr2,"skull");if(err)gotofail_that;err=register_those(ptr3,"skull");if(err)gotofail_those;return0;/*success*/fail_those:unregister_that(ptr2,"skull");fail_that:unregister_this(ptr1,"skull");fail_this:returnerr;/*propagatetheerror*/}如何编写清理函数void__exitmy_cleanup_function(void){unregister_those(ptr3,"skull");unregister_that(ptr2,"skull");unregister_this(ptr1,"skull");return;}内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动主次设备号字符设备可以通过文件系统来存取字符设备一般位于/dev下有c标志的是字符设备有b标志的是块设备设备号文档Documentation/devices.txt主设备号决定驱动的种类次设备号决定使用哪个设备设备编号的内部表达dev_t类型(在<linux/types.h>中定义)用来持有设备编号--主次部分都包括获得一个dev_t的主或者次编号,使用MAJOR(dev_tdev);MINOR(dev_tdev);转换为一个dev_t,使用:MKDEV(intmajor,intminor);分配和释放设备编号分配指定的主设备号intregister_chrdev_region(dev_tfirst,unsignedintcount,char*name);动态分配主设备号intalloc_chrdev_region(dev_t*dev,unsignedintfirstminor,unsignedintcount,char*name);释放

voidunregister_chrdev_region(dev_tfirst,unsignedintcount);字符驱动中重要的数据结构file_operationsfileinode字符设备的注册在Linux2.6下使用“structcdev”记录字符设备的信息。结构定义如下:

structcdev{

structmodule*owner;

structfile_operations*ops;

dev_tdev;

};voidcdev_init(structcdev*,structfile_operations*);structcdev*cdev_alloc(void);intcdev_add(structcdev*,dev_t,unsigned)voidcdev_del(structcdev*);

Open方法检查设备的特定错误如果设备是首次打开,则对其进行初始化如有必要,更新f_op指针分配且填写filp->private_data里的数据结构Release方法释放由open分配的,保存在filp->private中的所有内容在最后一次关闭操作时关闭设备内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动通过打印调试通过宏可以定义日志级别KERN_EMERG用于紧急消息,常常是那些崩溃前的消息.KERN_ALERT需要立刻动作的情形.KERN_CRIT严重情况,常常与严重的硬件或者软件失效有关.KERN_ERR用来报告错误情况;设备驱动常常使用KERN_ERR来报告硬件故障.KERN_WARNING有问题的情况的警告,这些情况自己不会引起系统的严重问题.KERN_NOTICE正常情况,但是仍然值得注意.在这个级别一些安全相关的情况会报告.KERN_INFO信息型消息.在这个级别,很多驱动在启动时打印它们发现的硬件的信息.KERN_DEBUG用作调试消息.printk语句缺省是DEFAULT_MESSAGE_LOGLEVEL(KERN_WARNING)通过打印调试如果开启Klogd及Syslogd则输出到日志日志文件参考/var/log/message在printk当中打印设备编号Print_dev_tFormat_dev_t内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动并发和管理并发源很多多个进程运行SMP多个CPU并行设备中断延迟机制(工作队列,定时器,Tasklet)并发和竞争两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序,就称为竞争条件(RaceConditions)。竞争情况来自对资源的共享存取的结果.存取管理的常用技术是加锁或者互斥其次常用的技术是引用计数临界区把对共享内存进行访问的程序片段称作临界区(criticalregion),或临界段(criticalsection)。如果我们能够适当地安排使得两个进程不可能同时处于临界区,则就能够避免竞争条件。临界区四要素任何两个进程不能同时处于临界区临界区外的进程不能阻塞其他进程不能使进程在临界区外无限等待不应对CPU的速度和数目做假设PV操作解决同步互斥PV原语的含义

P操作和V操作是不可中断的程序段,称为原语。PV原语及信号量的概念都是由荷兰科学家E.W.Dijkstra提出的。信号量sem是一整数,sem大于等于零时代表可供并发进程使用的资源实体数,但sem小于零时则表示正在等待使用临界区的进程数。

P原语操作的动作是:

(1)sem减1;

(2)若sem减1后仍大于或等于零,则进程继续执行;

(3)若sem减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。

V原语操作的动作是:

(1)sem加1;

(2)若相加结果大于零,则进程继续执行;

(3)若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。

PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用。在PV原语执行期间不允许有中断的发生。

解决互斥用PV原语实现进程的互斥由于用于互斥的信号量sem与所有的并发进程有关,所以称之为公有信号量。公有信号量的值反映了公有资源的数量。只要把临界区置于P(sem)和V(sem)之间,即可实现进程间的互斥。就象火车中的每节车厢只有一个卫生间,该车厢的所有旅客共享这个公有资源:卫生间,所以旅客间必须互斥进入卫生间,只要把卫生间放在P(sem)和V(sem)之间,就可以到达互斥的效果。

解决同步用PV原语实现进程的同步与进程互斥不同,进程同步时的信号量只与制约进程及被制约进程有关而不是与整组并发进程有关,所以称该信号量为私有信号量。利用PV原语实现进程同步的方法是:首先判断进程间的关系为同步的,且为各并发进程设置私有信号量,然后为私有信号量赋初值,最后利用PV原语和私有信号量规定各进程的执行顺序。Linux信号量实现voidsema_init(structsemaphore*sem,intval);DECLARE_MUTEX(name);DECLARE_MUTEX_LOCKED(name);voidinit_MUTEX(structsemaphore*sem);voidinit_MUTEX_LOCKED(structsemaphore*sem);voiddown(structsemaphore*sem);intdown_interruptible(structsemaphore*sem);intdown_trylock(structsemaphore*sem);voidup(structsemaphore*sem);内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动ioctl接口大部分驱动需要通过设备驱动进行各种硬件控制的能力.大部分设备可进行超出简单的数据传输之外的操作;例如,设备锁上它的门,弹出它的介质,报告错误信息,改变波特率,或者自我销毁.这些操作常常通过ioctl方法来支持,它通过相同名字的系统调用来实现.阻塞I/O数据操作可能会遇到read的调用时可能没有数据时Write的调用时设备没有准备好接受数据当驱动不能立刻满足要求怎么办程序员希望调用read或write并且使调用返回驱动应当(缺省地)阻塞进程,使它进入睡眠直到请求可继续.进程的休眠进程被置为睡眠,从调度器的运行队列移除睡眠的进程被搁置一边,等待以后发生事件睡眠注意安全编程在原子上下文时不能睡眠休眠醒来,无法确定休眠时间和时序休眠的进程必须有时机被唤醒与休眠相关的数据结构和函数等待队列等待-唤醒函数wait_event(queue,condition)wait_event_interruptible(queue,condition)wait_event_timeout(queue,condition,timeout)wait_event_interruptible_timeout(queue,condition,timeout)voidwake_up(wait_queue_head_t*queue);voidwake_up_interruptible(wait_queue_head_t*queue);阻塞操作的推荐用法阻塞操作标准语法:如果一个进程调用read但是没有数据可用(尚未),这个进程必须阻塞.这个进程在有数据达到时被立刻唤醒,并且那个数据被返回给调用者,即便小于在给方法的count参数中请求的数量.如果一个进程调用write并且在缓冲中没有空间,这个进程必须阻塞,并且它必须在一个与用作read的不同的等待队列中.当一些数据被写入硬件设备,并且在输出缓冲中的空间变空闲,这个进程被唤醒并且写调用成功,尽管数据可能只被部分写入,这时缓冲内没有足够空间给被请求的count字节.非阻塞I/O,poll和select可以实现非阻塞读写多个文件三者的区别和联系select在BSDUnix中引入poll是SystemV的解决方案epoll扩展到几千个文件描述符,提高了性能内部实现

unsignedint(*poll)(structfile*filp,poll_table*wait);内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动测量时间流失内核通过定时器中断来跟踪时间的流动定时器中断由系统定时硬件以规律地间隔产生每次发生一个时钟中断,一个内核计数器的值递增.这个计数器在系统启动时初始化为0,因此它代表从最后一次启动以来的时钟嘀哒的数目这个计数器是一个64-位变量(即便在32-位的体系上)并且称为jiffies_64获知当前时间voiddo_gettimeofday(structtimeval*tv);延后执行长延时技术忙等待让出处理器超时短延时技术voidndelay(unsignedlongnsecs);voidudelay(unsignedlongusecs);voidmdelay(unsignedlongmsecs);

内核定时器structtimer_list{/*...*/unsignedlongexpires;void(*function)(unsignedlong);unsignedlongdata;};voidinit_timer(structtimer_list*timer);structtimer_listTIMER_INITIALIZER(_function,_expires,_data);voidadd_timer(structtimer_list*timer);intdel_timer(structtimer_list*timer);Tasklets机制structtasklet_struct{/*...*/void(*func)(unsignedlong);unsignedlongdata;};voidtasklet_init(structtasklet_struct*t,void(*func)(unsignedlong),unsignedlongdata);DECLARE_TASKLET(name,func,data);DECLARE_TASKLET_DISABLED(name,func,data);Tasklet特性一个tasklet能够被禁止并且之后被重新使能;它不会执行直到它被使能与被禁止相同的的次数.如同定时器,一个tasklet可以注册它自己.一个tasklet能被调度来执行以正常的优先级或者高优先级.后一组一直是首先执行.taslet可能立刻运行,如果系统不在重载下,但是从不会晚于下一个时钟嘀哒.一个tasklet可能和其他tasklet并发,但是对它自己是严格地串行的--同样的tasklet从不同时运行在超过一个处理器上.同样,如已经提到的,一个tasklet常常在调度它的同一个CPU上运行.工作队列工作队列表面类似于tasketstasklet在软件中断上下文中运行的结果是所有的tasklet代码必须是原子的.相反,工作队列函数在一个特殊内核进程上下文运行;结果,它们有更多的灵活性.特别地,工作队列函数能够睡眠.tasklet常常在它们最初被提交的处理器上运行.工作队列以相同地方式工作内核代码可以请求工作队列函数被延后一个明确的时间间隔.工作队列structworkqueue_struct*create_workqueue(constchar*name);structworkqueue_struct*create_singlethread_workqueue(constchar*name);

内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作内存管理与硬件通讯中断处理块设备驱动内存分配内存分配的最常用接口.#include<linux/slab.h>void*kmalloc(size_tsize,intflags);voidkfree(void*obj);内存管理功能程序在寻址过程中使用的是虚拟地址,该地址由段和偏移值该地址并不能直接用。为了能寻址物理内存,就需要一种地址变换机制将虚拟地址映射或变换到物理内存中,这种地址变换机制就是内存管理的主要功能之一(内存管理的另外一个主要功能是内存的寻址保护机制。由于时间有限限,本次不对其进行讨论)。内存管理机制1.无内存管理,直接寻址,也称为实地址模式(内存不受保护)2.段式管理,也称为保护模式(可分段记录内存的使用情况)3.分页式管理,目前操作系统使用的模式(可提高管理效率)分页式管理(一)内存分页管理是通过页目录表和内存页表所组成的二级表进行的。其中页目录表和页表的结构是一样的,表项结构也相同。页目录表中的每个表项(简称页目录项)(4字节)用来寻址一个页表,而每个页表项(4字节)用来指定一页物理内存页。因此,当指定了一个页目录项和一个页表项,我们就可以唯一地确定所对应的物理内存页。页目录表占用一页内存,因此最分页式管理(二)页目录表有1024个表项,每个表项占用4个字节,所以一个页目录表刚好占用一页内存(4X1024),因此最多可以寻址1024个页表。而每个页表也同样占用一页内存,因此一个页表可以寻址最多1024个物理内存页面。这样一个页目录表所寻址的所有页表共可以寻址1024X1024X4096=4G的内存空间页目录表和页表结构线性地址内存分配所获取的都是线性地址;一个32位的线性地址被分成了三个部分第一部分(10位):指定一个页目录项第二部分(10位):指定一个页表项第三部分(12位):指定物理内存页上的偏移地址线性地址结构(一)Typededstruct{ unsignedintdir:10/*用作页面表目录中的下标,该目录项指向一个页面表*/ unsignedintpage:10/*用作页面表中的下标,该表项指向一个物理页面*/unsignedintoffset:12/*用作4k字节物理页面内的偏移量*/}线性地址线性地址结构(二)在页面目录中共有210

=1024个目录项,每个目录项指向一个页面表,每个页面表共有1024个页面描述项。线性地址的位31-22共10个比特用来确定页目录中的目录项,位21-12用来寻址页目录项指定的页表中的页表项,最后的12个比特正好用作页表项指定的一页物理内存中的偏移地址线性地址到物理地址映射(一)有专门的寄存器CR3存储当前页面目录的地址1.从寄存器CR3中取得页面目录的基地址2.以线性地址中的dir位段为下标,在目录中取得相应也表的基地址3.以线性地址中的page位段为下标,在所得到的页面表中取得相应的页面描述项4.将页面描述项中给出的页面基地址与线性地址中的offset位段相加得到实际的物理地址线性地址到物理地址映射(二)线性地址到物理地址映射(三)线性地址到物理地址映射(四)对于第一个进程(任务0),其页表是在页目录表之后,共4页。对于应用程序的进程,其页表所使用的内存是在进程创建时向内存管理程序申请的,因此是在主内存区中表项结构(一)页框地址(PAGEFRAMEADDRESS)指定了一页内存的物理起始地址。因为内存页是位于4K边界上的,这些指针的低12位都永远是0。这样,在目录项和页表项中都只要有20位用于指针就够了,而余下的12位则可以用于控制或其他的目的用。表项结构(二)每个表项由页框地址、访问标志位、脏(已改写)标志位和存在标志位等构成表项结构(三)表项结构(四)页框地址(PAGEFRAMEADDRESS)指定了一页内存的物理起始地址存在位(PRESENT–P)确定了一个页表项是否可以用于地址转换。P=1可用,P=0不能用于地址转换,相应的页面或页面表不在内存.CPU会产生缺页中断,内核中表项结构(五)P=0表示相应的页面或页面表不在内存.CPU会产生缺页中断,内核中的有关异常服务程序就可根据从磁盘上的页面交换区将相应的页面读入内存,并且相应的设置表现中的基地址,并将P位设置为1,供后续访问使用。相反也可将内存中暂时不使用的内存页面写入到磁盘的交换区,然后将相应的页面表项的P位设置为0。这就实现虚拟内存功能表项结构(六)当P=0时,表项中的其余各位均无意义,所以可以被用来临时存储其他信息,如被换出的页面在磁盘上的位置等等信息PS=0时表示包含在由该目录项所指的页面面表中所有页面的大小都是4K字节,这也是目前在Linux内核中所采用的页面大小。但从Pentium处理器开始,intel引入了PSE页面大小扩充机制。即当PS=1,页面的大小就成了4M字节,而页面表就不足使用。表项结构(七)Ps=1时线性地址中的低22位就全部用在4M字节的页面中的位移,这样总的寻址能力不变1024X4M=4G,但映射过程减少了一层。提高访问速度(主要是由于内存容量和磁盘的容量的日益增加,磁盘访问速度的显著提高,以及对图像处理要求的日益增加,4M页面可能成为主流)表项结构(八)已访问(Accessed–A)和已修改(Dirty–D)比特位用于提供有关页使用的信息。除了页目录项中的已修改位,这些比特位将由硬件置位,但不复位。在对一页内存进行读或写操作之前,CPU将设置相关的目录和二级页表项的已访问位。在向一个二级页表项所涵盖的地址进行写操作之前,处理器将设置该二级页表项的已修改位,而页目录项中的已修改位是不用的。当所需求的内存超出实际物理内存量时,内存管理程序就可以使用这些位来确定那些页可以从内存中取走,以腾出空间。内存管理程序还需负责检测和复位这些比特位表项结构(九)读/写位(Read/Write–R/W)和用户/超级用户位(User/Supervisor–U/S)并不用于地址转换,但用于分页级的保护机制,是由CPU在地址转换过程中同时操作的。Linux中物理内存的管理和分配物理地址0处存放了一个页目录表,紧随其后是4个页表。这4个页表将被用于任务0,其它的派生进程将在主内存区申请内存页来存放自己的页表控制内存分配如何进行的标志,从最少限制的到最多的.GFP_USER和GFP_KERNEL优先级允许当前进程被置为睡眠来满足请求.GFP_NOFS和GFP_NOIO禁止文件系统操作和所有的I/O操作,分别地,而GFP_ATOMIC分配根本不能睡眠.#include<linux/mm.h>GFP_USERGFP_KERNELGFP_NOFSGFP_NOIOGFP_ATOMIC内存分配标志这些标志分配内存时修改内核的行为__GFP_DMA__GFP_HIGHMEM__GFP_COLD__GFP_NOWARN__GFP_HIGH__GFP_REPEAT__GFP_NOFAIL__GFP_NORETRYslab缓存创建和销毁一个slab缓存.这个缓存可被用来分配几个相同大小的对象.#include<linux/malloc.h>kmem_cache_t*kmem_cache_create(char*name,size_tsize,size_toffset,unsignedlongflags,constructor(),destructor());intkmem_cache_destroy(kmem_cache_t*cache);缓存标志在创建一个缓存时可指定的标志.SLAB_CTOR_ATOMICSLAB_CTOR_CONSTRUCTOR缓存中分配释放单个对象从缓存中分配和释放一个单个对象./proc/slabinfo一个包含对slab缓存使用情况统计的虚拟文件.void*kmem_cache_alloc(kmem_cache_t*cache,intflags);voidkmem_cache_free(kmem_cache_t*cache,constvoid*obj);内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动硬件读写屏障硬件内存屏障.它们请求CPU(和编译器)来检查所有的跨这个指令的内存读,写#include<asm/system.h>voidrmb(void);voidread_barrier_depends(void);voidwmb(void);voidmb(void);I/O读写用来读和写I/O端口的函数.它们还可以被用户空间程序调用,如果它们有正当的权限来存取端口.#include<asm/io.h>unsignedinb(unsignedport);voidoutb(unsignedcharbyte,unsignedport);unsignedinw(unsignedport);voidoutw(unsignedshortword,unsignedport);unsignedinl(unsignedport);voidoutl(unsigneddoubleword,unsignedport);延时读写函数如果在一次I/O操作后需要一个小延时,你可以使用在前一项中介绍的这些函数的6个暂停对应部分;这些暂停函数以_p结尾unsignedinb_p(unsignedport);字串函数这些"字串函数"被优化为传送数据从一个输入端口到一个内存区,或者其他的方式.这些传送通过读或写到同一端口count次来完成.voidinsb(unsignedport,void*addr,unsignedlongcount);voidoutsb(unsignedport,void*addr,unsignedlongcount);voidinsw(unsignedport,void*addr,unsignedlongcount);voidoutsw(unsignedport,void*addr,unsignedlongcount);voidinsl(unsignedport,void*addr,unsignedlongcount);voidoutsl(unsignedport,void*addr,unsignedlongcount);I/O端口资源分配I/O端口的资源分配器.这个检查函数成功返回0并且在错误时小于0#include<linux/ioport.h>structresource*request_region(unsignedlongstart,unsignedlonglen,char*name);voidrelease_region(unsignedlongstart,unsignedlonglen);intcheck_region(unsignedlongstart,unsignedlonglen);I/O地址映射ioremap重映射一个物理地址范围到处理器的虚拟地址空间,使它对内核可用.iounmap释放映射当不再需要它时.#include<asm/io.h>void*ioremap(unsignedlongphys_addr,unsignedlongsize);void*ioremap_nocache(unsignedlongphys_addr,unsignedlongsize);voidiounmap(void*virt_addr);内存区处理资源分配为内存区处理资源分配的函数structresource*request_mem_region(unsignedlongstart,unsignedlonglen,char*name);voidrelease_mem_region(unsignedlongstart,unsignedlonglen);intcheck_mem_region(unsignedlongstart,unsignedlonglen);I/O内存存取函数用来使用I/O内存的存取者函数.#include<asm/io.h>unsignedintioread8(void*addr);unsignedintioread16(void*addr);unsignedintioread32(void*addr);voidiowrite8(u8value,void*addr);voidiowrite16(u16value,void*addr);voidiowrite32(u32value,void*addr);

I/O内存函数.旧的,类型不安全的存取I/O内存的函数.unsignedreadb(address);unsignedreadw(address);unsignedreadl(address);voidwriteb(unsignedvalue,address);voidwritew(unsignedvalue,address);voidwritel(unsignedvalue,address);memset_io(address,value,count);memcpy_fromio(dest,source,nbytes);memcpy_toio(dest,source,nbytes);内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动注册注销中断处理调用这个注册和注销一个中断处理.#include<linux/interrupt.h>intrequest_irq(unsignedintirq,请求中断号irqreturn_t(*handler)(),中断服务程序unsignedlongflags,是否共享constchar*dev_name,设备名称/proc/interrupts来显示中断的拥有者

void*dev_id);用户数据voidfree_irq(unsignedintirq,void*dev_id);中断介绍

硬中断是外部设备对CPU的中断,软中断通常是硬中断服务程序对内核的中断,信号则是由内核(或其他进程)对某个进程的中断。软中断的一种典型应用就是所谓的“下半部”(bottomhalf),它的得名来自于将硬件中断处理分离成“上半部”和“下半部”两个阶段的机制:上半部在屏蔽中断的上下文中运行,用于完成关键性的处理动作;而下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行软中断,用软件方式进行模拟,实现宏观上的异步执行效果。很多情况下,软中断和"信号"有些类似

注册注销中断处理调用这个注册和注销一个中断处理.#include<linux/interrupt.h>intrequest_irq(unsignedintirq,请求中断号irqreturn_t(*handler)(),中断服务程序unsignedlongflags,是否共享constchar*dev_name,设备名称/proc/interrupts来显示中断的拥有者

void*dev_id);用户数据voidfree_irq(unsignedintirq,void*dev_id);中断申请标志给request_irq的标志.SA_INTERRUPT当置位了,这表示一个"快速"中断处理.快速处理在当前处理器上禁止中断来执行

SA_SHIRQ安装一个共享的处理者#include<asm/signal.h>SA_INTERRUPTSA_SHIRQSA_SAMPLE_RANDOM中断的文件系统节点报告硬件中断和安装的处理者的文件系统节点.可以获取当前系统得中断号和每个中断产生的中断次数等信息/proc/interrupts/proc/stat驱动使用探测函数驱动使用的函数,探测决定哪个中断线被设备在使用.probe_irq_on这个函数返回一个未安排的中断的位掩码.驱动必须保留返回的位掩码,并且在后面传递给probe_irq_off.在这个调用之后,驱动应当安排它的设备产生至少一次中断供探测使用probe_irq_off的返回值是被探测的中断号.中断处理汇编语言文件处理很多机器级别的工作.,汇编代码被安排到每个可能的中断.在每个情况下,这个代码将中断号压栈并且跳转到一个通用段,称为do_IRQ,在irq.c中定义.do_IRQ做的第一件事是确认中断以便中断控制器能够继续其他事情.它接着获取给定IRQ号的一个自旋锁,因此阻止任何其他CPU处理这个IRQ.它清除几个状态位(包括称为IRQ_WAITING的一个,我们很快会看到它)并且接着查看这个特殊IRQ的处理者.如果没有处理者,什么不作;自旋锁释放,任何挂起的软件中断被处理,最后do_IRQ返回.使能和禁止中断可以使能和禁止中断。共享处理不使用这个函数.voiddisable_irq(intirq);voiddisable_irq_nosync(intirq);voidenable_irq(intirq);禁止中断使用local_irq_save来禁止本地处理器的中断并且记住它们之前的状态voidlocal_irq_save(unsignedlongflags);voidlocal_irq_restore(unsignedlongflags);使能和禁止中断在当前处理器无条件禁止和使能中断的函数.voidlocal_irq_disable(void);voidlocal_irq_enable(void);内容提要设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动块设备注册register_blkdev注册一个块驱动到内核,并且,可选地,获得一个主编号.一个驱动可被注销,使用unregister_blkdev.#include<linux/fs.h>intregister_blkdev(unsignedintmajor,constchar*name);intunregister_blkdev(unsignedintmajor,constchar*name);块设备相关数据结构块设备驱动的数据结构.structblock_device_operations描述内核中单个块设备的结构.#include<linux/genhd.h>structgendisk;分配gendisk结构的函数,并且返回它们到系统.structgendisk*alloc_disk(intminors);voidadd_disk(structgendisk*gd); 块设备相关函数voidset_capacity(structgendisk*gd,sector_tsectors);存储设备能力(以512-字节)在gendisk结构中.voidadd_disk(structgendisk*gd);添加一个磁盘到内核.一旦调用这个函数,你的磁盘的方法可被内核调用.intcheck_disk_change(structblock_device*bdev);一个内核函数,检查在给定磁盘驱动器中的介质改变,并且采取要求的清理动作当检测到这样一个改变.请求队列相关函数#include<linux/blkdev.h>request_queue_tblk_init_queue(request_fn_proc*request,spinlock_t*lock);voidblk_cleanup_queue(request_queue_t*);处理块请求队列的创建和删除的函数.structrequest*elv_next_request(request_queue_t*queue);voidend_request(structrequest*req,intsuccess);elv_next_request从一个请求队列中获得下一个请求;end_request可用在每个简单驱动器中来标识一个(或部分)请求完成.voidblkdev_dequeue_request(structrequest*req);voidelv_requeue_request(request_queue_t*queue,structrequest*req);从队列中除去一个请求,并且放回它的函数如果需要.voidblk_stop_queue(request_queue_t*queue);voidblk_start_queue(request_queue_t*queue);如果你需要阻止对你的请求函数的进一步调用,调用blk_stop_queue来完成.调用blk_start_queue来使你的请求方法被再次调用.请求队列参数控制函数

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论