1.阻塞IO:
在linux驱动中,可使用等待队列实现阻塞I/O。信号量在内核中就是依赖等待队列来实现的。
等待队列:
//定义初始化‘等待队列头’
Wait_queue_head_t my_queue;
Init_wait_queue_head(&my_queue);
DECLARE_WAIT_QUEUE_HEAD (name);
//定义并初始化一个等待队列
DECLARE_WAITQUEUE(name, tsk);
//将等待队列添加到等待队列头Q指向的等待队列链表中
Void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
//将等待队列从等待队列头q指向的等待队列链表中移除
Void fastcall remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
//等待事件,queue为等待队列头,timeout以jiffy为单位
Wait_event(queue, condition);
Wait_event_interruptible(queue, condition);
Wait_event_timeout(queue, condition, timeout);
Wait_event_interruptible_timeout(queue, condition, timeout);
//唤醒队列
Void wake_up(wait_queue_head_t *q);
Void wake_up_interruptible (wait_queu_head_t *q);
注意:wake_up可唤醒wait_event或者wait_event_interruptible,但是 wake_up_interruptible只能唤醒wait_event_interruptible。
//在等待队列上睡眠
Sleep_on(wait_queue_head_t *q);
Interruptible_sleep_on(wait_queue_head_t *q);
注意:sleep_on或者interruptible_sleep_on函数的作用就是将目前进程的状态设置成TASK_UNINTERRUPTIBLE或者INTERRUPTIBLE。并定义一个等待队列,之后把它附属到等待队列头q,直到资源可获得,q引导的队列别唤醒或者接到信号。Sleep_on函数应该与wake_up成对使用,interruptible_sleep_on应该与wake_up_interruptble 成对使用。
set_current_state()(设置当前进程的运行状态)和signal_pending();current代表当前进程。
166页的例子。
需要特别注意的一点:当等待队列和信号量同时出现时,一定要注意是否会发生死锁。当进去等待时,看是否还持有别的进程需要的信号量。切记、切记。
.
2.轮询操作:
用户空间会调用select和poll对文件描述符进行监控。Select()和poll()系统调用最终会引发设备驱动中的poll()函数被执行。设备驱动中poll()函数的原型是:
Unsigned int (*poll)(struct file *filep, struct poll_table *wait);
(1)对可能引起设备文件状态变化的等待队列头调用poll_wait()函数将对应的等待队列头添加到poll_table;
(2)返回表示是否能对设备进行无阻塞读、写访问的掩码。
用于向poll_table注册等待队列的poll_wait()函数的原型如下:
Void poll_wait(struct file *filep, wait_queue_head_t *queue, poll_table *wait);
173页例子
总结:设备驱动中阻塞IO一般基于等待队列来实现。非阻塞IO会调用驱动中提供的poll函数。设备驱动中的poll()本身不会阻塞,但是select()和poll()系统调用则会等待文件描述符集合中的至少一个可访问或超时。