Chinaunix首页 | 论坛 | 博客
  • 博客访问: 31760
  • 博文数量: 12
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 130
  • 用 户 组: 普通用户
  • 注册时间: 2016-10-11 20:53
文章分类

全部博文(12)

文章存档

2017年(2)

2016年(10)

我的朋友

分类: 嵌入式

2016-11-02 21:36:53


1 .工作队列概述
工作队列(work queue)是另外一种将工作推后执行的形式,它和我们前面讨论的所有其他形式都不相同。工作队列可以把工作推后,交由一个内核线程去执行—这个下半部分总是会在进程上下文执行,但由于是内核线程,其不能访问用户空间。最重要特点的就是工作队列允许重新调度甚至是睡眠。

通常,在工作队列和软中断/tasklet中作出选择非常容易。可使用以下规则:

?      如果推后执行的任务需要睡眠,那么只能选择工作队列;

?      如果推后执行的任务需要延时指定的时间再触发,那么使用工作队列,因为其可以利用timer延时;

?      如果推后执行的任务需要在一个tick之内处理,则使用软中断或tasklet,因为其可以抢占普通进程和内核线程;

?      如果推后执行的任务对延迟的时间没有任何要求,则使用工作队列,此时通常为无关紧要的任务。

另外如果你需要用一个可以重新调度的实体来执行你的下半部处理,你应该使用工作队列。它是惟一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在你需要获得大量的内存时、在你需要获取信号量时,在你需要执行阻塞式的I/O操作时,它都会非常有用。

实际上,工作队列的本质就是将工作交给内核线程处理,因此其可以用内核线程替换。但是内核线程的创建和销毁对编程者的要求较高,而工作队列实现了内核线程的封装,不易出错,所以我们也推荐使用工作队列。


工作队列的使用:


1.  创建

2.  提交队列

3.  删除


1.自定义队列
创建:

  创建分为工作队列的创建和工作函数(任务)的创建.

  (1)  工作队列的创建需要有其描述符,它的数据结构是 workqueue_struct.该结构定义在中.这里我们不需要关心它的具体组成,内核已经写好了两个两个函数帮我们创建队列:

      struct workqueue_struct *create_workqueue(const char *name);

      struct workqueue_struct * workqueue_singlethread_workqueue(const char *name);

      他们的区别在于实际处理器的多少,如果是单核处理器的话,他们毫无区别.

      因为每个工作队列都有一个或多个(多核处理器)专用的进程(内核线程),这些进程运行提交到该工作队列函数.

      create_workqueue内核会在系统中的每个处理器上为该工作队列创建专用的线程.这样,如果工作队列足够多的话,可能对系统的性能有所杀伤,而create_singlethread_workqueue则只会创建一个专用的线程.所以,如果单个工作线程足够使用,推荐使用第二个函数来创建工作队列.

      同样的,工作任务的创建也需要有其描述符,它的数据结构是work_struct.内核同样为我们创建好了几个宏来方便的创建它.

        DECLARE_WORK(name,void(*function)(void *),void *data);   静态定义的一个工作

          用于在内核编译时使用.

        INIT_WORK(struct work_struct *work,void(*function)(void *),void *data);   动态定义一个工作

          用于在系统运行时创建.首次创建时使用它.

        PREPARE_WORK(struct work_struct *work,void(*function)(void *),void *data);

          用于在系统运行时创建.没有INIT_WORK初始化彻底,因为它不会初始化用来将work_struct结构连接到工作队列的指针.如果结构已经被提交到工作队列,而只是需要修改该结构,则应该使用PREPARE_WORK而不是INIT_WORK.

提交:

   如果要将工作提交到工作队列,则可使用如下两个函数之一:

    int queue_work(struct workqueue_struct *queue,struct work_struct *work);

    int queue_delayed_work(struct workqueue_struct *queue,struct work_struct *work,unsigned long delay);
                 
flush_workqueue(struct workqueue_struct *wq)    刷新等待指定队列中的所有工作完成

    它们都会将work添加到给定的queue.但是如果使用queue_delayed_work,则实际工作至少会在经过指定的jiffies(由delay指定)之后才会执行.如果工作被成功添加到队列,则上述函数的返回值为1,返回值为非零意味着给定的work_struct结构已经等待在该队列中,从而不能两次加入该队列.

删除:

   结束对工作队列的使用后,可调用下面的函数释放相关资源.

    void destroy_workqueue(struct workqueue_struct *queue);

2.内核提供的共享队列
创建:
    INIT_WORK(struct work_struct *work,void(*function)(void *),void *data);
提交:
    int schedule_work(struct work_struct *work)
    {
         return queue_work(keventd_wq, work);
    }

    int schedule_delayed_work(struct delayed_struct *work, unsigned long delay);
    flush_scheduled_work(void)   刷新等待keventd_wq中的所有工作完成
删除:   
    int cancel_delayed_work(struct delayed_struct *work);

简单实例:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>

static struct workqueue_struct *queue = NULL;
static struct work_struct work;

static void work_handler(struct work_struct *data)
{
        printk(KERN_ALERT “work handler function.\n”);
}

static int __init test_init(void)
{
        queue = create_singlethread_workqueue(“helloworld”); /*创建一个单线程的工作队列*/
        if (!queue)
                goto err;

        INIT_WORK(&work, work_handler);
        queue_work(queue, &work);

        return 0;
err:
        return -1;
}

static void __exit test_exit(void)
{
        destroy_workqueue(queue);
}
MODULE_LICENSE(“GPL”);
module_init(test_init);
module_exit(test_exit);










阅读(1171) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~