#导入Word文档图片# 阻塞与非阻塞IO操作
Posted DS小龙哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#导入Word文档图片# 阻塞与非阻塞IO操作相关的知识,希望对你有一定的参考价值。
Linux内核版本:linux3.5
1.1 简介
阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,从CPU调度器的运行队列中移走, 直到等待的条件被满足。 而非阻塞操作的进程在获取不到资源时不会进入睡眠状态, 它或者放弃获取资源, 或者不停地查询, 直到资源获取为止。
驱动程序通常需要提供这样的能力:当应用程序进行 read()、 write()等系统调用时,若设备的资源不能获取,而用户又希望以阻塞的方式访问设备, 驱动程序应在设备驱动的 xxx_read()、 xxx_write()等操作中将进程阻塞直到资源可以获取, 此后, 应用程序的 read()、 write()等调用才返回, 整个过程仍然进行了正确的设备访问,用户并没有感知到;若用户以非阻塞的方式访问设备文件, 则当设备资源不可获取时, 设备驱动的 xxx_read()、 xxx_write()等操作应立即返回, read()、 write()等系统调用也随即被返回。
阻塞从字面上听起来似乎意味着低效率,实则不然,如果设备驱动不阻塞,则用户想获取设备资源只能不停地查询,这反而会无谓地耗费 CPU 资源。 而阻塞访问时,不能获取资源的进程将进入休眠, 它将 CPU 资源让给其他进程。
因为阻塞的进程会进入休眠状态,因此,必须确保有一个地方能够唤醒休眠的进程。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着一个中断。
1.2 进程的状态
Linux将进程状态描述为如下五种:
TASK_RUNNING :可运行状态。处于该状态的进程可以被调度执行而成为当前进程。
TASK_INTERRUPTIBLE :信号可中断的睡眠状态。处于该状态的进程在所需资源有效时被唤醒,也可以通过信号或定时中断唤醒(因为有signal_pending()函数)。
TASK_UNINTERRUPTIBLE:不可中断的睡眠状态。处于该状态的进程仅当所需资源有效时被唤醒。
TASK_ZOMBIE :僵尸状态。表示进程结束且已释放资源,但其task_struct仍未释放。
TASK_STOPPED :暂停状态。处于该状态的进程通过其他进程的信号才能被唤醒。
进程调度函数,让出CPU使用权:
schedule(void) |
设置进程的状态(一般设置的状态为以上定义的五种):
set_current_state(state); |
1.3 等待队列
在 Linux 中, 一个等待队列由一个"等待队列头"来管理, 一个 wait_queue_head_t 类型的结构, 定义在<linux/wait.h>中. 一个等待队列头可被定义和初始化。
使用:
DECLARE_WAIT_QUEUE_HEAD(name); |
或者动态定义如下:
wait_queue_head_t my_queue; init_waitqueue_head(&my_queue); |
等待队列可以用来同步对系统资源的访问,前面所讲述的信号量在内核中也依赖等待队列来实现。
1.3.1 等待队列头休眠相关的API函数
1.3.1.1 静态初始化等待队列头
宏原型 | #define DECLARE_WAIT_QUEUE_HEAD(name) \\ wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name) |
宏功能 | 静态初始化数据。定义并且初始化一个等待队列头结构变量,名字为name。 |
宏参数 | name:要定义的队列头变量名 |
所在头文件 | include\\linux\\wait.h |
宏定义文件 | include\\linux\\wait.h |
1.3.1.2 动态初始化等待队列头
宏原型 | /*动态初始化等待队列头*/ #define init_waitqueue_head(q)\\ do \\ static struct lock_class_key __key;\\ \\ __init_waitqueue_head((q), #q, &__key);\\ while (0) |
宏功能 | 动态初始一个等待队列头 |
宏参数 | q:要定义的队列头变量名指针 |
所在头文件 | include\\linux\\wait.h |
宏定义文件 | include\\linux\\wait.h |
示例:
wait_queue_head_t wq; 定义全局变量)
init_waitqueue_head(&wq); 要在其他函数内调用初始化)
上面这两行等效于:DECLARE_WAIT_QUEUE_HEAD(wq)
1.3.1.3 休眠进程(不可中断)
宏原型 | #define wait_event(wq, condition) \\ do \\ if (condition)\\ break;\\ __wait_event(wq, condition);\\ while (0) |
宏功能 | 让进程在等待队列头上休眠。在等待会列中睡眠直到condition为真。在等待的期间,进程会被置为TASK_UNINTERRUPTIBLE(不可中断)进入睡眠,直到condition变量变为真。每次进程被唤醒的时候都会检查condition的值。 注意:这里的中断指的是信号(软中断)。 比如:ctrl+c信号等。 信号会进行阻塞,如果在进程睡眠时发送了信号给进程,这时进程是收不到信号的,但是信号并没有消失,当进程被唤醒的时候就会收到信号! |
宏参数 | wq:要休眠的队列头 ; condition:休眠的条件,0时休眠,否则不进入休眠,不能直接传递数字,要传递变量。 |
所在头文件 | include\\linux\\wait.h |
宏定义文件 | include\\linux\\wait.h |
1.3.1.4 休眠进程(可中断)
宏原型 | #define wait_event_interruptible(wq, condition)\\ (\\ int __ret = 0;\\ if (!(condition))/*判断--->条件为假的时候进入休眠*/\\ __wait_event_interruptible(wq, condition, __ret);\\ __ret;\\ ) |
宏功能 | 让进程在等待队列头上休眠。wait_event()的区别是调用该宏在等待的过程中当前进程会被设置为TASK_INTERRUPTIBLE休眠状态.在每次被唤醒的时候,首先检查condition是否为真,如果为真则返回,否则检查如果进程是被信号唤醒,会返回-ERESTARTSYS错误码.如果是condition为真,则返回0. |
宏参数 | wq:要休眠的队列头 ; condition:休眠的条件,0时休眠,否则不进入休眠 |
所在头文件 | include\\linux\\wait.h |
宏定义文件 | include\\linux\\wait.h |
1.3.1.5 休眠进程(可以指定时间)(不可中断)
宏原型 | #define wait_event_timeout(wq, condition, timeout)\\ (\\ long __ret = timeout;\\ if (!(condition)) \\ __wait_event_timeout(wq, condition, __ret);\\ __ret;\\ ) |
宏功能 | 让进程在等待队列头上休眠。与wait_event()类似.不过如果所给的睡眠时间为负数则立即返回.如果在睡眠期间被唤醒,且condition为真则返回剩余的睡眠时间,否则继续睡眠直到到达或超过给定的睡眠时间,然后返回0. |
宏参数 | wq:要休眠的队列头 ; condition:休眠的条件,0时休眠,否则不进入休眠; timeout:超时时间,单位是时钟节拍,一般使用:1 * HZ (1秒)这样形式定义。 |
所在头文件 | include\\linux\\wait.h |
宏定义文件 | include\\linux\\wait.h |
1.3.1.6 休眠进程(可以设置超时时间)(可中断)
宏原型 | #define wait_event_interruptible_timeout(wq, condition, timeout)\\ (\\ long __ret = timeout;\\ if (!(condition))\\ __wait_event_interruptible_timeout(wq, condition, __ret); \\ __ret;\\ ) |
宏功能 | 让进程在等待队列头上休眠。与wait_event_timeout()类似 如果在睡眠期间被信号打断则返回-ERESTARTSYS错误码 |
宏参数 | wq:要休眠的队列头 ; condition:休眠的条件,0时休眠,否则不进入休眠; timeout:超时时间,单位是时钟节拍,一般使用:5 * HZ (5秒)这样形式定义。 |
所在头文件 | include\\linux\\wait.h |
宏定义文件 | include\\linux\\wait.h |
1.3.2唤醒进程相关的函数API
上面介绍的是让进程休眠函数,接下来就是唤醒进程的函数。
1.3.2.1 唤醒休眠的进程(中断与非中断都可以)
宏原型 | #define wake_up(x)__wake_up(x, TASK_NORMAL, 1, NULL) |
宏功能 | 可唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERUPTIBLE状态的进程, 与wait_event/wait_event_timeout成对使用。 |
宏参数 | x:要唤醒的队列头指针 ; |
所在头文件 | include\\linux\\wait.h |
宏定义文件 | include\\linux\\wait.h |
示例:wake_up(&wq)
说明:如果驱动里阻塞了5个进程,只会唤醒第一个休眠的进程,按照先后顺序。
1.3.2.2 唤醒休眠的进程(可中断类型)
宏原型 | #define wake_up_interruptible(x)__wake_up(x, TASK_INTERRUPTIBLE, 1, NULL) |
宏功能 | NIO之阻塞IO与非阻塞IO |