linux 内核工作延迟机制-工作队列

一、概述

作用:

内核中执行延迟执行或者异步执行的机制,运行在抢占上下文中,中断下半部分需要要睡眠,工作队列是唯一选择。
睡眠是指处理I/O数据、持有互斥锁、延迟,以及可能导致睡眠或将任务移出运行队列的所有其他任务,工作队列是内核中的一种机制,用于将需要延迟执行的任务排队,这些任务会在将来某个时刻由工作线程来执行。这对于不能在中断上下文中执行的任务或者不应该长时间阻塞的任务非常有用。

内核使用场景:

  • 设备驱动程序:在设备驱动程序中,有些操作可能需要花费较长的时间来完成,例如硬盘读写、网络数据传输等。这些操作如果在中断处理程序中直接执行,可能会导致中断处理时间过长,影响系统的实时性。因此,可以将这些操作放入工作队列中,由内核在适当的时候异步执行。

  • 文件系统:在文件系统中,有些操作可能需要在后台进行,例如数据的预读取、缓存的更新等。这些操作可以放入工作队列中,由内核在适当的时候异步执行。

  • 网络协议栈:在网络协议栈中,有些操作可能需要在后台进行,例如数据的重传、连接的维护等。这些操作可以放入工作队列中,由内核在适当的时候异步执行。

  • 内存管理:在内存管理中,有些操作可能需要在后台进行,例如页面的换出、缓存的清理等。这些操作可以放入工作队列中,由内核在适当的时候异步执行。

具体的例子:

  • 在 USB 驱动中,当设备插入或者拔出时,需要进行一些初始化或者清理操作,这些操作可以放入工作队列中,由内核在适当的时候异步执行。
  • 在网络协议栈中,当接收到一个数据包时,可能需要进行一些复杂的处理,例如路由查找、协议解析等,这些操作可以放入工作队列中,由内核在适当的时候异步执行。

二、关键内核函数

INIT_WORK

功能:初始化一个内核工作项。

参数:void INIT_WORK(struct work_struct *work, void (*func)(struct work_struct *work))

  • work: 指向要初始化的工作项(work item)的指针。
  • func: 函数指针,指向当前工作项被调度是应该执行的函数。
#define __INIT_WORK(_work, _func, _onstack)                             \
        do {                                                            \
                __init_work((_work), _onstack);                         \
                (_work)->data = (atomic_long_t) WORK_DATA_INIT();       \
                INIT_LIST_HEAD(&(_work)->entry);                        \
                (_work)->func = (_func);                                \
        } while (0)
#endif

#define INIT_WORK(_work, _func)                                         \
        __INIT_WORK((_work), (_func), 0)
alloc_workqueue

函数原型:struct workqueue_struct *alloc_workqueue(const char *fmt,unsigned int flags,int max_active, ...)

疑惑:工作队列是不是由一个内核线程单独处理?- - 由内核kworker来处理。

功能: 初始化一个工作队列。

参数:

  • fmt:工作队列名。
  • flags:掩码,用于指定工作队列的属性和行为,是一个枚举类型定义在include/linux/workqueue.h中。
  • max_active:指定了可以同时激活的最大任务数,如果设置为0,则会使用默认值。
  • ...:可变参部分用于传递fmt格式化字符串对应的参数。
destroy_workqueue

函数原型:void destroy_workqueue(struct workqueue_struct *wq)

功能:安全的终止一个工作队列,会等待所有正在处理事项完成。

参数:

  • wq:等待队列。
flush_workqueue

函数原型:void flush_workqueue(struct workqueue_struct *wq)

功能:确保任何需要被调度执行的工作都运行完成,这个函数会休眠,直到所有在进入排队的工作项都已完成执行,新添加的工作项不会活锁?

参数:

  • wq:等待队列。
/**
 * flush_workqueue - ensure that any scheduled work has run to completion.
 * @wq: workqueue to flush
 *
 * This function sleeps until all work items which were queued on entry
 * have finished execution, but it is not livelocked by new incoming ones.
 */
queue_work

函数原型:static inline bool queue_work(struct workqueue_struct *wq, struct work_struct *work)

功能:将工作项work添加到工作队列workqueue中,将工作项排队到提交它的CPU上,但是如果该CPU停止工作,那么这个工作项可以由另一个CPU处理。

参数:

  • wq 要使用的工作队列。
  • work 要排队的工作项。

返回值:

  • 如果work已经在队列中,则返回false。
  • 否则,返回true。
/**
 * 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.
 */
static inline bool queue_work(struct workqueue_struct *wq,
                              struct work_struct *work)
{
        return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}

三、内核使用示例

在内核md模块中有相关的使用示例。

static struct workqueue_struct *md_misc_wq;	// 定义了一个md杂项工作队列。

// mddev用来标识一个软raid设备,包含一个del_work的成员。
struct mddev {
    struct work_struct del_work;	/* used for delayed sysfs removal */
}

// 初始化工作队列。
md_misc_wq = alloc_workqueue("md_misc", 0, 0);

// 销毁工作队列。 
destroy_workqueue(md_misc_wq);

// 初始化del_work工作项,绑定调度执行时函数md_start_sync。
INIT_WORK(&mddev->del_work, md_start_sync);

// 将工作项添加进入md_misc_wq工作队列。
queue_work(md_misc_wq, &mddev->del_work);

你可能感兴趣的:(linux)