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

来源:互联网 发布:sql建立学生表 编辑:程序博客网 时间:2024/06/05 15:33

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

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

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


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


总结:

工作队列的使用又分两种情况一种是利用系统共享的工作队列来添加自己的工作,这种情况处理函数不能消耗太多时间,这样会影响共享队列中其他任务的处理,调用使用(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


0 0
原创粉丝点击