等待队列
应用程序在中断发生之前应该有两种等待状态,一种是忙等待,另一种休眠等,也就是如果设备没有准备就绪,最好应该让进程进入休眠。休眠的概念就是将CPU的资源从当前进程上撤下来给别的任务去使用。只有驱动才能访问硬件设备,所以只有驱动才能判断设备到底可用不可用,如果驱动发生设备不可用,那么由设备驱动主动让进程休眠。
如何唤醒休眠的进程:唤醒的前提是设备可用了,通过中断判断设备可用,如果产生中断,预示着设备可用,就应该唤醒之前休眠的进程。
所以对于CPU要访问的外设,这类设备如果没有准备就绪,都是由设备驱动主动让进程进行休眠和唤醒。
内核利用等待队列机制实现进程的休眠和唤醒,实现的方法有两种:
等待队列头:wait_queue_head_t
等待队列(容器):wait_queue_t
方法1:
1.分配初始化等待队列头
wait_queue_head_twq;
init_waitqueue_head(&wq);
一般来说等待队列头只有一个,如果有读和写,分别定义两个读,写等待队列头。
2.分配初始化存放进程的容器
DECLARE_WAITQUEUE(wait,current);
或者
wait_queue_t wait;
init_waitqueue_entry(&wait, current);
分配一个容器wait,将当期进程添加到其中,current是内核的一个全局变量,代表当前进程。它对应的数据结构是struct task_struct(内核使用这个结构体来描述进程)。current指向当前进程的task_struct结构体。
例如,通过current能够查看进程的pid和名字
printk("processname = %s, process id = %d\n", current->comm,current->pid);
3.将当前进程添加到等待队列头所在的数据链中
add_wait_queue(&wq,&wait); //还没有真正的休眠
4.设置进程的休眠状态(不可中断,可中断)
current->state= TASK_INTERRUPTIBLE;
或者
set_current_state(TASK_INTERRUPTIBLE);
5.让进程进入真正的休眠状态(进程所使用的CPU资源全部释放)
schedule();//代码就会执行到停止
6.当设备可用或者进程接收到信号,就会唤醒休眠的进程,schedule引起的休眠,唤醒的方法有两种,一个驱动主动唤醒(设备可用)或者接收到信号
7.schedule()函数返回,代表进程被唤醒,所以根据需求判断是哪种原因引起的唤醒
if(signal_pending(current)) {
//当前接收到信号引起的唤醒,如果接收到信号此函数返回非0,否则返回0
return-ERESTARTSYS;
}else {
//驱动主动的唤醒(设备可用的唤醒),设备准备就绪
读取数据上报用户空间或者其他操作
}
8.设置当前进程的状态为运行
set_current_state(TAST_RUNNING);
9.将当前进程从队列头所在的数据链中移出
remove_wait_queue();
参考代码:
//1.在驱动的入口函数已经分配初始化好了一个读的等待队列头
wait_queue_head_trwq;
init_waitqueue_head(&rwq);
//2.每当读进程在读内存时,如果内存是空的,没有数据,驱动应该让进程进入休眠状态,实现过程如下:
mem_read(...){
wait_queue_t wait;
init_waitqueue_entry(&wait,current); //将当前进程放到wait容器中
add_wait_queue(&rwq,&wait);//将当前进程添加到等待队列头中去
set_current_state(TASK_INTERRUPTIBLE);//设置进程的睡眠状态为可中断
schedule();//真正的休眠,代码执行到这里就会停止
//判断是哪种原因引起的唤醒
if(signal_pending(current)) {
printk("接收到了信号引起的唤醒\n");
}else {
//内存非空,有数据引起的唤醒
}
//设置当前进程的状态为运行
set_current_state(TASK_RUNNING);
//移出
remove_wait_queue(&rwq,&wait);
//读取内存的数据上报给用户空间
}
方法2:内核将以上的通用步骤进行了再一次封装
1.分配初始化等待队列头
wait_queue_head_t rwq;
init_waitqueue_head(&rwq);
2.在设备驱动的某个地方,调用wait_event/wait_event_interruptible判断设备是否可用,如果不可用,进程进入休眠状态。
mem_read(...)
{
如果设备可用condition为真,此函数立即返回,不会休眠,如果condition为假,次函数不会立即返回,进程将进入休眠状态。
wait_event_interruptible(rwq,condition);
}
驱动由于设备可用主动唤醒的方法:
wake_up/wake_up_interruptible,唤醒休眠的进程,这个函数应该在驱动的中断处理函数中调用,唤醒休眠的进程。中断的触发本身代表设备的可用。