《LINUX设备驱动开发及内核原理实用教案》由会员分享,可在线阅读,更多相关《LINUX设备驱动开发及内核原理实用教案(96页珍藏版)》请在金锄头文库上搜索。
1、日程安排设备驱动(qdn)简介建立和运行模块字符驱动(qdn)调试技术并发和竞争高级字符驱动(qdn)操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动(qdn)第1页/共95页第一页,共96页。日程安排设备(shbi)驱动简介第2页/共95页第二页,共96页。设备(shbi)驱动简介驱动是什么Driverisasoftwarelayerthatliesbetweentheapplicationsandtheactualdevice驱动程序的角色提供机制,而不是(bshi)策略隐藏在UNIX中的哲学mechanism:Whatcapabilitiesareprovided.polic
2、y:Howthesecapabilitiescanbeused.第3页/共95页第三页,共96页。Kernel的作用(zuyng)Kernel可划分为下列功能单元进程管理:进程调度,资源分配,进程间通信.内存管理:其实也算是资源分配的一部分文件系统:管理,组织物理媒介上数据的方法设备控制:设备驱动(qdn)(ldd3所关注的)网络:实质上是进程间通信.但它不局限于一个特定的进程.它关注收/发packets,路由,地址解析.第4页/共95页第四页,共96页。Kernel的结构(jigu)第5页/共95页第五页,共96页。模块(mkui)可加载模块(lodablemodules)module:可实
3、时加载到内核中的代码,它可动态连接到内核(insmod,rmmod)设备驱动就是module的代表(dibio),但module还包括文件系统等等.第6页/共95页第六页,共96页。设备(shbi)和模块的分类模块(mkui)分为这些类型,每种类型的模块(mkui)驱动对应类型的设备charactermodule,blockmodule,networkinterfaceothermodule第7页/共95页第七页,共96页。字符(zf)设备和块设备字符设备:以字节流的形式被访问的设备。e.g:/dev/console:文本控制台./dev/ttyS0:串口它通过文件系统节点被访问.e.g:/d
4、ev/tty1,/dev/lp0字符设备与一般文件(regularfile)的区别可以在一般文件中前后移动(lseek),但只能顺序访问字符设备.当然,也有特例:framegrabbers.块设备:能支持文件系统的设备传统的UNIX:只能以block(512B)为单位访问块设备Linux:能以访问字符设备的方式访问块设备,即以字节文单位访问块设备.Linux中字符设备与块设备的区别内核内部对数据的组织和管理不同,对驱动开发者来说透明(tumng)接口不同:使用两套不同的interface第8页/共95页第八页,共96页。网络设备网络接口:能与其他主机通信的设备(shbi)它可以是硬件设备(sh
5、bi),也可以是软件设备(shbi),比如lo.(参考TCP/IP详解p26)网络接口只管收发数据包,而不管这些数据包被什么协议所使用不同于字符设备(shbi)和块设备(shbi),网络接口没有对应的文件系统节点.虽然可以通过类似eth0这样的文件名来访问网络接口,但文件系统节点中却没有针对网络接口的节点内核与网络接口之间的通信也不同于内核与字符/块设备(shbi)之间的通信(read,write),它们之间使用特定的传输数据包的函数调用第9页/共95页第九页,共96页。其他(qt)设备也有一些module不能严格地划分类型.USBmodule:它工作在内核的USB子系统之上实际的USB设备可
6、以是字符设备,块设备,也可以是网络接口在设备驱动(qdn)之外,别的功能,不论硬件和软件,在内核中都是模块化的例如文件系统第10页/共95页第十页,共96页。日程安排设备驱动(qdn)简介建立和运行模块第11页/共95页第十一页,共96页。建立(jinl)和运行模块建立开发环境ldd3例子开发环境驱动开发需要预先安装内核源码源码需要从官方下载或者其他发行版的官方下载直接解压到/usr/src目录下版本影响内核官方版本注意注意发行版的内部版本最新内核版本工作队列接口(jiku)变化小版本变动不会对驱动的架构造成太大影响对于不同发行版,不同内核版本要做少量移植和测试第12页/共95页第十二页,共9
7、6页。内核模块VS应用程序执行机制不同模块(mkui)初始化模块(mkui)退出类似事件编程使用库不一样无法使用标准库只能调用内核提供的函数第13页/共95页第十三页,共96页。用户空间(kngjin)VS内核空间(kngjin)用户空间VS内核空间应用程序运行在用户空间设备模块运行在内核空间运行模式不一样内存地址映射也不一样用户空间和内核空间的转换可能发生在进程中的系统调用时或者硬件中断系统调用虽然在内核中执行,但是依然是在进程的上下文中进行(jnxng)的,所以可以访问到进程中的数据。中断处理和进程是异步的了,而且不和任何进程有关系模块跨越两个空间,有两个触发入口一些函数作为系统调用的一部
8、分执行一些函数负责中断处理第14页/共95页第十四页,共96页。内核(nih)中的并发应用程序很多时候是按照顺序来执行的内核处于并发的执行环境当中内核当中有并发的进程中断需要响应和处理内核中的服务(fw)也在运行对称多处理器导致并行第15页/共95页第十五页,共96页。模块(mkui)的加载卸载和查看加载使用(shyng)insmod卸载使用(shyng)rmmod查看使用(shyng)lsmod第16页/共95页第十六页,共96页。模块(mkui)代码staticint_initinitialization_function(void)/*initializationcodehere*/mo
9、dule_init(initialization_function);第17页/共95页第十七页,共96页。模块(mkui)代码staticvoid_exitcleanup_function(void)/*Cleanupcodehere*/module_exit(cleanup_function);第18页/共95页第十八页,共96页。如何(rh)处理加载中的失败int _init my_init_function(void) int err; /* registration takes a pointer and a name */ err = register_this(ptr1, sku
10、ll); if (err) goto fail_this; err = register_that(ptr2, skull); if (err) goto fail_that; err = register_those(ptr3, skull); if (err) goto fail_those; return 0; /* success */fail_those: unregister_that(ptr2, skull);fail_that: unregister_this(ptr1, skull);fail_this: return err; /* propagate the error
11、*/第19页/共95页第十九页,共96页。如何编写(binxi)清理函数void _exit my_cleanup_function(void) unregister_those(ptr3, skull); unregister_that(ptr2, skull); unregister_this(ptr1, skull); return;第20页/共95页第二十页,共96页。日程安排设备驱动简介建立和运行模块(mkui)字符驱动第21页/共95页第二十一页,共96页。主次(zhc)设备号字符设备可以通过文件系统来存取字符设备一般位于(wiy)/dev下有c标志的是字符设备有b标志的是块设备设
12、备号文档Documentation/devices.txt主设备号决定驱动的种类次设备号决定使用哪个设备第22页/共95页第二十二页,共96页。设备编号的内部(nib)表达dev_t类型(在中定义)用来持有(chyu)设备编号-主次部分都包括获得一个dev_t的主或者次编号,使用MAJOR(dev_tdev);MINOR(dev_tdev);转换为一个dev_t,使用:MKDEV(intmajor,intminor);第23页/共95页第二十三页,共96页。分配和释放(shfng)设备编号分配(fnpi)指定的主设备号intregister_chrdev_region(dev_tfirst,u
13、nsignedintcount,char*name);动态分配(fnpi)主设备号intalloc_chrdev_region(dev_t*dev,unsignedintfirstminor,unsignedintcount,char*name);释放voidunregister_chrdev_region(dev_tfirst,unsignedintcount);第24页/共95页第二十四页,共96页。字符驱动(qdn)中重要的数据结构file_operationsfileinode第25页/共95页第二十五页,共96页。字符设备(shbi)的注册在下使用“structcdev”记录字符设备
14、的信息(xnx)。结构定义如下:structcdevstructmodule*owner;structfile_operations*ops;dev_tdev;voidcdev_init(structcdev*,structfile_operations*);structcdev*cdev_alloc(void);intcdev_add(structcdev*,dev_t,unsigned)voidcdev_del(structcdev*);第26页/共95页第二十六页,共96页。Open方法(fngf)检查设备的特定错误如果(rgu)设备是首次打开,则对其进行初始化如有必要,更新f_op指针
15、分配且填写filp-private_data里的数据结构第27页/共95页第二十七页,共96页。Release方法(fngf)释放由open分配的,保存在filp-private中的所有内容在最后一次关闭(gunb)操作时关闭(gunb)设备第28页/共95页第二十八页,共96页。日程安排设备驱动简介建立和运行(ynxng)模块字符驱动调试技术第29页/共95页第二十九页,共96页。通过(tnggu)打印调试通过宏可以定义日志级别参考P79如果开启Klogd及Syslogd则输出到日志日志文件参考/var/log/message在printk当中(dngzhng)打印设备编号Print_dev
16、_tFormat_dev_t第30页/共95页第三十页,共96页。日程安排设备驱动简介建立和运行模块字符驱动调试技术并发(bngf)和竞争第31页/共95页第三十一页,共96页。并发(bngf)和管理并发源很多多个进程运行(ynxng)SMP多个CPU并行设备中断延迟机制(工作队列,定时器,Tasklet)第32页/共95页第三十二页,共96页。并发(bngf)和竞争两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序,就称为竞争条件(tiojin)(RaceConditions)。竞争情况来自对资源的共享存取的结果.存取管理的常用技术是加锁或者互斥其次常用的技术是引用计数第3
17、3页/共95页第三十三页,共96页。临界(lnji)区把对共享内存进行访问的程序片段称作临界区(criticalregion),或临界段(criticalsection)。如果我们能够适当地安排使得两个进程不可能同时处于临界区,则就能够避免竞争条件。临界区四要素(yos)任何两个进程不能同时处于临界区临界区外的进程不能阻塞其他进程不能使进程在临界区外无限等待不应对CPU的速度和数目做假设第34页/共95页第三十四页,共96页。PV操作解决(jiju)同步互斥PV原语的含义P操作和V操作是不可中断的程序段,称为原语。PV原语及信号量的概念都是由荷兰科学家提出的。信号量sem是一整数,sem大于等
18、于零时代表可供并发进程使用的资源实体数,但sem小于零时则表示正在等待使用临界区的进程数。P原语操作的动作(dngzu)是:(1)sem减1;(2)若sem减1后仍大于或等于零,则进程继续执行;(3)若sem减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。V原语操作的动作(dngzu)是:(1)sem加1;(2)若相加结果大于零,则进程继续执行;(3)若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用。在PV原语执行期间不允许有中断的发生。第35页/共95页第
19、三十五页,共96页。解决(jiju)互斥用PV原语实现进程的互斥由于用于互斥的信号量sem与所有的并发进程有关,所以称之为公有信号量。公有信号量的值反映了公有资源的数量。只要把临界区置于P(sem)和V(sem)之间,即可实现进程间的互斥。就象火车中的每节车厢只有(zhyu)一个卫生间,该车厢的所有旅客共享这个公有资源:卫生间,所以旅客间必须互斥进入卫生间,只要把卫生间放在P(sem)和V(sem)之间,就可以到达互斥的效果。第36页/共95页第三十六页,共96页。解决(jiju)同步用PV原语实现进程的同步与进程互斥不同,进程同步时的信号量只与制约进程及被制约进程有关而不是与整组并发进程有关
20、,所以称该信号量为私有信号量。利用PV原语实现进程同步的方法是:首先判断进程间的关系为同步的,且为各并发进程设置(shzh)私有信号量,然后为私有信号量赋初值,最后利用PV原语和私有信号量规定各进程的执行顺序。第37页/共95页第三十七页,共96页。Linux信号量实现(shxin)voidsema_init(structsemaphore*sem,intval);DECLARE_MUTEX(name);DECLARE_MUTEX_LOCKED(name);voidinit_MUTEX(structsemaphore*sem);voidinit_MUTEX_LOCKED(structsemap
21、hore*sem);voiddown(structsemaphore*sem);intdown_interruptible(structsemaphore*sem);intdown_trylock(structsemaphore*sem);voidup(structsemaphore*sem);第38页/共95页第三十八页,共96页。日程安排设备驱动简介建立和运行模块(mkui)字符驱动调试技术并发和竞争高级字符驱动操作第39页/共95页第三十九页,共96页。ioctl接口(jiku)大部分驱动需要通过设备驱动进行各种硬件控制的能力.大部分设备可进行超出简单的数据传输之外的操作;例如,设备锁上
22、(sushn)它的门,弹出它的介质,报告错误信息,改变波特率,或者自我销毁.这些操作常常通过ioctl方法来支持,它通过相同名字的系统调用来实现.第40页/共95页第四十页,共96页。阻塞(zs)I/O数据操作可能会遇到read的调用时可能没有数据时Write的调用时设备没有准备好接受数据当驱动不能立刻满足要求怎么办程序员希望调用read或write并且(bngqi)使调用返回驱动应当(缺省地)阻塞进程,使它进入睡眠直到请求可继续.第41页/共95页第四十一页,共96页。进程(jnchng)的休眠进程( jnchng)被置为睡眠,从调度器的运行队列移除睡眠的进程( jnchng)被搁置一边,等
23、待以后发生事件睡眠注意安全编程在原子上下文时不能睡眠休眠醒来,无法确定休眠时间和时序休眠的进程( jnchng)必须有时机被唤醒第42页/共95页第四十二页,共96页。与休眠相关的数据结构(shjjiu)和函数等待队列等待-唤醒(hunxng)函数wait_event(queue,condition)wait_event_interruptible(queue,condition)wait_event_timeout(queue,condition,timeout)wait_event_interruptible_timeout(queue,condition,timeout)voidwake
24、_up(wait_queue_head_t*queue);voidwake_up_interruptible(wait_queue_head_t*queue);第43页/共95页第四十三页,共96页。阻塞操作(cozu)的推荐用法阻塞操作标准语法:如果一个进程调用read但是没有数据可用(尚未),这个进程必须阻塞.这个进程在有数据达到时被立刻唤醒,并且那个数据被返回给调用者,即便小于在给方法的count参数中请求的数量.如果一个进程调用write并且在缓冲中没有空间,这个进程必须阻塞,并且它必须在一个与用作read的不同的等待队列中.当一些数据被写入硬件设备,并且在输出(shch)缓冲中的空间
25、变空闲,这个进程被唤醒并且写调用成功,尽管数据可能只被部分写入,这时缓冲内没有足够空间给被请求的count字节.第44页/共95页第四十四页,共96页。非阻塞(zs)I/O,poll和select可以实现非阻塞读写多个文件三者的区别和联系select在BSDUnix中引入poll是SystemV的解决方案epoll扩展到几千个文件描述符,提高了性能(xngnng)内部实现unsignedint(*poll)(structfile*filp,poll_table*wait);第45页/共95页第四十五页,共96页。日程安排设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作(co
26、zu)时间,延时和延后工作第46页/共95页第四十六页,共96页。测量(cling)时间流失内核通过(tnggu)定时器中断来跟踪时间的流动定时器中断由系统定时硬件以规律地间隔产生每次发生一个时钟中断,一个内核计数器的值递增.这个计数器在系统启动时初始化为0,因此它代表从最后一次启动以来的时钟嘀哒的数目这个计数器是一个64-位变量(即便在32-位的体系上)并且称为jiffies_64第47页/共95页第四十七页,共96页。获知当前(dngqin)时间voiddo_gettimeofday(structtimeval*tv);第48页/共95页第四十八页,共96页。延后执行(zhxng)长延时技
27、术(jsh)忙等待让出处理器超时短延时技术(jsh)voidndelay(unsignedlongnsecs);voidudelay(unsignedlongusecs);voidmdelay(unsignedlongmsecs);第49页/共95页第四十九页,共96页。内核(nih)定时器structtimer_list/*.*/unsignedlongexpires;void(*function)(unsignedlong);unsignedlongdata;voidinit_timer(structtimer_list*timer);structtimer_listTIMER_INITI
28、ALIZER(_function,_expires,_data);voidadd_timer(structtimer_list*timer);intdel_timer(structtimer_list*timer);第50页/共95页第五十页,共96页。Tasklets机制(jzh)structtasklet_struct/*.*/void(*func)(unsignedlong);unsignedlongdata;voidtasklet_init(structtasklet_struct*t,void(*func)(unsignedlong),unsignedlongdata);DECLAR
29、E_TASKLET(name,func,data);DECLARE_TASKLET_DISABLED(name,func,data);第51页/共95页第五十一页,共96页。Tasklet特性(txng)一个tasklet能够被禁止并且之后被重新使能;它不会执行直到它被使能与被禁止相同的的次数.如同定时器,一个tasklet可以注册它自己.一个tasklet能被调度来执行以正常的优先级或者高优先级.后一组一直是首先执行.taslet可能立刻运行,如果系统不在重载下,但是从不会晚于下一个时钟嘀哒.一个tasklet可能和其他tasklet并发,但是对它自己是严格(yng)地串行的-同样的task
30、let从不同时运行在超过一个处理器上.同样,如已经提到的,一个tasklet常常在调度它的同一个CPU上运行.第52页/共95页第五十二页,共96页。工作(gngzu)队列工作队列表面类似于tasketstasklet在软件中断上下文中运行的结果是所有(suyu)的tasklet代码必须是原子的.相反,工作队列函数在一个特殊内核进程上下文运行;结果,它们有更多的灵活性.特别地,工作队列函数能够睡眠.tasklet常常在它们最初被提交的处理器上运行.工作队列以相同地方式工作内核代码可以请求工作队列函数被延后一个明确的时间间隔.第53页/共95页第五十三页,共96页。工作(gngzu)队列stru
31、ctworkqueue_struct*create_workqueue(constchar*name);structworkqueue_struct*create_singlethread_workqueue(constchar*name);第54页/共95页第五十四页,共96页。日程安排设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作(cozu)时间,延时和延后工作分配内存第55页/共95页第五十五页,共96页。内存(nicn)分配内存分配的最常用(chnyn)接口.#includevoid*kmalloc(size_tsize,intflags);voidkfree(v
32、oid*obj);第56页/共95页第五十六页,共96页。内存分配(fnpi)标志控制内存分配如何进行的标志,从最少限制的到最多的.GFP_USER和GFP_KERNEL优先级允许当前(dngqin)进程被置为睡眠来满足请求.GFP_NOFS和GFP_NOIO禁止文件系统操作和所有的I/O操作,分别地,而GFP_ATOMIC分配根本不能睡眠.#includeGFP_USERGFP_KERNELGFP_NOFSGFP_NOIOGFP_ATOMIC第57页/共95页第五十七页,共96页。内存(nicn)分配标志这些(zhxi)标志分配内存时修改内核的行为_GFP_DMA_GFP_HIGHMEM_G
33、FP_COLD_GFP_NOWARN_GFP_HIGH_GFP_REPEAT_GFP_NOFAIL_GFP_NORETRY第58页/共95页第五十八页,共96页。slab缓存创建和销毁一个slab缓存.这个缓存可被用来(ynli)分配几个相同大小的对象.#includekmem_cache_t*kmem_cache_create(char*name,size_tsize,size_toffset,unsignedlongflags,constructor(),destructor();intkmem_cache_destroy(kmem_cache_t*cache);第59页/共95页第五十九
34、页,共96页。缓存标志(biozh)在创建一个缓存时可指定(zhdng)的标志.SLAB_CTOR_ATOMICSLAB_CTOR_CONSTRUCTOR第60页/共95页第六十页,共96页。缓存中分配(fnpi)释放单个对象从缓存中分配和释放一个(y)单个对象./proc/slabinfo一个(y)包含对slab缓存使用情况统计的虚拟文件.void*kmem_cache_alloc(kmem_cache_t*cache,intflags);voidkmem_cache_free(kmem_cache_t*cache,constvoid*obj);第61页/共95页第六十一页,共96页。日程安
35、排设备(shbi)驱动简介建立和运行模块字符驱动调试技术并发和竞争高级字符驱动操作时间,延时和延后工作分配内存I/O读写第62页/共95页第六十二页,共96页。硬件(ynjin)读写屏障硬件内存屏障.它们请求CPU(和编译器)来检查所有(suyu)的跨这个指令的内存读,写#includevoidrmb(void);voidread_barrier_depends(void);voidwmb(void);voidmb(void);第63页/共95页第六十三页,共96页。I/O读写用来(ynli)读和写I/O端口的函数.它们还可以被用户空间程序调用,如果它们有正当的权限来存取端口.#include
36、unsignedinb(unsignedport);voidoutb(unsignedcharbyte,unsignedport);unsignedinw(unsignedport);voidoutw(unsignedshortword,unsignedport);unsignedinl(unsignedport);voidoutl(unsigneddoubleword,unsignedport);第64页/共95页第六十四页,共96页。延时读写函数(hnsh)如果在一次I/O操作后需要一个小延时,你可以使用在前一项中介绍的这些函数的6个暂停(zntn)对应部分;这些暂停(zntn)函数以_p
37、结尾unsignedinb_p(unsignedport);第65页/共95页第六十五页,共96页。字串函数(hnsh)这些(zhxi)字串函数被优化为传送数据从一个输入端口到一个内存区,或者其他的方式.这些(zhxi)传送通过读或写到同一端口count次来完成.voidinsb(unsignedport,void*addr,unsignedlongcount);voidoutsb(unsignedport,void*addr,unsignedlongcount);voidinsw(unsignedport,void*addr,unsignedlongcount);voidoutsw(unsi
38、gnedport,void*addr,unsignedlongcount);voidinsl(unsignedport,void*addr,unsignedlongcount);voidoutsl(unsignedport,void*addr,unsignedlongcount);第66页/共95页第六十六页,共96页。I/O端口资源分配I/O端口的资源分配器.这个检查函数成功返回(fnhu)0并且在错误时小于0#includestructresource*request_region(unsignedlongstart,unsignedlonglen,char*name);voidrelea
39、se_region(unsignedlongstart,unsignedlonglen);intcheck_region(unsignedlongstart,unsignedlonglen);第67页/共95页第六十七页,共96页。I/O地址映射ioremap重映射一个物理地址范围到处理器的虚拟地址空间,使它对内核可用.iounmap释放(shfng)映射当不再需要它时.#includevoid*ioremap(unsignedlongphys_addr,unsignedlongsize);void*ioremap_nocache(unsignedlongphys_addr,unsignedl
40、ongsize);voidiounmap(void*virt_addr);第68页/共95页第六十八页,共96页。内存(nicn)区处理资源分配为内存(nicn)区处理资源分配的函数structresource*request_mem_region(unsignedlongstart,unsignedlonglen,char*name);voidrelease_mem_region(unsignedlongstart,unsignedlonglen);intcheck_mem_region(unsignedlongstart,unsignedlonglen);第69页/共95页第六十九页,共9
41、6页。I/O内存(nicn)存取函数用来使用(shyng)I/O内存的存取者函数.#includeunsignedintioread8(void*addr);unsignedintioread16(void*addr);unsignedintioread32(void*addr);voidiowrite8(u8value,void*addr);voidiowrite16(u16value,void*addr);voidiowrite32(u32value,void*addr);第70页/共95页第七十页,共96页。I/O内存(nicn)函数.旧的,类型不安全(nqun)的存取I/O内存的函数.
42、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);第71页/共95页第七十一页,共96页。日程安排设备驱动简介建立
43、和运行模块字符驱动调试技术并发和竞争高级(goj)字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理第72页/共95页第七十二页,共96页。注册(zhc)注销中断处理调用这个注册和注销一个(y)中断处理.#includeintrequest_irq(unsignedintirq,irqreturn_t(*handler)(),unsignedlongflags,constchar*dev_name,void*dev_id);voidfree_irq(unsignedintirq,void*dev_id);第73页/共95页第七十三页,共96页。中断(zhngdun)申请标志给reque
44、st_irq的标志.SA_INTERRUPT请求安装(nzhung)一个快速处理者(相反是一个慢速的).SA_SHIRQ安装(nzhung)一个共享的处理者#includeSA_INTERRUPTSA_SHIRQSA_SAMPLE_RANDOM第74页/共95页第七十四页,共96页。中断(zhngdun)的文件系统节点报告硬件中断(zhngdun)和安装的处理者的文件系统节点./proc/interrupts/proc/stat第75页/共95页第七十五页,共96页。驱动使用探测(tnc)函数驱动使用(shyng)的函数,探测决定哪个中断线被设备在使用(shyng).probe_irq_on的
45、结果必须传回给probe_irq_off在中断产生之后.probe_irq_off的返回值是被探测的中断号.unsignedlongprobe_irq_on(void);intprobe_irq_off(unsignedlong);第76页/共95页第七十六页,共96页。中断处理(chl)返回从一个中断(zhngdun)处理返回的可能值,指示是否一个来自设备的真正的中断(zhngdun)出现了.IRQ_NONEIRQ_HANDLEDIRQ_RETVAL(intx)第77页/共95页第七十七页,共96页。使能和禁止(jnzh)中断可以使能和禁止中断(zhngdun)。共享处理不使用这个函数.vo
46、iddisable_irq(intirq);voiddisable_irq_nosync(intirq);voidenable_irq(intirq);第78页/共95页第七十八页,共96页。禁止(jnzh)中断使用local_irq_save来禁止本地处理器的中断并且记住它们之前(zhqin)的状态voidlocal_irq_save(unsignedlongflags);voidlocal_irq_restore(unsignedlongflags);第79页/共95页第七十九页,共96页。使能和禁止(jnzh)中断在当前处理器无条件禁止和使能中断(zhngdun)的函数.voidloca
47、l_irq_disable(void);voidlocal_irq_enable(void);第80页/共95页第八十页,共96页。日程安排设备驱动简介建立和运行模块字符驱动调试技术并发和竞争高级(goj)字符驱动操作时间,延时和延后工作分配内存与硬件通讯中断处理块设备驱动第81页/共95页第八十一页,共96页。块设备(shbi)注册register_blkdev注册(zhc)一个块驱动到内核,并且,可选地,获得一个主编号.一个驱动可被注销,使用unregister_blkdev.#includeintregister_blkdev(unsignedintmajor,constchar*nam
48、e);intunregister_blkdev(unsignedintmajor,constchar*name);第82页/共95页第八十二页,共96页。块设备相关(xinggun)数据结构块设备驱动的数据结构( jigu).structblock_device_operations描述内核中单个块设备的结构( jigu).#includestructgendisk;分配gendisk结构( jigu)的函数,并且返回它们到系统.structgendisk*alloc_disk(intminors);voidadd_disk(structgendisk*gd);第83页/共95页第八十三页,共
49、96页。块设备相关(xinggun)函数voidset_capacity(structgendisk*gd,sector_tsectors);存储设备能力(以512-字节(zji)在gendisk结构中.voidadd_disk(structgendisk*gd);添加一个磁盘到内核.一旦调用这个函数,你的磁盘的方法可被内核调用.intcheck_disk_change(structblock_device*bdev);一个内核函数,检查在给定磁盘驱动器中的介质改变,并且采取要求的清理动作当检测到这样一个改变.第84页/共95页第八十四页,共96页。请求队列(duli)相关函数#include
50、request_queue_tblk_init_queue(request_fn_proc*request,spinlock_t*lock);voidblk_cleanup_queue(request_queue_t*);处理块请求队列的创建(chungjin)和删除的函数.structrequest*elv_next_request(request_queue_t*queue);voidend_request(structrequest*req,intsuccess);elv_next_request从一个请求队列中获得下一个请求;end_request可用在每个简单驱动器中来标识一个(或部
51、分)请求完成.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来使你的请求方法被再次调用.第
52、85页/共95页第八十五页,共96页。请求队列参数控制(kngzh)函数设置(shzh)各种队列参数的函数,来控制请求如何被创建给一个特殊设备voidblk_queue_bounce_limit(request_queue_t*queue,u64dma_addr);voidblk_queue_max_sectors(request_queue_t*queue,unsignedshortmax);voidblk_queue_max_phys_segments(request_queue_t*queue,unsignedshortmax);voidblk_queue_max_hw_segments
53、(request_queue_t*queue,unsignedshortmax);voidblk_queue_max_segment_size(request_queue_t*queue,unsignedintmax);blk_queue_segment_boundary(request_queue_t*queue,unsignedlongmask);voidblk_queue_dma_alignment(request_queue_t*queue,intmask);voidblk_queue_hardsect_size(request_queue_t*queue,unsignedshortm
54、ax);第86页/共95页第八十六页,共96页。块I/O请求(qngqi)#includestructbio;低级函数,表示一个块I/O请求的一部分.bio_sectors(structbio*bio);bio_data_dir(structbio*bio);2个宏定义,表示一个由bio结构( jigu)描述的传送的大小和方向.bio_for_each_segment(bvec,bio,segno);一个伪控制结构( jigu),用来循环组成一个bio结构( jigu)的各个段.char*_bio_kmap_atomic(structbio*bio,inti,enumkm_typetype);
55、void_bio_kunmap_atomic(char*buffer,enumkm_typetype);_bio_kmap_atomic可用来创建一个内核虚拟地址给一个在bio结构( jigu)中的给定的段.映射必须使用_bio_kunmap_atomic来恢复.第87页/共95页第八十七页,共96页。块I/O请求(qngqi)intend_that_request_first(structrequest*req,intsuccess,intcount);voidend_that_request_last(structrequest*req);使用(shyng)end_that_request
56、_firest来指示一个块I/O请求的一部分完成.当那个函数返回0,请求完成并且应当被传递给end_that_request_last.rq_for_each_bio(bio,request)另一个用宏定义来实现的控制结构;它步入构成一个请求的每个bio.intblk_rq_map_sg(request_queue_t*queue,structrequest*req,structscatterlist*list);为一次DMA传送填充给定的散布表,用需要来映射给定请求中的缓冲的信息typedefint(make_request_fn)(request_queue_t*q,structbio*b
57、io);make_request函数的原型.voidbio_endio(structbio*bio,unsignedintbytes,interror);指示一个给定bio的完成.这个函数应当只用在你的驱动直接获取bio,通过make_request函数从块层.request_queue_t*blk_alloc_queue(intflags);voidblk_queue_make_request(request_queue_t*queue,make_request_fn*func);使用(shyng)blk_alloc_queue来分配由定制的make_request函数使用(shyng)的请
58、求队列,.那个函数应当使用(shyng)blk_queue_make_request来设置.第88页/共95页第八十八页,共96页。文件系统原理(yunl)文件(wnjin)系统操作系统通过文件(wnjin)系统管理设备、组织数据用户通过I/O函数操作文件(wnjin)文件(wnjin)分为逻辑结构和物理结构文件(wnjin)系统内置安全特性第89页/共95页第八十九页,共96页。嵌入式文件系统的层次结构根文件系统分区文件系统底层(dcn)硬件第90页/共95页第九十页,共96页。常见(chnjin)文件系统JFFS2CRAMFSFAT16/32EXT3第91页/共95页第九十一页,共96页。
59、文件系统超级(choj)块目录和文件Inode结构文件系统数据结构第92页/共95页第九十二页,共96页。文件系统分析(fnx)EXT3文件系统分析(fnx)FAT16/32文件系统分析(fnx)第93页/共95页第九十三页,共96页。谢谢(xixie)大家问题建议( jiny)反馈后续资源第94页/共95页第九十四页,共96页。感谢您的观看(gunkn)!第95页/共95页第九十五页,共96页。内容(nirng)总结日程安排。设备驱动就是module的代表, 但module还包括文件系统等等.。内核内部对数据的组织和管理不同, 对驱动开发者来说透明。对于不同发行版,不同内核版本要做少量移植和测试。模块跨越两个空间,有两个触发入口。unregister_this(ptr1, skull)。如果我们能够适当地安排使得两个进程不可能同时处于临界区,则就能够避免竞争条件。休眠醒来,无法确定(qudng)休眠时间和时序。感谢您的观看第九十六页,共96页。