class="hide-preCode-box">

- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
⑴处代码计算需要申请的双向链表的内存大小,OS_TSK_SORTLINK_LEN
为32,即需要为32个双向链表节点申请内存空间。然后申请内存,⑵处申请内存失败时返回相应错误码。⑶处初始化申请的内存区域为0等。⑷处把申请的双向链表节点赋值给g_taskSortLink
的链表节点.sortLink
,作为排序链表的头节点,游标.cursor
初始化为0。然后⑸处的循环,调用LOS_ListInit()
函数把双向链表数组每个元素都初始化为双向循环链表。
2.2 插入排序链表
插入排序链表的函数为OsTaskAdd2TimerList()
。在任务等待互斥锁/信号量等资源时,都需要调用该函数将任务加入到对应的排序链表中。该函数包含两个入参,第一个参数LosTaskCB *taskCB
用于指定要延迟的任务,第二个参数UINT32 timeout
指定超时等待时间。
源码如下:
LITE_OS_SEC_TEXT VOID OsTaskAdd2TimerList(LosTaskCB *taskCB, UINT32 timeout)
{
LosTaskCB *taskDelay = NULL;
LOS_DL_LIST *listObject = NULL;
UINT32 sortIndex;
UINT32 rollNum;
⑴ sortIndex = timeout & OS_TSK_SORTLINK_MASK;
rollNum = (timeout >> OS_TSK_SORTLINK_LOGLEN);
⑵ (sortIndex > 0) ? 0 : (rollNum--);
⑶ EVALUATE_L(taskCB->idxRollNum, rollNum);
⑷ sortIndex = (sortIndex + g_taskSortLink.cursor);
sortIndex = sortIndex & OS_TSK_SORTLINK_MASK;
⑸ EVALUATE_H(taskCB->idxRollNum, sortIndex);
⑹ listObject = g_taskSortLink.sortLink + sortIndex;
⑺ if (listObject->pstNext == listObject) {
LOS_ListTailInsert(listObject, &taskCB->timerList);
} else {
⑻ taskDelay = LOS_DL_LIST_ENTRY((listObject)->pstNext, LosTaskCB, timerList);
do {
⑼ if (UWROLLNUM(taskDelay->idxRollNum) <= UWROLLNUM(taskCB->idxRollNum)) {
UWROLLNUMSUB(taskCB->idxRollNum, taskDelay->idxRollNum);
} else {
⑽ UWROLLNUMSUB(taskDelay->idxRollNum, taskCB->idxRollNum);
break;
}
⑾ taskDelay = LOS_DL_LIST_ENTRY(taskDelay->timerList.pstNext, LosTaskCB, timerList);
} while (&taskDelay->timerList != (listObject));
⑿ LOS_ListTailInsert(&taskDelay->timerList, &taskCB->timerList);
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 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
- 29
- 30
- 31
- 32
- 33
⑴处代码计算等待时间timeout
的低5位作为数组索引,高27位作为滚动数rollNum
。这2行代码数学上的意义,就是把等待时间处于32得到的商作为滚动数,余数作为数组索引。⑵处代码,如果余数为0,可以整除时,滚动数减1。减1设计的原因是,在函数VOID OsTaskScan(VOID)
中,每一个tick
到来时,如果滚动数大于0,滚动数减1,并继续滚动一圈。后文会分析该函数VOID OsTaskScan(VOID)
。
⑶处代码把滚动数赋值给任务taskCB->idxRollNum
的低27位。⑷处把数组索引加上游标,然后执行⑸赋值给任务taskCB->idxRollNum
的高5位。⑹根据数组索引获取双向链表头结点,⑺如果此处双向链表为空,直接插入链表里。如果链表不为空,执行⑻获取第一个链表节点对应的任务taskDelay
,然后遍历循环双向链表,把任务插入到合适的位置。⑼处如果待插入任务taskCB
的滚动数大于等于当前链表节点对应任务的滚动数,则从待插入任务taskCB
的滚动数中减去当前链表节点对应任务的滚动数,然后执行⑾获取下一个节点继续遍历。⑽处如果待插入任务taskCB
的滚动数小于当前链表节点对应任务的滚动数,则从当前链表节点对应任务的滚动数中减去待插入任务taskCB
的滚动数,然后跳出循环。执行⑿,完成任务插入。插入过程,可以结合上文的示意图进行理解。
2.3 从排序链表中删除
从排序链表中删除的函数为VOID OsTimerListDelete(LosTaskCB *taskCB)
。在任务恢复/删除等场景中,需要调用该函数将任务从任务排序链表中删除。该函数包含一个参数LosTaskCB *taskCB
,用于指定要从排序链表中删除的任务。
源码如下:
LITE_OS_SEC_TEXT VOID OsTimerListDelete(LosTaskCB *taskCB)
{
LOS_DL_LIST *listObject = NULL;
LosTaskCB *nextTask = NULL;
UINT32 sortIndex;
⑴ sortIndex = UWSORTINDEX(taskCB->idxRollNum);
⑵ listObject = g_taskSortLink.sortLink + sortIndex;
⑶ if (listObject != taskCB->timerList.pstNext) {
⑷ nextTask = LOS_DL_LIST_ENTRY(taskCB->timerList.pstNext, LosTaskCB, timerList);
UWROLLNUMADD(nextTask->idxRollNum, taskCB->idxRollNum);
}
⑸ LOS_ListDelete(&taskCB->timerList);
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
⑴处代码获取待从排序链表中删除的任务对应的数字索引。⑵处代码获取排序链表的头节点listObject
。⑶处代码判断待删除节点是否是最后一个节点,如果不是最后一个节点,执行执行⑷处代码获取待删除节点的下一个节点对应的任务nextTask
,在下一个节点的滚动数中增加待删除节点的滚动数,然后执行⑸处代码执行删除操作。如果是最后一个节点,直接执行⑸处代码删除该节点即可。
2.4 获取下一个超时到期时间
获取下一个超时到期时间的函数为OsTaskNextSwitchTimeGet()
,我们分析下其代码。
源码如下:
UINT32 OsTaskNextSwitchTimeGet(VOID)
{
LosTaskCB *taskCB = NULL;
UINT32 taskSortLinkTick = LOS_WAIT_FOREVER;
LOS_DL_LIST *listObject = NULL;
UINT32 tempTicks;
UINT32 index;
⑴ for (index = 0; index < OS_TSK_SORTLINK_LEN; index++) {
⑵ listObject = g_taskSortLink.sortLink + ((g_taskSortLink.cursor + index) % OS_TSK_SORTLINK_LEN);
⑶ if (!LOS_ListEmpty(listObject)) {
⑷ taskCB = LOS_DL_LIST_ENTRY((listObject)->pstNext, LosTaskCB, timerList);
⑸ tempTicks = (index == 0) ? OS_TSK_SORTLINK_LEN : index;
⑹ tempTicks += (UINT32)(UWROLLNUM((UINT32)taskCB->idxRollNum) * OS_TSK_SORTLINK_LEN);
⑺ if (taskSortLinkTick > tempTicks) {
taskSortLinkTick = tempTicks;
}
}
}
return taskSortLinkTick;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
⑴处代码循环遍历双向链表数组,⑵处代码从当前游标位置开始获取排序链表的头节点listObject
。⑶处代码判断排序链表是否为空,如果排序链表为空,则继续遍历下一个数组。如果链表不为空,⑷处代码获取排序链表的第一个链表节点对应的任务。⑸处如果遍历的数字索引为0,tick
数目使用32,否则使用具体的数字索引。⑹处获取任务的滚动数,计算出需要的等待时间,加上⑸处计算出的不足滚动一圈的时间。⑺处计算出需要等待的最小时间,即下一个最快到期的时间。
3 排序链表和Tick时间的关系
任务加入到排序链表后,时间一个tick
一个tick
的逝去,排序链表中的滚动数该如何更新呢?
时间每走过一个tick
,系统就会调用Tick
中断的处理函数OsTickHandler()
,该函数在kernel\src\los_tick.c
文件中实现。下面是该函数的代码片段,⑴处代码分别任务的超时到期情况。
LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{
#if (LOSCFG_BASE_CORE_TICK_HW_TIME == 1)
platform_tick_handler();
#endif
g_ullTickCount++;
#if (LOSCFG_BASE_CORE_TIMESLICE == 1)
OsTimesliceCheck();
#endif
⑴ OsTaskScan(); // task timeout scan
#if (LOSCFG_BASE_CORE_SWTMR == 1)
(VOID)OsSwtmrScan();
#endif
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
详细分析下函数OsTaskScan()
,来了解排序链表和tick
时间的关系。函数在kernel\base\los_task.c
文件中实现,代码片段如下:
LITE_OS_SEC_TEXT VOID OsTaskScan(VOID)
{
LosTaskCB *taskCB = NULL;
BOOL needSchedule = FALSE;
LOS_DL_LIST *listObject = NULL;
UINT16 tempStatus;
UINTPTR intSave;
intSave = LOS_IntLock();
⑴ g_taskSortLink.cursor = (g_taskSortLink.cursor + 1) % OS_TSK_SORTLINK_LEN;
listObject = g_taskSortLink.sortLink + g_taskSortLink.cursor;
⑵ if (listObject->pstNext == listObject) {
LOS_IntRestore(intSave);
return;
}
⑶ for (taskCB = LOS_DL_LIST_ENTRY((listObject)->pstNext, LosTaskCB, timerList);
&taskCB->timerList != (listObject);) {
tempStatus = taskCB->taskStatus;
⑷ if (UWROLLNUM(taskCB->idxRollNum) > 0) {
UWROLLNUMDEC(taskCB->idxRollNum);
break;
}
⑸ LOS_ListDelete(&taskCB->timerList);
⑹ if (tempStatus & OS_TASK_STATUS_PEND) {
taskCB->taskStatus &= ~(OS_TASK_STATUS_PEND);
LOS_ListDelete(&taskCB->pendList);
taskCB->taskSem = NULL;
taskCB->taskMux = NULL;
}
⑺ else if (tempStatus & OS_TASK_STATUS_EVENT) {
taskCB->taskStatus &= ~(OS_TASK_STATUS_EVENT);
}
⑻ else if (tempStatus & OS_TASK_STATUS_PEND_QUEUE) {
LOS_ListDelete(&taskCB->pendList);
taskCB->taskStatus &= ~(OS_TASK_STATUS_PEND_QUEUE);
} else {
taskCB->taskStatus &= ~(OS_TASK_STATUS_DELAY);
}
⑼ if (!(tempStatus & OS_TASK_STATUS_SUSPEND)) {
taskCB->taskStatus |= OS_TASK_STATUS_READY;
OsHookCall(LOS_HOOK_TYPE_MOVEDTASKTOREADYSTATE, taskCB);
OsPriqueueEnqueue(&taskCB->pendList, taskCB->priority);
needSchedule = TRUE;
}
if (listObject->pstNext == listObject) {
break;
}
taskCB = LOS_DL_LIST_ENTRY(listObject->pstNext, LosTaskCB, timerList);
}
LOS_IntRestore(intSave);
⑽ if (needSchedule) {
LOS_Schedule();
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 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
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
⑴处代码更新全局变量g_taskSortLink
的游标,指向双向链表数组下一个位置,然后获取该位置的双向链表头结点listObject
。
⑵如果链表为空,则返回。如果双向链表不为空,则执行
⑶循环遍历每一个链表节点。
⑷处如果链表节点的滚动数大于0,则滚动数减1,说明任务还需要继续等待一轮。如果链表节点的滚动数等于0,说明任务超时到期,执行
⑸从排序链表中删除。接下来需要根据任务状态分别处理,
⑹处如果代码是阻塞状态,取消阻塞状态,并从阻塞链表中删除。
⑺处如果任务阻塞在事件中,取消阻塞状态。
⑻如果任务阻塞在队列,从阻塞链表中删除,取消阻塞状态,如果不是上述状态,取消延迟状态OS_TASK_STATUS_DELAY
。
⑼处如果代码是挂起状态,设置任务为就绪状态,加入任务就绪队列,设置需要重新调度标记。
⑽如果设置需要重新调度,调用调度函数触发任务调度。
小结
掌握鸿蒙轻内核的排序链表TaskSortLinkAttr
这一重要的数据结构,会给进一步学习、分析鸿蒙轻内核源代码打下了基础,让后续的学习更加容易。
如果大家想更加深入的学习 OpenHarmony 开发的内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……

- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……



data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/maniuT/article/details/139471958","extend1":"pc","ab":"new"}">>
id="blogExtensionBox" style="width:400px;margin:auto;margin-top:12px" class="blog-extension-box"> class="blog_extension blog_extension_type2" id="blog_extension">
class="extension_official" data-report-click="{"spm":"1001.2101.3001.6471"}" data-report-view="{"spm":"1001.2101.3001.6471"}">
class="blog_extension_card_left">
class="blog_extension_card_cont">
鸿蒙开发学习资料领取!!!
class="blog_extension_card_cont_r">
微信名片
评论记录:
回复评论: