1、引言
1.1、工作队列的核心概念
工作队列(Workqueue)是 Linux 内核提供的一种异步执行机制,它允许内核开发者将任务推迟执行或分配到其他线程处理。其核心架构由三个关键组件构成:
- 工作项(Work):描述待执行任务的数据结构,包含要执行的函数及其参数
- 工作队列(Workqueue):用于存放工作项的队列容器
- 工作者(Worker):负责执行工作队列中任务的独立内核线程
1.2、工作机制与特点
工作队列的运行遵循以下机制:
- 当工作项被加入队列时,空闲的工作者线程会被唤醒
- 工作者按 FIFO 顺序依次执行队列中的工作项函数
- 队列为空时,工作者线程进入空闲状态(阻塞状态)
1.3、典型应用场景
工作队列特别适用于以下情况:
- 中断处理:将中断服务程序(上半部)中的复杂逻辑推迟到工作队列(下半部)执行
- 耗时操作:执行可能阻塞或需要调度的长耗时任务
这种机制有效平衡了系统响应性与任务复杂性,是 Linux 内核异步编程的重要基础设施。
2、SylixOS 下的内核工作队列
内核工作队列(JobQueue)位于内核文件 libsylixos/SylixOS/kernel/core/_JobQueue.c
中,是纯粹的针对工作队列对象的一些操作集合,供内核使用,系统工作队列和网络工作队列都是基于内核工作队列实现的。
内核工作队列本质上是基于数组实现的,同时使用了自旋锁进行过程保护和使用了信号量进行资源同步。实现过程比较简单,可以看源码直接分析。
2.1 创建工作队列
/*********************************************************************************************************
** 函数名称: _JobQueueCreate
** 功能描述: 创建一个工作队列
** 输 入 : uiQueueSize 队列大小
** bNonBlock 执行函数是否为非阻塞方式
** 输 出 : 工作队列控制块
** 全局变量:
** 调用模块:
*********************************************************************************************************/
PLW_JOB_QUEUE _jobQueueCreate (UINT uiQueueSize, BOOL bNonBlock)
{
PLW_JOB_QUEUE pjobq;
/* 初始化一个工作队列、分配内存空间 */
pjobq = (PLW_JOB_QUEUE)__KHEAP_ALLOC((size_t)(sizeof(LW_JOB_QUEUE) +
(uiQueueSize * sizeof(LW_JOB_MSG))));
if (pjobq == LW_NULL) {
_DebugHandle(__ERRORMESSAGE_LEVEL, "kernel low memory.\r\n");
_ErrorHandle(ERROR_KERNEL_LOW_MEMORY);
return (LW_NULL);
}
pjobq->JOBQ_pjobmsgQueue = (PLW_JOB_MSG)(pjobq + 1);
pjobq->JOBQ_uiIn = 0;
pjobq->JOBQ_uiOut = 0;
pjobq->JOBQ_uiCnt = 0;
pjobq->JOBQ_uiSize = uiQueueSize;
pjobq->JOBQ_ulLost = 0;
/* 这里的同步信号量,可以确保工作队列线程在空闲时,处于阻塞状态,不占用系统资源 */
if (bNonBlock == LW_FALSE) {
pjobq->JOBQ_ulSync = API_SemaphoreBCreate("job_sync", LW_FALSE, LW_OPTION_OBJECT_GLOBAL, LW_NULL);
if (pjobq->JOBQ_ulSync == LW_OBJECT_HANDLE_INVALID) {
__KHEAP_FREE(pjobq);
return (LW_NULL);
}
} else {
pjobq->JOBQ_ulSync = LW_OBJECT_HANDLE_INVALID;
}
LW_SPIN_INIT(&pjobq->JOBQ_slLock);
return (pjobq);
}
2.2 添加工作
/*********************************************************************************************************
** 函数名称: _JobQueueAdd
** 功能描述: 添加一个工作到工作队列
** 输 入 : pjobq 工作队列控制块
** pfunc 要执行的函数
** pvArg0 ~ 5 函数参数
** 输 出 : ERROR
** 全局变量:
** 调用模块:
*********************************************************************************************************/
ULONG _jobQueueAdd (PLW_JOB_QUEUE pjobq,
VOIDFUNCPTR pfunc,
PVOID pvArg0,
PVOID pvArg1,
PVOID pvArg2,
PVOID pvArg3,
PVOID pvArg4,
PVOID pvArg5)
{
INTREG iregInterLevel;
PLW_JOB_MSG pjobmsg;
LW_SPIN_LOCK_QUICK(&pjobq->JOBQ_slLock, &iregInterLevel);
/* 工作队列是有大小限制的。在队列初始化时,设置队列大小 */
if (pjobq->JOBQ_uiCnt == pjobq->JOBQ_uiSize) {
pjobq->JOBQ_ulLost++;
LW_SPIN_UNLOCK_QUICK(&pjobq->JOBQ_slLock, iregInterLevel);
_DebugHandle(__ERRORMESSAGE_LEVEL, "job message lost.\r\n");
return (ENOSPC);
}
/* 工作项,实际上就是一个函数 pfunc 和其入参 */
pjobmsg = &pjobq->JOBQ_pjobmsgQueue[pjobq->JOBQ_uiIn];
pjobmsg->JOBM_pfuncFunc = pfunc;
pjobmsg->JOBM_pvArg[0] = pvArg0;
pjobmsg->JOBM_pvArg[1] = pvArg1;
pjobmsg->JOBM_pvArg[2] = pvArg2;
pjobmsg->JOBM_pvArg[3] = pvArg3;
pjobmsg->JOBM_pvArg[4] = pvArg4;
pjobmsg->JOBM_pvArg[5] = pvArg5;
if (pjobq->JOBQ_uiIn == (pjobq->JOBQ_uiSize - 1)) {
pjobq->JOBQ_uiIn = 0;
} else {
pjobq->JOBQ_uiIn++;
}
pjobq->JOBQ_uiCnt++;
LW_SPIN_UNLOCK_QUICK(&pjobq->JOBQ_slLock, iregInterLevel);
/* 尝试唤醒工作队列(工作者) */
if (pjobq->JOBQ_ulSync) {
API_SemaphoreBPost(pjobq->JOBQ_ulSync);
}
return (ERROR_NONE);
}
2.3 执行工作
/*********************************************************************************************************
** 函数名称: _jobQueueExec
** 功能描述: 执行工作队列中的工作
** 输 入 : pjobq 工作队列控制块
** ulTimeout 等待超时时间
** 输 出 : 不为 ERROR_NONE 表示超时
** 全局变量:
** 调用模块:
*********************************************************************************************************/
ULONG _jobQueueExec (PLW_JOB_QUEUE pjobq, ULONG ulTimeout)
{
INTREG iregInterLevel;
PLW_JOB_MSG pjobmsg;
LW_JOB_MSG jobmsgRun;
if (pjobq->JOBQ_ulSync) {
for (;;) {
/* 这里是工作者阻塞的根源 */
if (API_SemaphoreBPend(pjobq->JOBQ_ulSync, ulTimeout)) {
return (ERROR_THREAD_WAIT_TIMEOUT);
}
LW_SPIN_LOCK_QUICK(&pjobq->JOBQ_slLock, &iregInterLevel);
if (pjobq->JOBQ_uiCnt) {
break;
}
LW_SPIN_UNLOCK_QUICK(&pjobq->JOBQ_slLock, iregInterLevel);
}
} else {
LW_SPIN_LOCK_QUICK(&pjobq->JOBQ_slLock, &iregInterLevel);
}
/* 从这里可以看到,工作者每被唤醒一次,会尝试把工作队列里面的所有工作项都完成 */
while (pjobq->JOBQ_uiCnt) {
pjobmsg = &pjobq->JOBQ_pjobmsgQueue[pjobq->JOBQ_uiOut];
LW_JOB_MSG_COPY(&jobmsgRun, pjobmsg);
if (pjobq->JOBQ_uiOut == (pjobq->JOBQ_uiSize - 1)) {
pjobq->JOBQ_uiOut = 0;
} else {
pjobq->JOBQ_uiOut++;
}
pjobq->JOBQ_uiCnt--;
LW_SPIN_UNLOCK_QUICK(&pjobq->JOBQ_slLock, iregInterLevel);
if (jobmsgRun.JOBM_pfuncFunc) {
jobmsgRun.JOBM_pfuncFunc(jobmsgRun.JOBM_pvArg[0],
jobmsgRun.JOBM_pvArg[1],
jobmsgRun.JOBM_pvArg[2],
jobmsgRun.JOBM_pvArg[3],
jobmsgRun.JOBM_pvArg[4],
jobmsgRun.JOBM_pvArg[5]);
}
LW_SPIN_LOCK_QUICK(&pjobq->JOBQ_slLock, &iregInterLevel);
}
LW_SPIN_UNLOCK_QUICK(&pjobq->JOBQ_slLock, iregInterLevel);
return (ERROR_NONE);
}
_jobQueueExec
通常会放到一个线程中。内核工作队列文件中并没有这个操作,通常由具体使用者去创建。
3、SylixOS 下的系统工作队列
系统工作队列(Work Queue)是 SylixOS 提供的系统级接口,供驱动和应用层直接调用。
系统工作队列底层实现其实分两部分,简单工作队列(simple work queue)和 延时工作队列(delay work queue)。
- 简单工作队列由 __wqS 打头的函数簇实现,本质是基于内核工作队列并结合了执行线程创建、同步保护等相关操作实现
- 延时工作队列由 __wqD 打头的函数簇实现,本质和内核工作队无关,是基于单向链表和等待唤醒链表实现的
系统工作队列相比于内核工作队列,不仅提供了执行线程,还提供了延时执行的功能,更便于被直接调用,是一种系统级调用接口。内核工作队列提供的则是更基础的调用方法,是一种半成品。对于一些不适合直接使用系统工作队列的地方,可以在内核工作队列的基础上构造自己独享的工作队列模块,如网络接收处理和系统中断下半部执行等都是基于内核工作队列单独实现的。
下面仅以简单工作队列为例讲解,延时工作队列具体内容,感兴趣的可以自行研究 SylixOS 源码。
3.1 创建工作队列
/*********************************************************************************************************
** 函数名称: __wqSExec
** 功能描述: 执行一次工作队列
** 输 入 : pwq 工作队列控制块
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static VOID __wqSExec (PLW_WORK_QUEUE pwq)
{
_jobQueueExec(pwq->q.WQ_sq.SWQ_pjobQ, LW_OPTION_WAIT_INFINITE);
}
/*********************************************************************************************************
** 函数名称: __wqTask
** 功能描述: 工作队列服务线程
** 输 入 : pvArg 工作队列控制块
** 输 出 : NULL
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static PVOID __wqTask (PVOID pvArg)
{
PLW_WORK_QUEUE pwq = (PLW_WORK_QUEUE)pvArg;
for (;;) {
switch (pwq->WQ_iType) {
case LW_WQ_TYPE_S:
__wqSExec(pwq);
break;
case LW_WQ_TYPE_D:
__wqDExec(pwq);
break;
default:
break;
}
if (pwq->WQ_bDelReq) {
break;
}
}
return (LW_NULL);
}
/*********************************************************************************************************
** 函数名称: __wqSCreate
** 功能描述: 创建一个简单工作队列
** 输 入 : pwq 工作队列控制块
** uiQSize 队列大小
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static PLW_WORK_QUEUE __wqSCreate (PLW_WORK_QUEUE pwq, UINT uiQSize)
{
pwq->WQ_iType = LW_WQ_TYPE_S;
pwq->q.WQ_sq.SWQ_pjobQ = _jobQueueCreate(uiQSize, LW_FALSE);
if (pwq->q.WQ_sq.SWQ_pjobQ == LW_NULL) {
return (LW_NULL);
}
return (pwq);
}
/*********************************************************************************************************
** 函数名称: API_WorkQueueCreate
** 功能描述: 创建一个工作队列
** 输 入 : pcName 队列名称
** uiQSize 队列大小
** bDelayEn 是否创建带有延迟执行功能的工作队列
** ulScanPeriod 如果带有延迟选项, 此参数指定服务线程扫描周期.
** pthreadattr 队列服务线程选项
** 输 出 : 工作队列句柄
** 全局变量:
** 调用模块:
** 注 意 : 如果不需要延迟执行功能, 则系统自动创建为简单工作队列, 内存占用量小, 执行效率高.
API 函数
*********************************************************************************************************/
LW_API
PVOID API_WorkQueueCreate (CPCHAR pcName,
UINT uiQSize,
BOOL bDelayEn,
ULONG ulScanPeriod,
PLW_CLASS_THREADATTR pthreadattr)
{
PLW_WORK_QUEUE pwq;
LW_CLASS_THREADATTR threadattr;
if (!uiQSize || (bDelayEn && !ulScanPeriod)) {
_ErrorHandle(EINVAL);
return (LW_NULL);
}
pwq = (PLW_WORK_QUEUE)__KHEAP_ALLOC(sizeof(LW_WORK_QUEUE));
if (pwq == LW_NULL) {
_DebugHandle(__ERRORMESSAGE_LEVEL, "kernel low memory.\r\n");
_ErrorHandle(ENOMEM);
return (LW_NULL);
}
lib_bzero(pwq, sizeof(LW_WORK_QUEUE));
/* 这里区分是普通工作队列还是延迟工作队列 */
if (bDelayEn) {
if (__wqDCreate(pwq, uiQSize, ulScanPeriod) == LW_NULL) {
__KHEAP_FREE(pwq);
return (LW_NULL);
}
} else {
/* 创建工作队列,调用创建内核工作队列接口 */
if (__wqSCreate(pwq, uiQSize) == LW_NULL) {
__KHEAP_FREE(pwq);
return (LW_NULL);
}
}
if (pthreadattr) {
threadattr = *pthreadattr;
} else {
threadattr = API_ThreadAttrGetDefault();
}
threadattr.THREADATTR_ulOption &= ~LW_OPTION_THREAD_DETACHED;
threadattr.THREADATTR_ulOption |= LW_OPTION_OBJECT_GLOBAL;
threadattr.THREADATTR_pvArg = pwq;
/* 创建用于执行工作队列的线程 __wqTask(工作者) */
pwq->WQ_ulTask = API_ThreadCreate(pcName, __wqTask, &threadattr, LW_NULL);
if (pwq->WQ_ulTask == LW_OBJECT_HANDLE_INVALID) {
if (bDelayEn) {
__wqDDelete(pwq);
} else {
__wqSDelete(pwq);
}
__KHEAP_FREE(pwq);
return (LW_NULL);
}
return ((PVOID)pwq);
}
3.2 添加工作
/*********************************************************************************************************
** 函数名称: __wqSInsert
** 功能描述: 将一个工作插入到工作队列
** 输 入 : pwq 工作队列控制块
** pfunc 执行函数
** pvArg0 ~ 5 执行参数
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static ULONG __wqSInsert (PLW_WORK_QUEUE pwq,
VOIDFUNCPTR pfunc,
PVOID pvArg0,
PVOID pvArg1,
PVOID pvArg2,
PVOID pvArg3,
PVOID pvArg4,
PVOID pvArg5)
{
/* 可以看到,这里是直接调用的内核工作队列 */
return (_jobQueueAdd(pwq->q.WQ_sq.SWQ_pjobQ,
pfunc, pvArg0, pvArg1, pvArg2,
pvArg3, pvArg4, pvArg5));
}
/*********************************************************************************************************
** 函数名称: API_WorkQueueInsert
** 功能描述: 将一个工作插入到工作队列
** 输 入 : pvWQ 工作队列句柄
** ulDelay 最小延迟执行时间
** pfunc 执行函数
** pvArg0 ~ 5 执行参数
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
API 函数
*********************************************************************************************************/
LW_API
ULONG API_WorkQueueInsert (PVOID pvWQ,
ULONG ulDelay,
VOIDFUNCPTR pfunc,
PVOID pvArg0,
PVOID pvArg1,
PVOID pvArg2,
PVOID pvArg3,
PVOID pvArg4,
PVOID pvArg5)
{
PLW_WORK_QUEUE pwq = (PLW_WORK_QUEUE)pvWQ;
ULONG ulRet;
if (!pwq) {
_ErrorHandle(EINVAL);
return (EINVAL);
}
switch (pwq->WQ_iType) {
case LW_WQ_TYPE_S:
ulRet = __wqSInsert(pwq, pfunc, pvArg0, pvArg1, pvArg2,
pvArg3, pvArg4, pvArg5);
break;
case LW_WQ_TYPE_D:
ulRet = __wqDInsert(pwq, ulDelay, pfunc, pvArg0, pvArg1, pvArg2,
pvArg3, pvArg4, pvArg5);
break;
default:
_ErrorHandle(ENXIO);
ulRet = ENXIO;
break;
}
return (ulRet);
}