Hongmeng light kernel m core source code analysis: task sorting list of data structure

Time:2021-7-22

Abstract:The task sorting list of Hongmeng light kernel is a very important and basic data structure, which is used for business scenarios such as task delay expiration / timeout wake-up.

This article will continue to introduce the important data structure in Hongmeng light kernel source code: task sort linkattr. The task sorting list of Hongmeng light kernel is a very important and basic data structure, which is used for business scenarios such as task delay expiration / timeout wake-up. The source code involved in this article, taking the openharmony liteos-m kernel as an example, can be downloaded from the open source sitehttps://gitee.com/openharmony…obtain.

1 task sort list

Let’s first look at the data structure of the task sort link. Task sort linked list is a circular two-way linked list array. As the head node of the two-way linked list, task sort linked list attribute structure tasksortlinkattr points to the first element of the two-way linked list array. It also maintains cursor information and records the current location information. Let’s first look at the definition of the structure of the sorted list attribute.

1.1 definition of task sort linked list attribute structure

In kernel / include / Los_ Task. H header file defines the structure tasksortlinkattr of sort linked list attribute. The structure defines the head node LOS of the sorted list_ DL_ List * sortlink, the cursor uint16 cursor, and a reserved field are not used for the time being.

The source code is as follows:

 typedef struct {
        LOS_DL_LIST *sortLink;
        UINT16      cursor;
        UINT16      reserved;
    } TaskSortLinkAttr;

In the file kernel / SRC / Los_ Task. C defines the global variable g of type tasksortlinkattr, which is a sort linked list attribute structure_ Task sortlink, the member variable sortlink of the global variable, as the head node of the sorted list, points to a 32 long circular bidirectional list array, and the member variable cursor, as a cursor, records the current cursor position of the circular array. The source code is as follows.

​​​​​​​    LITE_OS_SEC_BSS  TaskSortLinkAttr                    g_taskSortLink;

Let’s talk about it with a diagram. The task sorting list is a circular two-way list array, with a length of 32. Each element is a two-way list, and the list node timerlist of the task lostaskcb is mounted. The member variable idxrollnum of task lostaskcb records the index and scroll number of the array. Global variable G_ The member variable cursor of tasksortlink records the current cursor position. After each tick, the cursor points to the next position. It takes 32 ticks to turn one round. When the bidirectional linked list is not empty, the scrolling number maintained by the first node is reduced by 1. Such a data structure is similar to a clock dial, also known as a time wheel.

Let’s take an example to illustrate how the task sorting list based on time round can manage the delay and timeout of tasks. If the current cursor cursor is 1, when a task needs to delay 72 ticks, 72 = 2 * 32 + 8, it means that the sort index is 8 and the rollnum is 2. The task will be inserted into the bi-directional linked list position with the array index of sortindex + cursor = 9, and the bi-directional linked list at 9 will be required to maintain the node’s scroll to 2. As the tick time goes on, it takes 8 ticks from the current cursor position to the array index position 9. When running to 9, if the number of scrolls is greater than 0, the number of scrolls will be reduced by 1. After another 2 rounds of running, a total of 72 ticks will be needed, and the task will be delayed and expired, which can be removed from the sorted list. The scrolling number of the first linked list node of the bidirectional linked list corresponding to each array element indicates how many rounds it needs to turn before the node task is due. The number of scrolls of the second linked list node needs to be added to the number of scrolls of the first node to indicate the number of rounds that the second node needs to turn. And so on.

The schematic diagram is as follows:
Hongmeng light kernel m core source code analysis: task sorting list of data structure

1.2 task sorting list macro definition

On OS_ TSK_ SORTLINK_ Some macro definitions related to task sort list are defined in len header file. The length of bidirectional linked list array for delayed tasks is defined as 32, the high-order bits are 5, and the low-order bits are 27. For the timeout of the task, take the high 27 bits as the scrolling number and the low 5 bits as the array index.

The source code is as follows:

​​​​​​​

/**
        *Number of bidirectionally linked list arrays of deferred tasks (number of buckets): 32
        */
        #define OS_TSK_SORTLINK_LEN                         32
     
        /**
        *High order bit number: 5
        */
        #define OS_TSK_HIGH_BITS                            5U
     
        /**
        *Low order bit number: 27
        */
        #define OS_TSK_LOW_BITS                             (32U - OS_TSK_HIGH_BITS)
     
        /**
        *Maximum number of scrolls: 0xFFFF ffdf, 1111 0111 1111 1111 1111 1111 1101 1111
        */
        #define OS_TSK_MAX_ROLLNUM                          (0xFFFFFFFFU - OS_TSK_SORTLINK_LEN)
     
        /**
        *Bit width of task delay time: 5
        */
        #define OS_TSK_SORTLINK_LOGLEN                      5
     
        /**
        *Mask of bucket number of delayed task: 31, 0001, 1111
        */
        #define OS_TSK_SORTLINK_MASK                        (OS_TSK_SORTLINK_LEN - 1U)
     
        /**
        *High order mask for scrolls: 1111 100000000
        */
        #define OS_TSK_HIGH_BITS_MASK                       (OS_TSK_SORTLINK_MASK << OS_TSK_LOW_BITS)
     
        /**
        *Low order mask of scroll number: 0000 0111 1111 1111 1111 1111 1111 1111 1111
        */
        #define OS_TSK_LOW_BITS_MASK                        (~OS_TSK_HIGH_BITS_MASK)

2 task sort linked list operation

We analyze the operation of the next task sorting list, including initialization, insert, delete, scroll number update, get the next due time and so on.

2.1 initialize the sort list

In the initialization and startup phase of the system kernel, the task sorting list is initialized in the function uint32 ostaskinit (void). The calling relationship of this function is as follows: main. C: main() > – kernel / SRC / Los_ init.c:LOS_ KernelInit() –> kernel\src\los_ task.c:OsTaskInit()。

The source code for initializing the sort list function is as follows:

​​​​​​​

 LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID)
    {
        UINT32 size;
        UINT32 index;
        LOS_DL_LIST *listObject = NULL;
        ......
    ⑴  size = sizeof(LOS_DL_LIST) * OS_TSK_SORTLINK_LEN;
        listObject = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, size);
    ⑵  if (listObject == NULL) {
            (VOID)LOS_MemFree(m_aucSysMem0, g_taskCBArray);
            return LOS_ERRNO_TSK_NO_MEMORY;
        }
     
    ⑶  (VOID)memset_s((VOID *)listObject, size, 0, size);
    ⑷  g_taskSortLink.sortLink = listObject;
        g_taskSortLink.cursor = 0;
        for (index = 0; index < OS_TSK_SORTLINK_LEN; index++, listObject++) {
    ⑸      LOS_ListInit(listObject);
        }
     
        return LOS_OK;
    }
     

(1) the code calculates the memory size of the bidirectional linked list to be applied, OS_ TSK_ SORTLINK_ Len is 32, that is, 32 bidirectional linked list nodes need to apply for memory space. Then apply for memory, and return the corresponding error code when the memory application fails at (2). (3) the memory area of initialization application is 0, etc. (4) assign the bidirectional list node of the application to g_ As the head node of the sorted list, the cursor. Cursor is initialized to 0. Then the loop at (5) calls LOS_ The listinit() function initializes each element of the bidirectional list array as a bidirectional circular list.

2.2 insert sort list

The function to insert the sorted list is ostask add2timerlist(). When a task is waiting for resources such as mutex / semaphore, it needs to call this function to add the task to the corresponding sort list. The function contains two input parameters, the first parameter lostaskcb * taskcb is used to specify the task to be delayed, and the second parameter uint32 timeout is used to specify the timeout waiting time.

The source code is as follows:

​​​​​​​

 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);
        }
    }

(1) the lower 5 bits of the waiting time timeout are used as the array index, and the upper 27 bits are used as rollnum. The mathematical meaning of these two lines of code is to use the quotient obtained when the waiting time is at 32 as the rolling number and the remainder as the array index. (2) if the remainder is 0, when it can be divided, the scrolling number will be reduced by 1. The reason for minus 1 is that in the function void ostaskscan (void), if the number of scrolls is greater than 0, the number of scrolls will be minus 1 and continue to scroll one circle. Later, we will analyze the function void ostastaskscan (void).

(3) the code assigns the scroll number to the lower 27 bits of task taskcb – > idxrollnum. Add a cursor to the array index at (4), and then assign (5) to the top 5 bits of task taskcb – > idxrollnum. (6) get the two-way Chain header node according to the array index. (7) if the two-way chain list is empty, insert it directly into the chain list. If the linked list is not empty, execute (8) to get the task taskdelay corresponding to the first linked list node, and then traverse the circular bidirectional linked list to insert the task into the appropriate location. (9) if the scrolling number of the task taskcb to be inserted is greater than or equal to the scrolling number of the task corresponding to the current linked list node, then subtract the scrolling number of the task corresponding to the current linked list node from the scrolling number of the task taskcb to be inserted, and then execute (11) to get the next node to continue to traverse. (10) if the scrolling number of the task to be inserted is less than that of the task corresponding to the current linked list node, the scrolling number of the task to be inserted will be subtracted from the scrolling number of the task corresponding to the current linked list node, and then the cycle will jump out. Execute (12) to complete the task insertion. The insertion process can be understood in combination with the schematic diagram above.

2.3 remove from sorted list

The function deleted from the sorted list is void ostimerlistdelete (lostaskcb)taskCB)。 In the scenario of task recovery / deletion, we need to call this function to delete the task from the task sorting list. This function contains a parameter lostaskcbTaskcb, which is used to specify the task to be removed from the sorted list.

The source code is as follows:

​​​​​​​

 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);
    }

(1) get the number index corresponding to the task to be deleted from the sorted list. (2) get Listobject, the head node of the sort list. (3) the code determines whether the node to be deleted is the last node. If it is not the last node, execute (4) the code to obtain the nexttask corresponding to the next node of the node to be deleted, add the scrolling number of the node to be deleted in the scrolling number of the next node, and then execute (5) the code to delete. If it is the last node, directly execute code (5) to delete the node.

2.4 get the next timeout expiration time

The function to get the next timeout expiration time is ostasknextswitchtimeget (). Let’s analyze the code.

The source code is as follows:

​​​​​​​

    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;
    }

(1) the code iterates through the bidirectional linked list array, and (2) the code obtains the Listobject, the head node of the sorted linked list, from the current cursor position. (3) whether the sort list is empty is judged by the code at. If the sort list is empty, it will continue to traverse the next array. If the list is not empty, code (4) gets the task corresponding to the first list node of the sorted list. (5) if the traversal number index is 0, the number of ticks is 32, otherwise, the specific number index is used. (6) get the rolling number of the task, calculate the required waiting time, plus (5) calculate the time less than one roll. (7) calculate the minimum waiting time, that is, the next fastest due time.

3. The relationship between sort linked list and tick time

After a task is added to the sorted list, how to update the scrolling number in the sorted list when time goes by tick by tick?

Every time a tick is passed, the system will call the processing function ostickhandler () of tick interrupt, which is in kernel / SRC / Los_ C file. The following is the code fragment of the function, and the timeout and expiration of the code tasks.

​​​​​​​

   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
    }

Detailed analysis of the next function ostaskscan() to understand the relationship between sorted list and tick time. The function is in kernel / base / Los_ Task. C file, the code fragment is as follows:

​​​​​​​

    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();
        }
    }

(1) code update global variable G_ The cursor of tasksortlink points to the next position of the bidirectional linked list array, and then obtains the Listobject of the bidirectional linked list header node at that position. (2) if the linked list is empty, return. If the two-way linked list is not empty, then execute (3) loop to traverse each linked list node. (4) if the scrolling number of the linked list node is greater than 0, the scrolling number will be reduced by 1, indicating that the task still needs to wait for another round. If the scrolling number of the linked list node is equal to 0, it indicates that the task timeout has expired, and execute (5) to delete it from the sorted linked list. Next, we need to deal with it separately according to the task state. If the code is blocked, cancel the blocking state and delete it from the blocking list. (7) if the task is blocked in the event, cancel the blocking state. (8) if the task is blocked in the queue, delete it from the blocking list and cancel the blocking state. If not, cancel the delay state_ TASK_ STATUS_ DELAY。 (9) if the code is suspended, set the task to ready state, join the task ready queue, and set the need to reschedule flag. (10) if the setting needs to be rescheduled, call the scheduling function to trigger task scheduling.

Summary

Mastering the important data structure of Hongmeng light kernel, tasksortlinkattr, will lay a foundation for further learning and analyzing the source code of Hongmeng light kernel, and make the subsequent learning easier. More sharing articles will be launched in the future. Please look forward to it. You are also welcome to share your experience in learning and using Hongmeng light kernel. If you have any questions or suggestions, you can leave us a message:https://gitee.com/openharmony…。 In order to find Hongmeng light kernel code warehouse more easily, it is recommended to visithttps://gitee.com/openharmony…, follow watch, like star, and fork to your account, thank you.

Click follow to learn about Huawei’s new cloud technology for the first time~

Recommended Today

What does webrtc mean to you

As a toolbox, webrtc has the advantages of security, good compatibility and network enhancement compared with the traditional video conference. Author / Eric Rescorla Original link/https://blog.mozilla.org/blog… The wide availability of high-quality video conferencing is one of the real successes of the Internet. Of course, the concept of video conference has a long history (you can […]