您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 管理学资料 > Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型IO和休眠]
Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型I/O和休眠]Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型I/O和休眠]这一部分主要讨论:如果驱动程序无法立即满足请求,该如何响应?(65865346)一、休眠进程被置为休眠,意味着它被标识为处于一个特殊的状态并且从调度器的运行队列中移走。这个进程将不被在任何CPU上调度,即将不会运行。直到发生某些事情改变了那个状态。安全地进入休眠的两条规则:(1)永远不要在原子上下文中进入休眠,即当驱动在持有一个自旋锁、seqlock或者RCU锁时不能睡眠;关闭中断也不能睡眠。持有一个信号量时休眠是合法的,但你应当仔细查看代码:如果代码在持有一个信号量时睡眠,任何其他的等待这个信号量的线程也会休眠。因此发生在持有信号量时的休眠必须短暂,而且决不能阻塞那个将最终唤醒你的进程。(2)当进程被唤醒,它并不知道休眠了多长时间以及休眠时发生什么;也不知道是否另有进程也在休眠等待同一事件,且那个进程可能在它之前醒来并获取了所等待的资源。所以不能对唤醒后的系统状态做任何的假设,并必须重新检查等待条件来确保正确的响应。除非确信其他进程会在其他地方唤醒休眠的进程,否则也不能睡眠。使进程可被找到意味着:需要维护一个称为等待队列的数据结构。它是一个进程链表,其中饱含了等待某个特定事件的所有进程。在Linux中,一个等待队列由一个wait_queue_head_t结构体来管理,其定义在linux/wait.h中。wait_queue_head_t类型的数据结构非常简单:struct__wait_queue_head{spinlock_tlock;structlist_headtask_list;};typedefstruct__wait_queue_headwait_queue_head_t;它包含一个自旋锁和一个链表。这个链表是一个等待队列入口,它被声明做wait_queue_t。wait_queue_head_t包含关于睡眠进程的信息和它想怎样被唤醒。简单休眠(其实是高级休眠的宏)Linux内核中最简单的休眠方式是称为wait_event的宏(及其变种),它实现了休眠和进程等待的条件的检查。形式如下:wait_event(queue,condition)/*不可中断休眠,不推荐*/wait_event_interruptible(queue,condition)/*推荐,返回非零值意味着休眠被中断,且驱动应返回-ERESTARTSYS*/wait_event_timeout(queue,condition,timeout)wait_event_interruptible_timeout(queue,condition,timeout)/*有限的时间的休眠;若超时,则不管条件为何值返回0,*/唤醒休眠进程的函数称为wake_up,形式如下:voidwake_up(wait_queue_head_t*queue);voidwake_up_interruptible(wait_queue_head_t*queue);惯例:用wake_up唤醒wait_event;用wake_up_interruptible唤醒wait_event_interruptible。简单休眠实验模块程序链接:sleepy模块测试程序链接:sleepy-test实验现象:[Tekkaman2440@SBC2440V4]#cd/lib/modules/[Tekkaman2440@SBC2440V4]#insmodsleepy.ko[Tekkaman2440@SBC2440V4]#cd/dev/[Tekkaman2440@SBC2440V4]#cat/proc/devicesCharacterdevices:1mem2pty3ttyp4/dev/vc/04tty4ttyS5/dev/tty5/dev/console5/dev/ptmx7vcs10misc13input14sound81video4linux89i2c90mtd116alsa128ptm136pts180usb189usb_device204s3c2410_serial252sleepy253usb_endpoint254rtcBlockdevices:1ramdisk256rfd7loop31mtdblock93nftl96inftl179mmc[Tekkaman2440@SBC2440V4]#mknod-m666sleepyc2520[Tekkaman2440@SBC2440V4]#cd/tmp/[Tekkaman2440@SBC2440V4]#./sleepy_testr&[Tekkaman2440@SBC2440V4]#./sleepy_testr&[Tekkaman2440@SBC2440V4]#psPIDUidVSZStatCommand1root1744Sinit2rootSW[kthreadd]3rootSWN[ksoftirqd/0]4rootSW[watchdog/0]5rootSW[events/0]6rootSW[khelper]59rootSW[kblockd/0]60rootSW[ksuspend_usbd]63rootSW[khubd]65rootSW[kseriod]77rootSW[pdflush]78rootSW[pdflush]79rootSW[kswapd0]80rootSW[aio/0]707rootSW[mtdblockd]708rootSW[nftld]709rootSW[inftld]710rootSW[rfdd]742rootSW[kpsmoused]751rootSW[kmmcd]769rootSW[rpciod/0]778root1752S-sh779root1744Sinit781root1744Sinit783root1744Sinit787root1744Sinit799root1336S./sleepy_testr800root1336S./sleepy_testr802root1744Rps[Tekkaman2440@SBC2440V4]#./sleepy_testwreadcode=0writecode=0[2]+Done./sleepy_testr[Tekkaman2440@SBC2440V4]#psPIDUidVSZStatCommand1root1744Sinit2rootSW[kthreadd]3rootSWN[ksoftirqd/0]4rootSW[watchdog/0]5rootSW[events/0]6rootSW[khelper]59rootSW[kblockd/0]60rootSW[ksuspend_usbd]63rootSW[khubd]65rootSW[kseriod]77rootSW[pdflush]78rootSW[pdflush]79rootSW[kswapd0]80rootSW[aio/0]707rootSW[mtdblockd]708rootSW[nftld]709rootSW[inftld]710rootSW[rfdd]742rootSW[kpsmoused]751rootSW[kmmcd]769rootSW[rpciod/0]778root1752S-sh779root1744Sinit781root1744Sinit783root1744Sinit787root1744Sinit799root1336S./sleepy_testr804root1744Rps[Tekkaman2440@SBC2440V4]#./sleepy_testwwritecode=0[Tekkaman2440@SBC2440V4]#readcode=0[1]+Done./sleepy_testr[Tekkaman2440@SBC2440V4]#psPIDUidVSZStatCommand1root1744Sinit2rootSW[kthreadd]3rootSWN[ksoftirqd/0]4rootSW[watchdog/0]5rootSW[events/0]6rootSW[khelper]59rootSW[kblockd/0]60rootSW[ksuspend_usbd]63rootSW[khubd]65rootSW[kseriod]77rootSW[pdflush]78rootSW[pdflush]79rootSW[kswapd0]80rootSW[aio/0]707rootSW[mtdblockd]708rootSW[nftld]709rootSW[inftld]710rootSW[rfdd]742rootSW[kpsmoused]751rootSW[kmmcd]769rootSW[rpciod/0]778root1752S-sh779root1744Sinit781root1744Sinit783root1744Sinit787root1744Sinit806root1744Rps阻塞和非阻塞操作全功能的read和write方法涉及到进程可以决定是进行非阻塞I/O还是阻塞I/O操作。明确的非阻塞I/O由filp-f_flags中的O_NONBLOCK标志来指示(定义再linux/fcntl.h,被linux/fs.h自动包含)。浏览源码,会发现O_NONBLOCK的另一个名字:O_NDELAY,这是为了兼容SystemV代码。O_NONBLOCK标志缺省地被清除,因为等待数据的进程的正常行为只是睡眠.其实不一定只有read和write方法有阻塞操作,open也可以有阻塞操作。后面会见到。而我的项目有一个和CPLD的接口的驱动,我决定要在ioctl中使用阻塞。以下是后面的scullpipe实验的有关阻塞的代码,我觉得写得很好,先结合书上的介绍看看吧:while(dev-rp==dev-wp){/*nothingtoread*/up(&dev-sem);/*releasethelock*/if(filp-f_flags&O_NONBLOCK)return-EAGAIN;PDEBUG(\%s\reading:goingtosleep\n,current-comm);if(wait_event_interruptible(dev-inq,(dev-rp!=dev-wp)))return-ERESTARTSYS;/*signal:tellthefslayertohandleit*//*otherwiseloop,butfirstreacquirethelock*/if(down_interruptible(&dev-sem))return-ERESTARTSYS;}/*ok,dataisthere,returnsomething*/......高级休眠步骤:(1)分配和初始化一个wait_queue_t结构,随后将其添加到正确的等待队列。(2)设置进程状态,标记为休眠。在linux/sched.h中定义有几个任务状态:TASK_RUNNING意思是进程能够运行。有2个状态指示一个进程是在睡眠:TASK_INTERRUPTIBLE和TASK_UNTINTERRUPTIBLE。2.6内核的驱动代码通常不需要直接操作进程状态。但如果需要这样做使用的代码是:voidset_current_state(intnew_state);在老的代码中,你常常见到如此的东西:current-state=TASK_INTERRUPTIBLE;但是象这样直接改变current是不推荐的,当数据结构改变时这样的代码将会失效。通过改变current状态,只改变了调度器对待进程的方式,但进程还未让出处理器。(3)最后一步是放弃处理器。但必须先
本文标题:Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型IO和休眠]
链接地址:https://www.777doc.com/doc-6040322 .html