阻塞操作 : 是指在执行设备操作时,若不能获得资源,则挂起进程直到满足操作条件后再进行操作。被挂起的进程进入休眠, 被从调度器移走,直到条件满足。
非阻塞操作 :在不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直到可以进行操作。非阻塞应用程序通常使用select系统调用查询是否可以对设备进行无阻塞的访问最终会引发设备驱动中 poll函数执行。
阻塞地读取串口一个字符:
char buf;
fd = open("/dev/ttys",O_RDWR);
.. ..
res = read(fd,&buf,1); //当串口上有输入时才返回
if(res == 1)
{
printf("%c\n",buf);
}
非阻塞地读取串口一个字符:
char buf;
fd = open("/dev/ttys",O_RDWR | O_NONBLOCK);
.. ..
while( read(fd,&buf,1) !=1); //当串口上无输入也返回,所以要循环尝试读取串口
printf("%c\n",buf);
在Linux设备驱动中,可以使用等待队 列(wait queue)来实现阻塞进程的唤醒.等待队列能够用于实现内核中的异步事件通知机制。
Linux提供了有关等待队列的操作:
①: wait_queue_head_t my_queue; //定义等待队列头
wait_queue_head_t是wait_queue_head 结构体中的一个typedef
②: init_waitqueue_head(&my_queue); //初始化队列头
如果觉得上边两步来的麻烦,可以直接使用“快捷方式”:
DECLARE_WAIT_QUEUE_HEAD(name)
③:DECLARE_WAITQUEUE(name,tsk); //定义等待队列
④: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添加到等待队列头指向的等待队列链表中 。
⑤:
wait_event(queue, conditon);//不能被信号打断
wait_event_interruptible(queue, condition);//可以被信号打断
wait_event_timeout(queue, condition, timeout);
wait_event_interruptible_timeout(queue, condition, timeout);
wait_event()和wait_event_interruptible()的区别在于后者可以被信号打断,前者不能
queue:作为等待队列头的等待队列被唤醒
conditon:必须满足,否则阻塞
timeout和conditon相比,有更高优先级,就是在第三个参数的timeout到达时,无论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);
sleep_on作用是把目前进程的状态置成TASK_UNINTERRUPTIBLE,并定义一个等待队列,之后把他挂到等待队列头q指向的双向链表,直到资源可用,q引导的等待队列被唤醒。
interruptible_sleep_on作用是一样的, 只不过它把进程状态置为TASK_INTERRUPTIBLE,并定义一个等待队列,之后把他挂到等待队列头q指向的队列,直到资源可获得(q指引的等待队列被唤醒),或者进程收到信号。
sleep_on()和wake_up()成对使用;interruptible_sleep_on与wake_up_interruptible()成对使用;
这两个函数的流程是首先,定义并初始化等待队列,把进程的状态置成TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE,并将对待队列添加到等待队列头。
然后通过schedule(放弃CPU,调度其他进程执行。最后,当进程被其他地方唤醒,将等待队列移除等待队列头。
在Linux内核中,使用set_current_state()和__add_wait_queue()函数来实现目前进程状态的改变,直接使用current->state = TASK_UNINTERRUPTIBLE
类似的语句也是可以的。
因此我们有时也可能在许多驱动中看到,它并不调用sleep_on或interruptible_sleep_on(),而是亲自进行进程的状态改变和切换。