阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的
条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到
等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时并不挂起,它或者放
弃,或者不停地查询,直至可以进行操作为止。
驱动程序通常需要提供这样的能力:当应用程序进行read()、write()等系统调用时,
若设备的资源不能获取,而用户又希望以阻塞的方式访问设备,驱动程序应在设备驱
动的xxx_read()、xxx_write()等操作中将进程阻塞直到资源可以获取,此后,应用程
序的read()、write()等调用才返回,整个过程仍然进行了正确的设备访问,用户并没
有感知到;若用户以非阻塞的方式访问设备文件,则当设备资源不可获取时,设备驱
动的xxx_read()、xxx_write()等操作应立即返回,read()、write()等系统调用也随即被
返回。
阻塞从字面上听起来似乎意味着低效率,实则不然,如果设备驱动不阻塞,则用
户想获取设备资源只能不停地查询,这反而会无谓地耗费CPU资源。而阻塞访问时,
不能获取资源的进程将进入休眠,它将CPU资源让给其他进程。
因为阻塞的进程会进入休眠状态,因此,必须确保有一个地方能够唤醒休眠的进
程。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着
一个中断。
在 Linux 驱动程序中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒。
wait queue 很早就作为一个基本的功能单位出现在Linux 内核里了,它以队列为基础
数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。
使用非阻塞I/O 的应用程序也可借助轮询函数来查询设备是否能
立即被访问,用户空间调用select()和poll()接口,设备驱动提供poll()函数。设备驱动
的poll()本身不会阻塞,但是poll()和select()系统调用则会阻塞地等待文件描述符集合
中的至少一个可访问或超时。