Aliens

Kernel 工作队列

May 25, 2021

内核版本: 4.15

调度一个 work,首先需要定义一个 workqueue,然后将这个 work 放入到这个 workqueue 中。 当我们希望调度一个 work 时,可以有两种方法:

  1. 内核会为每一个 cpu 创建一个默认的 workqueue,可以用 schedule_work 函数将 work 加入到默认的 workqueue 中;
  2. 当默认的 workqueue 任务太重时,为了加快响应,我们可以自定义一个 workqueue,然后使用 queue_work 函数将 work 加入到自定义的 workqueue 中。

使用自定义的 workqueue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
struct work_struct {
/*	atomic_long_t data; */
	unsigned long data;

	struct list_head entry;
	work_func_t func;
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

使用前进行必要的初始化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct lt9611 {
    struct workqueue_struct *wq;
    struct work_struct some_work;
    ...
};

static void lt9611_some_work(struct work_struct *work)
{
    struct lt9611 *pdata = container_of(work, struct lt9611, some_work) ;
    ...
}

static int lt9611_probe (struct i2c_client *client,
    const struct i2c_device_id *id)
{
    struct lt9611 *pdata;
...
    pdata->wq = create_singlethread_workqueue ("lt9611_wq") ;
    if (!pdata->wq) {
        pr_err ("lt9611: fail to create workqueue") ;
        goto err;
    }

    INIT_WORK(&pdata->some_work, lt9611_some_work) ;
...
}

使用 queue_work 调用需要的 work:

1
2
3
4
static void foo_func (struct lt9611 *pdata)
{
    queue_work(pdata->wq, &pdata->some_work) ;
}

使用 delayed workqueue

有时候,我们需要一个 work 在一段时间后才执行,这时候便可以使用 delayed work 。delayed workqueue 与普通的 workqueue 在使用上差别并不大, 从 delayed workqueue 的结构体定义就可以看出。

1
2
3
4
5
6
7
8
struct delayed_work {
	struct work_struct work;
	struct timer_list timer;

	/* target workqueue and CPU ->timer uses to queue ->work */
	struct workqueue_struct *wq;
	int cpu;
};

使用前进行必要的初始化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct lt9611 {
    struct workqueue_struct *wq;
    struct work_struct some_delayed_work;
    ...
};

static void lt9611_some_work ( struct work_struct *work )
{
    /* 注意这里的to_delayed_work(work) */
    struct lt9611 *pdata = container_of(to_delayed_work(work),
                        struct lt9611, some_delayed_work ) ;
    ...
}

static int lt9611_probe ( struct i2c_client *client,
    const struct i2c_device_id *id )
{
    struct lt9611 *pdata;
...
    pdata->wq = create_singlethread_workqueue ( "lt9611_wq" ) ;
    if ( !pdata->wq ) {
        pr_err ( "lt9611: fail to create workqueue" ) ;
        goto err;
    }

    INIT_DELAYED_WORK(&pdata->some_delayed_work, lt9611_some_delayed_work) ;
...
}

使用 queue_delayed_work 调用需要的 delayed_work :

1
2
3
4
5
6
static void bar_func (struct lt9611 *pdata)
{
    queue_delayed_work(pdata->wq, &pdata->some_delayed_work,
                            msecs_to_jiffies(300)) ;
    ...
}

和普通的 work 相比,除了初始化和调用的方式不同外,有一点需要格外注意:

work 和 delayed_work 对应的回调函数都是: void (*func)(struct work_struct *)delayed_work 回调函数的入参并不是 struct delayed_work *。 因此,在其回调函数中,需要两次调用 container_of 来获得包含 delayed_work 的结构体的地址。

参考

https://blog.csdn.net/heanyu/article/details/6899679