Linux内核工作队列
本文最后更新于:2023年2月8日 晚上
快速上手
内核工作队列使用方法:
struct work_struct my_work;
struct delayed_work my_delay_work;
static void my_work_func(struct work_struct *work)
{
// do something
}
static void my_delay_work_func(struct work_struct *work)
{
// do something
}
// 绑定工作函数
INIT_WORK(&my_work, my_work_func);
INIT_DELAYED_WORK(&my_delay_work, my_delay_work_func);
schedule_work(&my_work); // 立即加入到工作队列中
schedule_delayed_work(&my_delay_work, 1000); // 延时工作
以上是使用工作队列最简单的方式,仔细想想其实上述步骤并没有队列的创建过程,因为 schedule_work()
和 schedule_delayed_work()
是封装过的,使用这两个接口会把 work 加入到系统预定义的 system_wq
工作队列中。
这很方便,但难免会有些问题需要衡量,系统中各个模块都有可能将各种 work 加入到 system_wq
工作队列,如果前面的 work 执行时间过长就会影响后面 work 的实时性。因此往往我们的某些工作比较频繁且耗时,就需要创建一个独立的工作队列,避免别的模块影响到我们,同时也避免我们影响到别的模块。
以下代码展示了基本的独立工作队列创建方法:
struct work_struct my_work;
struct delayed_work my_delay_work;
static void my_work_func(struct work_struct *work)
{
// do something
}
static void my_delay_work_func(struct work_struct *work)
{
// do something
}
// 创建一个自己的工作队列,独立的内核线程
struct workqueue_struct *my_wq = alloc_ordered_workqueue("my_wq", 0);
// 绑定工作函数
INIT_WORK(&my_work, my_work_func);
INIT_DELAYED_WORK(&my_delay_work, my_delay_work_func);
// 将指定work加入到自定义工作队列my_wq中
queue_work(my_wq, &my_work);
queue_delayed_work(my_wq, &my_delay_work, 1000);
相关源码分析
相关内核源码:https://elixir.bootlin.com/linux/latest/source/include/linux/workqueue.h
创建工作队列/内核线程的接口还有多个:
#define alloc_ordered_workqueue(fmt, flags, args...) \
alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | \
__WQ_ORDERED_EXPLICIT | (flags), 1, ##args)
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
#define create_freezable_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \
WQ_MEM_RECLAIM, 1, (name))
#define create_singlethread_workqueue(name) \
alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)
内核预定义工作队列:
/*
* System-wide workqueues which are always present.
*
* system_wq is the one used by schedule[_delayed]_work[_on]().
* Multi-CPU multi-threaded. There are users which expect relatively
* short queue flush time. Don't queue works which can run for too
* long.
*
* system_highpri_wq is similar to system_wq but for work items which
* require WQ_HIGHPRI.
*
* system_long_wq is similar to system_wq but may host long running
* works. Queue flushing might take relatively long.
*
* system_unbound_wq is unbound workqueue. Workers are not bound to
* any specific CPU, not concurrency managed, and all queued works are
* executed immediately as long as max_active limit is not reached and
* resources are available.
*
* system_freezable_wq is equivalent to system_wq except that it's
* freezable.
*
* *_power_efficient_wq are inclined towards saving power and converted
* into WQ_UNBOUND variants if 'wq_power_efficient' is enabled; otherwise,
* they are same as their non-power-efficient counterparts - e.g.
* system_power_efficient_wq is identical to system_wq if
* 'wq_power_efficient' is disabled. See WQ_POWER_EFFICIENT for more info.
*/
extern struct workqueue_struct *system_wq;
extern struct workqueue_struct *system_highpri_wq;
extern struct workqueue_struct *system_long_wq;
extern struct workqueue_struct *system_unbound_wq;
extern struct workqueue_struct *system_freezable_wq;
extern struct workqueue_struct *system_power_efficient_wq;
extern struct workqueue_struct *system_freezable_power_efficient_wq;
schedule_work
与 queue_work
,schedule_delayed_work
与 queue_delayed_work
:
/**
* queue_work - queue work on a workqueue
* @wq: workqueue to use
* @work: work to queue
*
* Returns %false if @work was already on a queue, %true otherwise.
*
* We queue the work to the CPU on which it was submitted, but if the CPU dies
* it can be processed by another CPU.
*
* Memory-ordering properties: If it returns %true, guarantees that all stores
* preceding the call to queue_work() in the program order will be visible from
* the CPU which will execute @work by the time such work executes, e.g.,
*
* { x is initially 0 }
*
* CPU0 CPU1
*
* WRITE_ONCE(x, 1); [ @work is being executed ]
* r0 = queue_work(wq, work); r1 = READ_ONCE(x);
*
* Forbids: r0 == true && r1 == 0
*/
static inline bool queue_work(struct workqueue_struct *wq,
struct work_struct *work)
{
return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}
/**
* queue_delayed_work - queue work on a workqueue after delay
* @wq: workqueue to use
* @dwork: delayable work to queue
* @delay: number of jiffies to wait before queueing
*
* Equivalent to queue_delayed_work_on() but tries to use the local CPU.
*/
static inline bool queue_delayed_work(struct workqueue_struct *wq,
struct delayed_work *dwork,
unsigned long delay)
{
return queue_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay);
}
/**
* schedule_work - put work task in global workqueue
* @work: job to be done
*
* Returns %false if @work was already on the kernel-global workqueue and
* %true otherwise.
*
* This puts a job in the kernel-global workqueue if it was not already
* queued and leaves it in the same position on the kernel-global
* workqueue otherwise.
*
* Shares the same memory-ordering properties of queue_work(), cf. the
* DocBook header of queue_work().
*/
static inline bool schedule_work(struct work_struct *work)
{
return queue_work(system_wq, work);
}
/**
* schedule_delayed_work - put work task in global workqueue after delay
* @dwork: job to be done
* @delay: number of jiffies to wait or 0 for immediate execution
*
* After waiting for a given time this puts a job in the kernel-global
* workqueue.
*/
static inline bool schedule_delayed_work(struct delayed_work *dwork,
unsigned long delay)
{
return queue_delayed_work(system_wq, dwork, delay);
}
Linux内核工作队列
https://mxy493.xyz/202301028721/