Linux驱动学习——等待队列

来源:互联网 发布:淘宝卖家帐号被冻结 编辑:程序博客网 时间:2024/05/21 17:55

等待队列

应用程序在中断发生之前应该有两种等待状态,一种是忙等待,另一种休眠等,也就是如果设备没有准备就绪,最好应该让进程进入休眠。休眠的概念就是将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,唤醒休眠的进程,这个函数应该在驱动的中断处理函数中调用,唤醒休眠的进程。中断的触发本身代表设备的可用。
0 0
原创粉丝点击