LiteOS内核源码分析系列六 -任务及调度(2)-任务LOS_Task
LiteOS内核源码分析系列六 -任务及调度(2)-任务LOS_Task
任务是操作系统一个重要的概念,是竞争系统资源的最小运行单元。任务可以使用或等待CPU
、使用内存空间等系统资源,并独立于其它任务运行。Huawei LiteOS
的任务模块可以给用户提供多个任务,实现任务间的切换,帮助用户管理业务程序流程。本文我们来一起学习下LiteOS
任务模块的源代码。文中所涉及的源代码,均可以在LiteOS
开源站点https://gitee.com/LiteOS/LiteOS 获取。任务模块源代码、开发文档路径如下:
-
LiteOS
内核任务模块源代码包括任务模块的私有头文件kernel\base\include\los_task_pri.h、头文件kernel\include\los_task.h、
C
源代码文件kernel\base\los_task.c。 -
开发指南任务模块文档
在线文档https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Kernel_Developer_Guide.md#%E4%BB%BB%E5%8A%A1。
有关任务的基本概念、任务状态及状态迁移的介绍,请参考LiteOS
开发指南任务模块文档。接下来,我们看下任务模块的结构体,任务初始化,任务常用操作的源代码。
1、任务模块的结构体定义
在文件kernel\base\include\los_task_pri.h
定义的任务控制块结构体LosTaskCB
,源代码如下,结构体成员的解释见注释部分。
typedef struct {
VOID *stackPointer; /* 任务栈指针 */
UINT16 taskStatus; /* 任务状态 */
UINT16 priority; /* 任务优先级 */
UINT32 taskFlags : 31; /* 任务扩展标记,支持标记为自删除任务OS_TASK_FLAG_DETACHED、 系统级任务OS_TASK_FLAG_SYSTEM*/
UINT32 usrStack : 1; /* 是否使用用户栈 */
UINT32 stackSize; /* 任务栈大小 */
UINTPTR topOfStack; /* 栈顶指针 */
UINT32 taskId; /* 任务Id */
TSK_ENTRY_FUNC taskEntry; /* 任务入口函数 */
VOID *taskSem; /* 任务持有的信号量 */
#ifdef LOSCFG_LAZY_STACK
UINT32 stackFrame; /* 栈帧: 0=Basic, 1=Extended */
#endif
#ifdef LOSCFG_COMPAT_POSIX
VOID *threadJoin; /* pthread 适配 */
VOID *threadJoinRetval; /* pthread 适配 */
#endif
VOID *taskMux; /* 导致任务阻塞的互斥锁 */
#ifdef LOSCFG_OBSOLETE_API
UINTPTR args[4]; /* 任务入口函数的参数,兼容遗留API */
#else
VOID *args; /* 任务入口函数的参数 */
#endif
CHAR *taskName; /* 任务名称 */
LOS_DL_LIST pendList; /* 任务就绪队列等链表节点 */
SortLinkList sortList; /* 任务超时排序链表节点 */
#ifdef LOSCFG_BASE_IPC_EVENT
EVENT_CB_S event;
UINT32 eventMask; /* 事件掩码 */
UINT32 eventMode; /* 事件模式 */
#endif
VOID *msg; /* 分给给队列的内存*/
UINT32 priBitMap; /* BitMap for recording the change of task priority,
the priority can not be greater than 31 */
UINT32 signal; /* 任务信号 */
#ifdef LOSCFG_BASE_CORE_TIMESLICE
UINT16 timeSlice; /* 剩余的时间片 */
#endif
#ifdef LOSCFG_KERNEL_SMP
UINT16 currCpu; /* 当前运行的CPU核编号 */
UINT16 lastCpu; /* 上次运行的CPU核编号 */
UINT32 timerCpu; /* 任务阻塞或请求的CPU核编号 */
UINT16 cpuAffiMask; /* CPU亲和性掩码,最多支持16个核 */
#ifdef LOSCFG_KERNEL_SMP_TASK_SYNC
UINT32 syncSignal; /* 同步信号处理标记 */
#endif
#ifdef LOSCFG_KERNEL_SMP_LOCKDEP
LockDep lockDep;
#endif
#endif
#ifdef LOSCFG_DEBUG_SCHED_STATISTICS
SchedStat schedStat; /* 调度统计*/
#endif
#ifdef LOSCFG_KERNEL_PERF
UINTPTR pc;
UINTPTR fp;
#endif
} LosTaskCB;
另外一个比较重要的结构体是TSK_INIT_PARAM_S
,创建任务时,需要指定任务初始化的参数。源代码如下,结构体成员的解释见注释部分。
#ifdef LOSCFG_OBSOLETE_API
typedef VOID *(*TSK_ENTRY_FUNC)(UINTPTR param1,
UINTPTR param2,
UINTPTR param3,
UINTPTR param4);
#else
typedef VOID *(*TSK_ENTRY_FUNC)(VOID *param);
#endif
typedef struct tagTskInitParam {
TSK_ENTRY_FUNC pfnTaskEntry; /** 任务入口函数,函数类型定义为TSK_ENTRY_FUNC */
UINT16 usTaskPrio; /** 任务优先级 */
#ifdef LOSCFG_OBSOLETE_API
UINTPTR auwArgs[4]; /**< 任务参数 */
#else
VOID *pArgs; /**< 任务参数 */
#endif
UINT32 uwStackSize; /** 任务栈大小 */
CHAR *pcName; /** 任务名称 */
#ifdef LOSCFG_KERNEL_SMP
UINT16 usCpuAffiMask; /**< CPU亲和性掩码,需开启多核 */
#endif
UINT32 uwResved; /**< 若设置为 #LOS_TASK_STATUS_DETACHED,任务会自动删除 */
} TSK_INIT_PARAM_S;
2、任务模块初始化
在系统启动时,在kernel\init\los_init.c
中调用OsTaskInit()
进行任务模块初始化,还会调用OsIdleTaskCreate()
创建空闲任务。
2.1 任务模块初始化
函数OsTaskInit()
定义在kernel\base\los_task.c
,我们分析下这个函数的执行过程。
⑴处代码获取开发板配置的最大任务数g_taskMaxNum
,计算需要申请的内存大小size
,为任务控制块TCB
数组(也叫作任务池)g_taskCBArray
申请内存。为什么比最大任务数多申请一个呢?在删除任务时会使用。下文分析删除任务的源码时再详细讲解其用意。⑵处代码初始化双向链表g_losFreeTask
用作空闲的任务链表、g_taskRecycleList
可以回收的任务链表。⑶处循环初始化每一个任务,任务状态未使用OS_TASK_STATUS_UNUSED
,初始化任务Id
,并把任务挂在空闲任务链表上。⑷处初始化优先级队列,详细的代码实现剖析,参见之前的源码剖析文章。⑸处互斥锁死锁检测的调测特性的,后续系列文章专题进行讲解。⑹处代码初始化排序链表,详细的代码实现剖析,参见之前的源码剖析文章。⑺处如果开启了惰性栈,计算TCB
的成员变量stackFrame
在其结构体中的偏移量g_stackFrameOffLenInTcb
。
LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID)
{
UINT32 index;
UINT32 ret;
UINT32 size;
⑴ g_taskMaxNum = LOSCFG_BASE_CORE_TSK_LIMIT;
size = (g_taskMaxNum + 1) * sizeof(LosTaskCB);
g_taskCBArray = (LosTaskCB *)LOS_MemAlloc(m_aucSysMem0, size);
if (g_taskCBArray == NULL) {
return LOS_ERRNO_TSK_NO_MEMORY;
}
(VOID)memset_s(g_taskCBArray, size, 0, size);
⑵ LOS_ListInit(&g_losFreeTask);
LOS_ListInit(&g_taskRecycleList);
⑶ for (index = 0; index < g_taskMaxNum; index++) {
g_taskCBArray[index].taskStatus = OS_TASK_STATUS_UNUSED;
g_taskCBArray[index].taskId = index;
LOS_ListTailInsert(&g_losFreeTask, &g_taskCBArray[index].pendList);
}
⑷ ret = OsPriQueueInit();
if (ret != LOS_OK) {
return LOS_ERRNO_TSK_NO_MEMORY;
}
⑸ ret = OsMuxDlockCheckInitHook();
if (ret != LOS_OK) {
return LOS_ERRNO_TSK_NO_MEMORY;
}
⑹ for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {
ret = OsSortLinkInit(&g_percpu[index].taskSortLink);
if (ret != LOS_OK) {
return LOS_ERRNO_TSK_NO_MEMORY;
}
}
#ifdef LOSCFG_LAZY_STACK
⑺ g_stackFrameOffLenInTcb = (UINT16)LOS_OFF_SET_OF(LosTaskCB, stackFrame);
#endif
return LOS_OK;
}
2.2 创建空闲任务IdleCore000
除了初始化任务池,在系统启动阶段还会创建idle
空闲任务。⑴处设置任务初始化参数时,空闲任务的入口执行函数为OsIdleTask()
。⑵处可以看出空闲任务标记为了系统级别的任务OS_TASK_FLAG_SYSTEM
。
LITE_OS_SEC_TEXT_INIT UINT32 OsIdleTaskCreate(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S taskInitParam;
Percpu *perCpu = OsPercpuGet();
UINT32 *idleTaskId = &perCpu->idleTaskId;
(VOID)memset_s((VOID *)(&taskInitParam), sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
⑴ taskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)OsIdleTask;
taskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE;
taskInitParam.pcName = "IdleCore000";
taskInitParam.usTaskPrio = OS_TASK_PRIORITY_LOWEST;
#ifdef LOSCFG_KERNEL_SMP
taskInitParam.usCpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());
#endif
ret = LOS_TaskCreate(idleTaskId, &taskInitParam);
if (ret == LOS_OK) {
⑵ OS_TCB_FROM_TID(*idleTaskId)->taskFlags |= OS_TASK_FLAG_SYSTEM;
}
return ret;
}
我们看下空闲任务的入口执行函数为OsIdleTask()
,它调用LOS_TaskResRecycle()
回收任务栈资源,后文会分析如何回收任务指引。
LITE_OS_SEC_TEXT WEAK VOID OsIdleTask(VOID)
{
while (1) {
LOS_TaskResRecycle();
#ifdef LOSCFG_KERNEL_LOWPOWER
if (g_lowPowerHook != NULL) {
g_lowPowerHook();
}
#else
wfi();
#endif
}
}
- 点赞
- 收藏
- 关注作者
评论(0)