首页 第四章 阻塞和非阻塞型IO

第四章 阻塞和非阻塞型IO

举报
开通vip

第四章 阻塞和非阻塞型IOnull上章回顾上章回顾从并发的需要,引起的竞争状态,到解决竞争状态的方法机制: 禁止中断 信号量 自旋锁 Completion 原子操作 阻塞和非阻塞型I/O 阻塞和非阻塞型I/O第4章本章目标本章目标 掌握进程睡眠和唤醒的方法 掌握阻塞型I/O的实现方法 掌握poll/select系统调用的实现方法 本章结构本章结构阻塞型I/O 阻塞和非阻塞型I/O异步通知 非阻塞I/O 非阻塞I/Opoll/select进程休眠和唤醒方法 阻塞型I/O实例 4...

第四章 阻塞和非阻塞型IO
null上章回顾上章回顾从并发的需要,引起的竞争状态,到解决竞争状态的方法机制: 禁止中断 信号量 自旋锁 Completion 原子操作 阻塞和非阻塞型I/O 阻塞和非阻塞型I/O第4章本章目标本章目标 掌握进程睡眠和唤醒的方法 掌握阻塞型I/O的实现方法 掌握poll/select系统调用的实现方法 本章结构本章结构阻塞型I/O 阻塞和非阻塞型I/O异步通知 非阻塞I/O 非阻塞I/Opoll/select进程休眠和唤醒方法 阻塞型I/O实例 4-1 阻塞型I/O4-1 阻塞型I/O当用户程序调用read函数时,驱动程序的read并没有准备好数据,怎么办?当用户程序调用write时,驱动程序的write的缓冲区已满,此时怎么办?1.用户程序不会管理这些问题。 2.驱动程序默认情况下,阻塞该进程。将其置入休眠状态直到请求可继续 3. 直接返回,这是非阻塞IO 4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法休眠的意义? 从调度器的运行队列某个等待队列 等到某个事件发生,在从等待队列返回到运行队列。 如何将进程安全的进入休眠状态? 不能在原子上下文进行休眠: 休眠时,对外界一无所知,进程必须重新检测等待条件 进程只有确保会被其他进程唤醒,才能进入休眠 需要等待队列来管理和维护这些信息: 等待队列就是一个进程链表,其中包含了等待某个特定事件的所有进程4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法等待队列的作用 实现阻塞进程的唤醒 实现内核中的异步事件通知机制 队列的数据结构 系统的调度机制 同步系统资源的访问 Semaphore可以用wait queue来实现 4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法 等待队列通过”等待队列头”来管理 Struct wait_queue_head_t DECLARE_WAIT_QUEUE_HEAD(name);wait_queue_head_t xxx_queue; init_waitqueue_head(&xxx_queue);4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法等待事件 在休眠的同时,也检查进程等待的条件 休眠函数wait_event(queue,condition); wait_event_interruptible(queue,condition);wait_event_timeout(queue,condition,timeout); wait_event_interruptible_timeout(queue, condition,timeout);休眠前后都要对该表达式求值;在条件为真之前,进程会保持休眠等待队列头,通过”值传递”等待限定的时间4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法唤醒函数 wake_up(wait_queue_head_t *queue); wake_up_interruptible(wait_queue_head_t *queue);约定的做法是成对使用wake_up_nr(wait_queue_head_t *queue,int nr); wake_up_interruptible_nr(wait_queue_head_t *queue,int nr);唤醒nr各独占等待进程,而不是一个,当nr=0时,唤醒所有的独占等待进程。wake_up_all(wait_queue_head_t *queue); wake_up_interruptible_all(wait_queue_head_t *queue);wake_up_interruptible_sync(wait_queue_head_t *queue);4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法static DECLARE_WAIT_QUEUE_HEAD(wq); static int flag = 0; ssize_t sleepy_read(struct file *filp,char __user *buf, size_t count,loff t *pos) { printk(KERN_DEBUG “process %i (%s) going to sleep\n”, current->pid,current->comm); wait_event_interruptible(wq,flag != 0); flag = 0; printk(KERN_DEBUG “awoken %i (%s)\n”, current->pid,current->comm); return 0; } 定义并初始化wait_queue_head_t 打印出是哪个进程调用驱动程序。在调用前后都要检查condition,如果满足条件,就不再休眠,否则进入休眠状态4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法ssize_t sleepy_write(struct file *filp, const char __user *buf, size_t count,loff_t *pos) { printk(KERN_DEBUG “process %i (%s) awakening the readers…\n”, current->pid,current->comm); flag = 1; wake_up_interruptible(&wq); return count; } 察看那个进程调用驱动的write函数唤醒休眠在wq队列上的所有的进程。4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法上面实例中存在一个概率很小的竟态条件 A,B 进程都等在wq的队列上 C进程调用wake_up_interruptible A进程被唤醒,检查条件flag !=0 成立 此时,调度到B进程 B进程也检查到flag != 0成立, 这样,一个事件唤醒了 两 个进程,可能产生竞 态 可以用原子操作防止这种情况 4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法设置进程休眠的内部细节 分配并初始化一个wait_queue_t结构 包含休眠进程的信息,以及期望被唤醒的相关细节 设置进程的状态,将其标记为休眠状态 TASK_INTERRUTIBLE TASK_UNINTERRUPTIBLE 让出处理器 void set_current_state(int new_state);current->state=TASK_INTERRUPTIBLE;if(!condition) schedule();对应于两种休眠状态新旧函数操作进程状态4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法手工休眠 早期版本的方法 读者可以自行了解 唤醒等待队列可能发生的情况 当调用wake_up时,所有等待在该队列上的进程都被唤醒,并进入可运行状态 如果只有一个进程可获得资源,此时,其他的进程又将再次进入休眠 如果数量很大,被称为”疯狂兽群”4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法独占等待 与普通休眠的不同 等待队列入口设置了WQ_FLAG_EXCLUSIVE标志时,则会被添加到等待队列的尾部。而没有这个标志的入口会被添加到头部。 在某个等待队列上调用wake_up时,它会在唤醒第一个具有WQ_FLAG_EXCLUSIVE标志的进程之后停止唤醒其他进程。 使进程进入独占等待函数: void prepare_to_wait_exclusive(wait_queue_head_t *queue wait_queue_t *wait,int state);使用wait_event及其变种无法使用独占等待4-1-1进程休眠和唤醒方法4-1-1进程休眠和唤醒方法其他休眠调用 void sleep_on(wait_queue_head_t *queue); void interruptible_sleep_on(wait_queue_head_t *queue); 4-1-2阻塞型IO实例4-1-2阻塞型IO实例struct xxx_pipe{ wait_queue_head_t inq,outq; /*读取和写入队列*/ char *buffer,*end; /*缓冲区的起始和结尾*/ int buffersize; /*用于指针计算*/ char *rp,*wp; /*读取和写入的位置*/ int nreaders,nwriters; /*用于读写打开的数量*/ struct fasync_struct *async_queue;/*异步读取者*/ struct semaphore sem; /*互斥信号量*/ struct cdev cdev; /*字符设备结构*/ }; 4-1-2阻塞型IO实例4-1-2阻塞型IO实例static ssize_t xxx_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos) { struct xxx_pipe *dev=filp->private_data; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; while(dev->rp == dev->wp) /*缓冲区没有准备好,无数据可读取*/ {up(&dev->sem);/*进入休眠之前,释放信号量*/ if(filp->f-flags & O_NONBLOCK) return -EAGAIN; if(wait_event_interruptible(dev->inq,(dev->rp!=dev->wp)) return -ERESTARTSYS;/*此时应该有进程唤醒,交给内核上层处理*/ /*否则循环,但首先获取锁,防止被其他竞争者拿着数据*/ if(down_interruptible(&dev->sem)) return -ERESTARTSYS; }4-1-2阻塞型IO实例4-1-2阻塞型IO实例/*数据已就绪,返回*/ if(dev->wp > dev->rp) count=min(count,(size_t)(dev->wp - dev->rp)); else /*写入指针回卷,返回数据直到dev->end*/ count=min(count,(size_t)(dev->end – dev->rp)); if(copy_to_user(buf,dev->rp,count)) { up(&dev->sem); return -EFAULT; } dev->rp += count: if(dev->rp == dev->end) dev->rp = dev->buffer;/*回卷*/ up(&dev->sem); /*最后,唤醒所有写入者并返回*/ wake_up_interruptible(&dev->outq); return count; } 阶段总结阶段总结介绍了阻塞型I/O的实现方法 介绍了进程休眠和唤醒的方法,重点介绍了wait_event和wake_up 4-2 非阻塞I/O4-2 非阻塞I/O调用进程显式的指明不想阻塞 设置filp->f_flag |= O_NONBLOCK int fd; char buf; fd = open(“/dev/ttyS1”,O_RDWR | O_NONBLOCK); … while(read(fd, &buf, 1) != 1);/*串口上无输入也返回,所以要循环尝试读取串口*/ printf(“%c\n”,buf); 4-2-1 poll和select4-2-1 poll和selectPoll、select、epoll 都允许进程决定是否可以对一个或多个打开的文件做非阻塞的读取或写入 这些调用也会阻塞进程,直到给定的文件描述符集合中的任何一个可读取或写入 常常用于那些要使用多个输入或输出流而又不会阻塞于其中任何一个流的应用程序中 4-2-1 poll和select4-2-1 poll和select都是调用驱动程序的poll来实现的 在一个或多个可指示poll状态变化的等待队列上调用poll_wait 如果当前没有文件描述符可用来执行I/0,则内核将使进程在传递到该系统调用的所有文件描述符对应的等待队列上等待。 返回一个用来描述操作是否可以立即无阻塞执行的位掩码unsigned int (*poll)(struct file *filp,poll_table *wait);中声明,驱动程序不需要了解该结构的细节void poll_wait(struct file *,wait_queue_head_t *,poll_table *);向poll_table添加一个等待队列返回可以立即执行操作的位掩码4-2-1 poll和select4-2-1 poll和select static unsigned int xxx_poll(struct file *filp,poll_table *wait) { struct xxx_pipe *dev = filp->private_data; unsigned int mask=0; down(&dev->sem); poll_wait(filp,&dev->inq,wait); poll_wait(filp,&dev->outq,wait); if(read_buffer_not_empty) //如果接收buffer不为空,可读 mask |= POLLIN | POLLRDNORM; /*可读取*/ if(write_buffer_not_full) //如果写buffer不满,可写 mask |= POLLOUT | POLLWRNORM; /*可写入*/ up(&dev->sem); return mask; }增加两个等待队列到poll_table中4-2-1 poll和select4-2-1 poll和select调用poll和select的目的是: 确定接下来的操作是否是阻塞型的 使应用程序可以同时等待多个数据流 从设备读取数据 read可以返回比请求少的数据 如果设置了O_NONBLOCK, 在没有数据时,立即返回。此时poll会 报告 软件系统测试报告下载sgs报告如何下载关于路面塌陷情况报告535n,sgs报告怎么下载竣工报告下载 设备不可读。 如果到达文件尾,poll会报告POLLUP. 向设备些数据 写缓冲区有空间,poll应该报告设备可写 写缓冲区已满,poll应该报告设备不可写 永远不要让write调用在返回前等待数据的传输结束,即使O_NONBLOCK标志被清除。 4-2-1 poll和select4-2-1 poll和selectselect()系统调用的原型如下: 文件描述符集合相关函数: int select ( int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);struct timeval { int tv_sec; /*秒*/ int tv_usec; /*微妙*/ }; FD_ZERO(fd_set *set);FD_SET(int fd, fd_set *set);FD_CLR(int fd, fd_set *set);FD_ISSET(int fd, fd_set *set);清除一个文件描述符集将一个文件描述符加入文件描述符集中将一个文件描述符从文件描述符集中清除。判断文件描述符是否被置位 4-2 非阻塞I/O4-2 非阻塞I/O int fd; fd_set rfds,wfds;//读/写文件描述符集 /*以非阻塞方式打开/dev/xxx设备文件*/ fd = open(“/dev/xxx”, O_RDWR | O_NONBLOCK); FD_ZERO(&rfds); FD_ZERO(&wfds); FD_SET(fd, &rfds); FD_SET(fd, &wfds); select(fd + 1, &rfds, &wfds, NULL, NULL); /*数据可获得*/ if(FD_ISSET(fd, &rfds)) { //读数据 } if(FD_ISSET(fd, &wfds)) { //写数据 }阶段总结阶段总结poll/select在驱动程序中的实现 应用层如何调用驱动中的poll/select本章总结本章总结重点掌握进程休眠和唤醒方法重点掌握poll/select实现方法一般了解阻塞型I/O 阻塞和非阻塞型I/O异步通知 非阻塞I/O 非阻塞I/Opoll/select进程休眠和唤醒方法 阻塞型I/O实例 实验实验任务一、阻塞型字符设备驱动实验
本文档为【第四章 阻塞和非阻塞型IO】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_628363
暂无简介~
格式:ppt
大小:412KB
软件:PowerPoint
页数:0
分类:互联网
上传时间:2011-12-24
浏览量:19