• Linux中的工作队列(转载)


    转载自《Linux中的工作队列》

    http://blog.chinaunix.net/space.php?uid=487105&do=blog&cuid=971040

    [前记]Linux自从2.6.20之后,工作队列发生了一些变化,目前从网络上搜索的资料一般都是介绍老版本的工作队列,很少见到对新版本的介绍。本文对新老版本都做了简要概述,并分别提供了简单的实作案例。


    *******************************************************************************************************************

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

    工作队列是2.6内核开始引入的机制,在2.6.20之后,工作队列的数据结构发生了一些变化,因此本文分成两个部分对2.6.20之前和之后的版本分别做介绍。
    struct work_struct {
    /*记录工作是否已经挂在队列上*/
    unsigned
    long pending;
    /*循环链表结构*/
    struct list_head entry;
    /*func作为函数指针,由用户实现*/
    void (*func)(void *);
    /*data用来存储用户的私人数据,此数据即是func的参数*/
    void *data;
    /*wq_data一般用来指向工作者线程(工作者线程参考下文)*/
    void *wq_data;
    /*是推后执行的定时器*/
    struct timer_list timer;
    };

    API函数简介

    1)
    /*初始化指定工作,目的是把用户指定的函数_func及_func需要的参数_data赋给work_struct的func及data变量。*/
    INIT_WORK(_work, _func, _data)

    2)
    /*对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。
    工作者线程本质上是一个普通的内核线程,在默认情况下,每个CPU均有一个类型
    为“events”的工作者线程,当调用schedule_work时,这个工作者线程会被唤醒
    去执行工作链表上的所有工作。
    */

    int schedule_work(struct work_struct *work)

    3)
    /*延迟执行工作,与schedule_work类似。*/
    int schedule_delayed_work(struct work_struct *work, unsigned long delay)

    4)
    /*刷新缺省工作队列。此函数会一直等待,直到队列中的所有工作都被执行。*/
    void flush_scheduled_work(void)

    5)
    /*flush_scheduled_work并不取消任何延迟执行的工作,
    因此,如果要取消延迟工作,应该调用cancel_delayed_work。
    */
    int cancel_delayed_work(struct work_struct *work)

    以上均是采用缺省工作者线程来实现工作队列,其优点是简单易用,缺点是如果缺省工作队列负载太重,执行效率会很低,这就需要我们创建自己的工作者线程和工作队列。

    1)
    /**创建新的工作队列和相应的工作者线程,name用于该内核线程的命名。*/
    struct workqueue_struct *create_workqueue(const char *name)

    2)
    /**类似于schedule_work,区别在于queue_work把给定工作提交给创建的工作队列wq
    而不是缺省队列。
    */
    int queue_work(struct workqueue_struct *wq, struct work_struct *work)

    3)
    /**延迟执行工作。*/
    int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay)

    4)
    /**刷新指定工作队列。*/
    void flush_workqueue(struct workqueue_struct *wq)

    5)
    /*释放创建的工作队列。*/
    void destroy_workqueue(struct workqueue_struct *wq)

    下面一段代码可以看作一个简单的实作:

    void my_func(void *data)
    {

    char *name = (char *)data;

    printk(KERN_INFO “Hello world, my name
    is %s!\n”, name);

    }

    struct workqueue_struct *my_wq = create_workqueue(“my wq”);

    struct work_struct my_work;

    INIT_WORK(
    &my_work, my_func, “Jack”);

    queue_work(my_wq,
    &my_work);

    destroy_workqueue(my_wq);

    2、2.6.20~2.6.??

    自2.6.20起,工作队列的数据结构发生了一些变化,使用时不能沿用旧的方法。

    数据结构:
    typedef void (*work_func_t)(struct work_struct *work);
    struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;
    };
    与2.6.19之前的版本相比,work_struct瘦身不少。粗粗一看,entry和之前的版本相同,func和data发生了变化,另外并无其他的变量。

    entry我们不去过问,这个和以前的版本完全相同。data的类型是atomic_long_t,这个类型从字面上看可以知道是一个原子类型。第一次看到这个变量时,很容易误认为和以前的data是同样的用法,只不过类型变了而已,其实不然,这里的data是之前版本的pending和wq_data的复合体,起到了以前的pending和wq_data的作用。

    func的参数是一个work_struct指针,指向的数据就是定义func的work_struct。

    看到这里,会有两个疑问,第一,如何把用户的数据作为参数传递给func呢?以前有void *data来作为参数,现在好像完全没有办法做到;第二,如何实现延迟工作?目前版本的work_struct并没有定义timer。

    解决第一个问题,需要换一种思路。2.6.20版本之后使用工作队列需要把work_struct定义在用户的数据结构中,然后通过container_of来得到用户数据。具体用法可以参考稍后的实作。

    对于第二个问题,新的工作队列把timer拿掉的用意是使得work_struct更加单纯。首先回忆一下之前版本,只有在需要延迟执行工作时才会用到timer,普通情况下timer是没有意义的,所以之前的做法在一定程度上有些浪费资源。所以新版本中,将timer从work_struct中拿掉,然后又定义了一个新的结构delayed_work用于处理延迟执行:
    struct delayed_work {
    struct work_struct work;
    struct timer_list timer;
    };

    下面把API罗列一下,每个函数的解释可参考之前版本的介绍或者之后的实作:

    1) INIT_WORK(struct work_struct *work, work_func_t func)

    2) INIT_DELAYED_WORK(struct delayed_work *work, work_func_t func)

    3) int schedule_work(struct work_struct *work)

    4) int schedule_delayed_work(struct delayed_work *work, unsigned long delay)

    5) struct workqueue_struct *create_workqueue(const char *name)

    6) int queue_work(struct workqueue_struct *wq, struct work_struct *work)

    7) int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work,
                   unsigned
    long delay)

    8) void flush_scheduled_work(void)

    9) void flush_workqueue(struct workqueue_struct *wq)

    10) int cancel_delayed_work(struct delayed_work *work)

    11) void destroy_workqueue(struct workqueue_struct *wq)

    其中,1), 2), 4) ,7)和以前略有区别,其他用法完全一样。

    参考过程代码

    struct my_struct_t {

    char *name;

    struct work_struct my_work;
    };


    void my_func(struct work_struct *work)
    {

    struct my_struct_t *my_name = container_of(work, struct my_struct_t, my_work);

    printk(KERN_INFO “Hello world, my name
    is %s!\n”, my_name->name);

    }


    struct workqueue_struct *my_wq = create_workqueue(“my wq”);

    struct my_struct_t my_name;

    my_name.name
    = “Jack”;


    INIT_WORK(
    &(my_name.my_work), my_func);

    queue_work(my_wq,
    &(my_name.my_work));

    destroy_workqueue(my_wq);
  • 相关阅读:
    Codeforces Round #352 (Div. 1) B. Robin Hood 二分
    Codeforces Round #352 (Div. 1) A. Recycling Bottles 暴力
    Codeforces Round #352 (Div. 2) B. Different is Good 水题
    Codeforces Round #352 (Div. 2) A. Summer Camp 水题
    Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition) D. Bear and Two Paths 构造
    Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition) C. Bear and Colors 暴力
    Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition) B. Problems for Round 水题
    Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition) A. Bear and Game 水题
    Codeforces Beta Round #97 (Div. 1) C. Zero-One 数学
    Codeforces Beta Round #97 (Div. 1) B. Rectangle and Square 暴力
  • 原文地址:https://www.cnblogs.com/westfly/p/2006786.html
Copyright © 2020-2023  润新知