阻塞I/O
阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞I/O
而非阻塞操作的进程在不能进行设备操作时,并不挂起,它要么放弃,要么不停地查询,直至可以进行操作为止。
等待队列
等待队列以队列为基础数据结构,与进程调度机制紧密结合,可以用来实现阻塞进程的唤醒。
使用方法
定义等待队列头
wait_queue_head_t my_queue;
初始化等待队列头
init_waitqueue_head(&my_queue);
还可以用宏定义
DECLARE_WAIT_QUEUE_HEAD (name);
定义等待队列元素
DECLARE_WAITQUEUE(name, tsk);tsk一般为current,指当前进程。
添加/移除等待队列
void add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
等待事件
wait_event(queue, condition)
wait_event_interruptible(queue, condition) //可以被信号打断
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
queue作为等待队列头部的队列被唤醒,而且第2个参数condition必须满足,否则继续阻塞。
唤醒队列
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
唤醒以queue作为等待队列头部的队列中所有的进程。
在等待队列上睡眠
sleep_on(wait_queue_head_t *q );
interruptible_sleep_on(wait_queue_head_t *q );
这两个函数可以把进程状态设置为TASK_UNINTERRUPTIBLE。
驱动使用等待队列模板
static ssize_t xxx_write(struct file *file, const char *buffer, size_t count,loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&xxx_wait, &wait);
do {
avail = device_writable(...);
if (avail < 0) {
if (file->f_flags &O_NONBLOCK) {
ret = -EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE);
schedule(); /* 调度其他进程执行 */
if (signal_pending(current)) { /* 如果是因为信号唤醒 */
ret = -ERESTARTSYS;
goto out;
}
}
} while (avail < 0);
/* 写设备缓冲区 */
device_write(...)
out:
remove_wait_queue(&xxx_wait, &wait); /* 将元素移出 xxx_wait 指引的队列 */
set_current_state(TASK_RUNNING); /* 设置进程状态为 TASK_RUNNING */
return ret;
}
轮询操作
使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询是否可对设备进行无阻塞的访问。select()和poll()系统调用最终会使设备驱动中的poll()函数被执行。
select函数
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);
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) //判断文件描述符是否被置位
poll函数
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
设备驱动中的poll函数
unsigned int(*poll)(struct file * filp, struct poll_table* wait);
1)对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头部添加到poll_table中。
2)返回表示是否能对设备进行无阻塞读、写访问的掩码。
poll_wait
void poll_wait(struct file *filp, wait_queue_heat_t *queue, poll_table * wait);
作用是将当前进程添加到指定的等待列表中,以便在设备就绪时通知进程。