工作队列(系统工作队列和自定义工作队列使用区别) tasklet

中断底半部机制有三种:1.工作队列 2.tasklet 3.软中断

注:软中断和tasklet运行于软中断上下文,仍然属于原子上下文的一种,而工作队列则运行于进程上下文,因此,软中断和taeklet处理函数中不能睡眠

而工作队列处理函数中允许睡眠。


工作队列: (work queue) Linux kernel 中将 工作推后执行的一种机制 。这种机制和 BH Tasklets 不同之处在于工作队列是把推后的工作交由一个 内核线程去执行 ,因此工作队列的优势就在于它允许重新调度甚至睡眠。


总结:

工作队列的使用又分两种情况 一种是利用系统共享的工作队列来添加自己的工作,这种情况处理函数不能消耗太多时间,这样会影响共享队列中其他任务的处理,调用使用(schedule_work or schedule_delayed_work);
另外一种是创建自己的工作队列并添加工作,调用使用(queue_work  or  queue_delayed_work)。


第一种:利用系统共享的工作队列,自己创建节点并添加到系统共享工作队列

(对于多cpu而言,会在所有cpu上创建线程,执行挂在工作队列的工作节点上的函数。)

        

        第一步:声明或编写一个工作处理函数

  void my_func();

       第二 步:创建一个工作结构体变量,并将处理函数和参数的入口地址赋给这个工作结构体变量

          法一:DECLARE_WORK(my_work,my_func,&data); //编译时创建名为my_work的结构体变量并把函数入口地址和参数地址赋给它;   如果不想要在编译时就用DECLARE_WORK()创建并初始化工作结构体变量,也可以在程序运行时再用INIT_WORK()创建

               法二:struct work_struct my_work; //创建一个名为my_work的结构体变量,创建后才能使用INIT_WORK()

                        或struct  delayed_work  my_work;//创建后用INIT_DELAYED_WORK()

  INIT_WORK(&my_work,my_func,&data); //初始化已经创建的my_work,其实就是往这个结构体变量中添加处理函数的入口地址和data的地址,或INIT_DELAYED_WORK();

       第三步 将工作结构体变量添加入系统的共享工作队列

schedule_work(&my_work); //添加入队列的工作完成后会自动从队列中删除

  或schedule_delayed_work(&my_work,tick); //延时tick个滴答后再提交工作

第二种:自己创建工作队列,自己创建工作节点并添加到自己创建的工作队列当中

对于多cpu而言,会在调用执行挂在工作队列的工作节点上的函数上的那个cpu上,创建线程,函数如下:

    需要对自己创建的工作队列开个线程:workqueue = create_singlethread_workqueue("dsx_exp_workqueue");

    

    第一步:声明工作处理函数和一个指向工作队列的指针

  void my_func();

  struct workqueue_struct *p_queue;

 第二步:创建自己的工作队列和工作节点

  (1).p_queue=create_workqueue("my_queue"); //创建一个名为my_queue的工作队列并把工作队列的入口地址赋给声明的指针

或:p_queue = create_singlethread_workqueue("dsx_exp_workqueue");//哪个cpu调用工作处理函数,为该cpu创建处理线程


  (2).struct work_struct my_work;//创建工作队列上的一个工作节点

                或struct  delayed_work  my_work;//创建后用INIT_DELAYED_WORK()初始化


  (3).INIT_WORK(&my_work, my_func, &data); //创建一个工作结构体变量并初始化,或INIT_DELAYED_WORK();

  第三步:将工作节点添加入自己创建的工作队列等待执行

  queue_work(p_queue, &my_work); //将自定义工作节点加入自定义工作队列,并调用线程执行。

  //作用与schedule_work()类似,不同的是将工作添加入p_queue指针指向的工作队列而不是系统共享的工作队列

 第四步:删除自己的工作队列

  destroy_workqueue(p_queue); //一般是在close函数中删除

示例:



于是就让我们来仔细看看INIT_WORKINIT_DELAYED_WORK.其实前者是后者的一个特例,它们涉及到的就是传说中的工作队列

如果我们将来某个时刻希望能够调用led_work()这么一个我们自己写的函数,那么我们所要做的就是利用工作队列.如何利用呢?第一步就是使用INIT_WORK()或者INIT_DELAYED_WORK()来初始化这么一个工作,或者叫任务,初始化了之后,将来如果咱们希望调用这个led_work()函数,那么咱们只要用一句schedule_work()或者schedule_delayed_work()就可以了,特别的,咱们这里使用的是INIT_DELAYED_WORK(),那么之后我们就会调用schedule_delayed_work(),这俩是一对.它表示,您希望经过一段延时然后再执行某个函数,所以,咱们今后会见到schedule_delayed_work()这个函数的,而它所需要的参数,一个就是咱们这里的&hub->leds,另一个就是具体自己需要的延时

INIT_WORK () 
INIT_DELAYED_WORK()

INIT_DELAYED_WORK()schedule_delayed_work(),INIT_WORK()schedule_work().



,它们是cancel_delayed_work(struct delayed_work *work)flush_scheduled_work().其中cancel_delayed_work()的意思不言自明,对一个延迟执行的工作来说,这个函数的作用是在这个工作还未执行的时候就把它给取消掉.flush_scheduled_work()的作用,是为了防止有竞争条件的出现,虽说哥们儿也不是很清楚如何防止竞争,可是好歹大二那年学过一门专业课,数字电子线路,尽管没学到什么有用的东西,怎么说也还是记住了两个专业名词,竞争与冒险.您要是对竞争条件不是很明白,那也不要紧,反正基本上每次cancel_delayed_work之后您都得调用flush_scheduled_work()这个函数,特别是对于内核模块,


中断上下文:

1.新建等待队列头和flag标志 

2.在进程中执行:wait_event_interruptible(waiter, tpd_flag != 0);

目的:将本进程置为可中断的挂起状态条件满足后,即把本进程状态置为运行态(此时如果不执行下面的函数 wake_up_interruptible,wait_event_interruptible还会使进程继续休眠)。

3.在中断中执行:tpd_flag = 1;

    wake_up_interruptible(&waiter);
目的:中断产生后,标志满足,wake_up唤醒执行waiter里的进程,


4解释案列 :


Linux使用wake_up_interruptible()唤醒注册到等待队列上的进程

(2013-02-18 22:24:53)
转载
标签:

linux

嵌入式

it


功能:唤醒注册到等待队列上的进程

原型
    #include
    void wake_up_interruptible (wait_queue_head_t  *q);

说明
    唤醒 q 指定的注册在等待队列上的进程。该函数不能直接的立即唤醒进程,而是由调度程序转换上下文,调整为可运行状态。

变量
q :  等待队列变量指针。

 
    最近在学习驱动时有一个问题始终想不明白,为什么wait_event_interruptible(button_waitq, ev_press)需要一个全局变量来记住中断的发生?
在驱动中实现读取操作时,使用了
  1. wait_event_interruptible(button_waitq, ev_press);
在中断函数中使用了如下语句唤醒:
  1. ev_press = 1;  //表示中断发生了
  2. wake_up_interruptible(&button_waitq);  //唤醒休眠的进程
这样的话,中断能正确读取到。我分别尝试了屏蔽ev_press = 1;和wake_up_interruptible(&button_waitq);代码,发现中断不能正常产生。

查找资料,阅读源代码。
  1. #define wait_event_interruptible(wq, condition)          \
  2. ({                                                       \
  3.     int __ret = 0;                                       \
  4.     if (!(condition))                                    \
  5.         __wait_event_interruptible(wq, condition, __ret);\
  6.     __ret;                                               \
  7. })
  1. #define __wait_event_interruptible(wq, condition, ret)                        \
  2. do {                                                                        \
  3.         DEFINE_WAIT(__wait);                                                \
  4.                                                                         \
  5.         for (;;) {                                                        \
  6.                 prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);        \
  7.                 if (condition)                                                \
  8.                         break;                                                \
  9.                 if (!signal_pending(current)) {                                \
  10.                         schedule();                                        \
  11.                         continue;                                        \
  12.                 }                                                        \
  13.                 ret = -ERESTARTSYS;                                        \
  14.                 break;                                                        \
  15.         }                                                                \
  16.         finish_wait(&wq, &__wait);                                        \
  17. } while (0)
__wait_event_interruptible()首先定义并初始化一个wait_queue_t变量__wait,其中数据为当前进程current,并把__wait入队。
    在无限循环中,__wait_event_interruptible()将本进程置为可中断的挂起状态,反复检查condition是否成立,如果成立则退出,如果不成立则继续休眠;条件满足后,即把本进程运行状态置为 运行态( 此时如果不执行下面的函数  wake_up_interruptible, 上面 wait_event_interruptible 还会继续休眠 ) ,并将__wait从等待队列中清除掉,从而进程能够调度运行。如果进程当前有异步信号(POSIX的),则返回-ERESTARTSYS。
  1. //唤醒 q 指定的注册在等待队列上的进程。该函数不能直接的立即唤醒进程,而是由调度程序转换上下文,调整为可运行状态。
  2. wake_up_interruptible (wait_queue_head_t *q);
我是这样考虑的:read 调用wait_event_interruptible(button_waitq),我觉得一个参数就够了。不判断什么条件,直接休眠,下面用wake_up_interruptible(&button_waitq)直接唤醒。这样看起来很简单。
不知道内核这样设计是基于什么原因?
 
http://www.groad.net/bbs/read.php?tid-1336.html
http://www.100ask.net/forum/showtopic-4322.aspx


你可能感兴趣的:(工作队列(系统工作队列和自定义工作队列使用区别) tasklet)