Hongmeng kernel source code analysis (process management)

Note: Based on core open source obscurity analysis, the official source [ kernel_liteos_a ] official documents [ docs ] Reference Document [ Huawei LiteOS ]
author: Hong Meng core enthusiasts, will continue to study obscurity kernel, update blog, so stay tuned. The content only represents personal views, errors are welcome, everyone is welcome to correct and improve. All articles in this series enter to view the  source code analysis of Hongmeng system (general catalog)


This article analyzes the source code of the task scheduling mechanism in detail: ../kernel/base/sched/sched_sq/los_process.c 

table of Contents

Let's take a look at the official instructions

basic concepts

scenes to be used

Start formal analysis

Process management initialization

The process of creating kernel Kprocess

The process of creating a user mode process

Leave two small questions for everyone to think about



Let's take a look at the official instructions

basic concepts

From a system perspective, a process is a resource management unit . Processes can use or wait for system resources such as CPU and memory space, and run independently of other processes.

The process module of the OpenHarmony kernel can provide users with multiple processes, realize switching and communication between processes, and help users manage business program flows. In this way, users can devote more energy to the realization of business functions.

The process in the OpenHarmony kernel adopts a preemptive scheduling mechanism, supports time slice round-robin scheduling and FIFO scheduling mechanism.

The OpenHarmony kernel process has a total of 32 priorities (0-31), and there are 22 (10-31) configurable priorities for user processes. The highest priority is 10 and the lowest priority is 31.

High-priority processes can preempt low-priority processes, and low-priority processes can only be scheduled after high-priority processes are blocked or terminated.

Each user mode process has its own independent process space, which is invisible to each other, realizing inter-process isolation.

The user mode root process init is created by the kernel mode, and other user mode processes are forks from the init process.

Process status description:

  • Initialization (Init): The process is being created.

  • Ready: The process is in the ready list, waiting for CPU scheduling.

  • Running: The process is running.

  • Pend: The process is blocked and suspended. When all threads in this process are blocked, the process is blocked and hangs.

  • Zombies (Zombies): The process ends, waiting for the parent process to reclaim its control block resources.

Figure 1  Schematic diagram of process state transition

Process state transition description:

  • Init→Ready:

    When a process is created or fork, it enters the Init state after getting the process control block and is in the process initialization stage. When the process initialization is completed, the process is inserted into the scheduling queue, and the process enters the ready state.

  • Ready→Running:

    After the process is created, it enters the ready state. When a process switch occurs, the process with the highest priority in the ready list is executed and enters the running state. If no other threads in the process are in the ready state at this time, the process is deleted from the ready list and only in the running state; if there are other threads in the process in the ready state at this time, the process is still in the ready queue. The ready state and the running state of the process coexist.

  • Running→Pend:

    When all the threads in the process are in the blocking state, when the last thread of the process turns into the blocking state, it enters the blocking state synchronously, and then the process switch occurs.

  • Pend→Ready / Pend→Running:

    When any thread in the blocked process returns to the ready state, the process is added to the ready queue and synchronously changes to the ready state. If a process switch occurs at this time, the process state changes from the ready state to the running state.

  • Ready→Pend:

    When the last ready state thread in the process is in the blocking state, the process is deleted from the ready list, and the process changes from the ready state to the blocking state.

  • Running→Ready:

    There are two situations in which the process changes from the running state to the ready state:

    1. After a process with a higher priority is created or restored, process scheduling occurs. At this moment, the highest priority process in the ready list becomes the running state, and the originally running process changes from the running state to the ready state.
    2. If the scheduling strategy of the process is SCHED_RR, and another process with the same priority is in the ready state, after the time slice of the process is exhausted, the process turns from the running state to the ready state, and another process with the same priority changes from the ready state The state changes to the running state.
  • Running→Zombies:

    When the main thread or all threads of the process are finished, the process turns from the running state to the zombie state, waiting for the parent process to reclaim resources.

scenes to be used

After the process is created, the user can only operate the resources of his own process space, and cannot operate the resources of other processes (except shared resources). User mode allows processes to be suspended, resumed, and delayed. At the same time, user mode process scheduling priority and scheduling strategy can be set to obtain process scheduling priority and scheduling strategy. When the process ends, the process will actively release the held process resources, but the held process pid resources need to be recycled by the parent process through wait/waitpid or when the parent process exits.

Start formal analysis

Please note that the red word process marked above  is a resource management unit, not a scheduling unit. Who is the scheduling unit? It is Task/Thread, look at the define of the official corresponding state

#define OS_PROCESS_STATUS_INIT           0x0010U
#define OS_PROCESS_STATUS_READY          0x0020U
#define OS_PROCESS_STATUS_RUNNING        0x0040U
#define OS_PROCESS_STATUS_PEND           0x0080U
#define OS_PROCESS_STATUS_ZOMBIES        0x100U

 The process of a process from creation to death must be extremely complicated in the kernel, just like the birth to the disappearance of a company. In order to facilitate the understanding of the process, the author often makes an analogy, from the examples in life, the mysterious system kernel is externalized and dissected for everyone to see. Such a complicated thing must be carried by a complicated structure, that is, LosProcessCB (Process Control Block). The code is very long but it must be taken out. The length is a little longer, bear it!


LITE_OS_SEC_BSS LosProcessCB *g_runProcess[LOSCFG_KERNEL_CORE_NUM]; //用一个指针数组记录进程运行,LOSCFG_KERNEL_CORE_NUM 为 CPU的核数
LITE_OS_SEC_BSS LosProcessCB *g_processCBArray = NULL;//进程数组,最大进程数为 64个 
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_freeProcess;//记录空闲进程链表
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_processRecyleList;//记录回收的进程列表


typedef struct ProcessCB {
    CHAR                 processName[OS_PCB_NAME_LEN]; /**< Process name */
    UINT32               processID;                    /**< process ID = leader thread ID */
    UINT16               processStatus;                /**< [15:4] process Status; [3:0] The number of threads currently
                                                            running in the process */
    UINT16               priority;                     /**< process priority */
    UINT16               policy;                       /**< process policy */
    UINT16               timeSlice;                    /**< Remaining time slice */
    UINT16               consoleID;                    /**< The console id of task belongs  */
    UINT16               processMode;                  /**< Kernel Mode:0; User Mode:1; */
    UINT32               parentProcessID;              /**< Parent process ID */
    UINT32               exitCode;                     /**< process exit status */
    LOS_DL_LIST          pendList;                     /**< Block list to which the process belongs */
    LOS_DL_LIST          childrenList;                 /**< my children process list */
    LOS_DL_LIST          exitChildList;                /**< my exit children process list */
    LOS_DL_LIST          siblingList;                  /**< linkage in my parent's children list */
    ProcessGroup         *group;                       /**< Process group to which a process belongs */
    LOS_DL_LIST          subordinateGroupList;         /**< linkage in my group list */
    UINT32               threadGroupID;                /**< Which thread group , is the main thread ID of the process */
    UINT32               threadScheduleMap;            /**< The scheduling bitmap table for the thread group of the
                                                            process */
    LOS_DL_LIST          threadSiblingList;            /**< List of threads under this process */
    LOS_DL_LIST          threadPriQueueList[OS_PRIORITY_QUEUE_NUM]; /**< The process's thread group schedules the
                                                                         priority hash table */
    volatile UINT32      threadNumber; /**< Number of threads alive under this process */
    UINT32               threadCount;  /**< Total number of threads created under this process */
    LOS_DL_LIST          waitList;     /**< The process holds the waitLits to support wait/waitpid */
#if (LOSCFG_KERNEL_SMP == YES)
    UINT32               timerCpu;     /**< CPU core number of this task is delayed or pended */
#endif
    UINTPTR              sigHandler;   /**< signal handler */
    sigset_t             sigShare;     /**< signal share bit */
#if (LOSCFG_KERNEL_LITEIPC == YES)
    ProcIpcInfo         ipcInfo;       /**< memory pool for lite ipc */
#endif
    LosVmSpace          *vmSpace;       /**< VMM space for processes */
#ifdef LOSCFG_FS_VFS
    struct files_struct *files;        /**< Files held by the process */
#endif
    timer_t             timerID;       /**< iTimer */

#ifdef LOSCFG_SECURITY_CAPABILITY
    User                *user;
    UINT32              capability;
#endif
#ifdef LOSCFG_SECURITY_VID
    TimerIdMap          timerIdMap;
#endif
#ifdef LOSCFG_DRIVERS_TZDRIVER
    struct file         *execFile;     /**< Exec bin of the process */
#endif
    mode_t umask;
} LosProcessCB;

There are two process modes, kernel mode and user mode. It can be thought that a kernel mode of the highest priority process will be created in the main function, which is  KProcess

The calling process is as follows

View the task running status through the task command, you can see the KProcess process, you know that it is a kernel process by the name, it is created when the system starts, and you can see the thread running status of KProcess in the figure

For example: swt_task, oom_task, jffs2_gc_thread, etc.

Hongmeng task

Process management initialization

LITE_OS_SEC_BSS LosProcessCB *g_runProcess[LOSCFG_KERNEL_CORE_NUM];//*kyf CPU内核个数
LITE_OS_SEC_BSS LosProcessCB *g_processCBArray = NULL; //*kyf 进程池
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_freeProcess;//*kyf 空闲状态下可供分配的进程,此时进程白纸一张
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_processRecyleList;//*kyf 需要回收的进程列表
LITE_OS_SEC_BSS UINT32 g_userInitProcess = OS_INVALID_VALUE;//*kyf 用户态的初始进程,用户态下其他进程由它 fork
LITE_OS_SEC_BSS UINT32 g_kernelInitProcess = OS_INVALID_VALUE;//*kyf 内核态初始进程,内核态下其他进程由它 fork
LITE_OS_SEC_BSS UINT32 g_kernelIdleProcess = OS_INVALID_VALUE;
LITE_OS_SEC_BSS UINT32 g_processMaxNum;//*kyf 进程最大数量
LITE_OS_SEC_BSS ProcessGroup *g_processGroup = NULL;//*kyf 进程组

Let me explain all the variables in .c first. The comments are added by the author, and there are few comments on the Hongmeng kernel.

Let's just look at the code

/**
 * @ingroup los_config
 * Maximum supported number of process rather than the number of usable processes.
 */
#ifndef LOSCFG_BASE_CORE_PROCESS_LIMIT
#define LOSCFG_BASE_CORE_PROCESS_LIMIT 64
#endif

LITE_OS_SEC_TEXT_INIT UINT32 OsProcessInit(VOID)
{
    UINT32 index;
    UINT32 size;

    g_processMaxNum = LOSCFG_BASE_CORE_PROCESS_LIMIT;//*kyf 默认是64个
    size = g_processMaxNum * sizeof(LosProcessCB);

    g_processCBArray = (LosProcessCB *)LOS_MemAlloc(m_aucSysMem1, size);//*kyf 进程池
    if (g_processCBArray == NULL) {
        return LOS_NOK;
    }
    (VOID)memset_s(g_processCBArray, size, 0, size);

    LOS_ListInit(&g_freeProcess);
    LOS_ListInit(&g_processRecyleList);

    for (index = 0; index < g_processMaxNum; index++) {
        g_processCBArray[index].processID = index;
        g_processCBArray[index].processStatus = OS_PROCESS_FLAG_UNUSED;//*kyf 默认都是白纸一张,臣妾干净着呢
        LOS_ListTailInsert(&g_freeProcess, &g_processCBArray[index].pendList);//*kyf 可分配链表
    }

    g_userInitProcess = 1; /* 1: The root process ID of the user-mode process is fixed at 1 */
    LOS_ListDelete(&g_processCBArray[g_userInitProcess].pendList);//*kyf 清空pend链表

    g_kernelInitProcess = 2; /* 2: The root process ID of the kernel-mode process is fixed at 2 */
    LOS_ListDelete(&g_processCBArray[g_kernelInitProcess].pendList);//*kyf 清空pend链表

    return LOS_OK;
}

The code is very clear, a process pool is created, the default is 64 processes, that is, the system can have up to 64 processes without changing the macro LOSCFG_BASE_CORE_PROCESS_LIMIT, but two processes are occupied first, one in user mode and one in kernel mode, they It is the father of the subsequent creation process, so at most 62 processes can be created outside, and the task blocking list of the last two fathers of the code is also cleared.

The process of creating kernel Kprocess

Create a core state process, which is the process number [2] in the thread pool. In the task command, Kprocess PID = 2, the parameters are the core state and the highest priority 0

 The code sets kprocess as the current process, and forks a KIdle (the idle process in kernel mode)

STATIC UINT32 OsCreateIdleProcess(VOID)
{
    UINT32 ret;
    CHAR *idleName = "Idle";
    LosProcessCB *idleProcess = NULL;
    Percpu *perCpu = OsPercpuGet();
    UINT32 *idleTaskID = &perCpu->idleTaskID;

    ret = OsCreateResourceFreeTask();//*kyf 创建一个资源回收任务 
    if (ret != LOS_OK) {
        return ret;
    }

    ret = LOS_Fork(CLONE_FILES, "KIdle", (TSK_ENTRY_FUNC)OsIdleTask, LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE);
    if (ret < 0) {
        return LOS_NOK;
    }
    g_kernelIdleProcess = (UINT32)ret;

    idleProcess = OS_PCB_FROM_PID(g_kernelIdleProcess);
    *idleTaskID = idleProcess->threadGroupID;
    OS_TCB_FROM_TID(*idleTaskID)->taskStatus |= OS_TASK_FLAG_SYSTEM_TASK;
#if (LOSCFG_KERNEL_SMP == YES)
    OS_TCB_FROM_TID(*idleTaskID)->cpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());
#endif
    (VOID)memset_s(OS_TCB_FROM_TID(*idleTaskID)->taskName, OS_TCB_NAME_LEN, 0, OS_TCB_NAME_LEN);
    (VOID)memcpy_s(OS_TCB_FROM_TID(*idleTaskID)->taskName, OS_TCB_NAME_LEN, idleName, strlen(idleName));
    return LOS_OK;
}

The above is the initialization process of the kernel mode process

The process of creating a user mode process

/**
 * @ingroup los_process
 * User state root process default priority
 */
#define OS_PROCESS_USERINIT_PRIORITY     28

LITE_OS_SEC_TEXT_INIT UINT32 OsUserInitProcess(VOID)
{
    INT32 ret;
    UINT32 size;
    TSK_INIT_PARAM_S param = { 0 };
    VOID *stack = NULL;
    VOID *userText = NULL;
    CHAR *userInitTextStart = (CHAR *)&__user_init_entry;
    CHAR *userInitBssStart = (CHAR *)&__user_init_bss;
    CHAR *userInitEnd = (CHAR *)&__user_init_end;
    UINT32 initBssSize = userInitEnd - userInitBssStart;
    UINT32 initSize = userInitEnd - userInitTextStart;

    LosProcessCB *processCB = OS_PCB_FROM_PID(g_userInitProcess);
    ret = OsProcessCreateInit(processCB, OS_USER_MODE, "Init", OS_PROCESS_USERINIT_PRIORITY);
    if (ret != LOS_OK) {
        return ret;
    }

    userText = LOS_PhysPagesAllocContiguous(initSize >> PAGE_SHIFT);
    if (userText == NULL) {
        ret = LOS_NOK;
        goto ERROR;
    }

    (VOID)memcpy_s(userText, initSize, (VOID *)&__user_init_load_addr, initSize);
    ret = LOS_VaddrToPaddrMmap(processCB->vmSpace, (VADDR_T)(UINTPTR)userInitTextStart, LOS_PaddrQuery(userText),
                               initSize, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE |
                               VM_MAP_REGION_FLAG_PERM_EXECUTE | VM_MAP_REGION_FLAG_PERM_USER);
    if (ret < 0) {
        goto ERROR;
    }

    (VOID)memset_s((VOID *)((UINTPTR)userText + userInitBssStart - userInitTextStart), initBssSize, 0, initBssSize);

    stack = OsUserInitStackAlloc(g_userInitProcess, &size);
    if (stack == NULL) {
        PRINTK("user init process malloc user stack failed!\n");
        ret = LOS_NOK;
        goto ERROR;
    }

    param.pfnTaskEntry = (TSK_ENTRY_FUNC)userInitTextStart;
    param.userParam.userSP = (UINTPTR)stack + size;
    param.userParam.userMapBase = (UINTPTR)stack;
    param.userParam.userMapSize = size;
    param.uwResved = OS_TASK_FLAG_PTHREAD_JOIN;
    ret = OsUserInitProcessStart(g_userInitProcess, &param);
    if (ret != LOS_OK) {
        (VOID)OsUnMMap(processCB->vmSpace, param.userParam.userMapBase, param.userParam.userMapSize);
        goto ERROR;
    }

    return LOS_OK;

ERROR:
    (VOID)LOS_PhysPagesFreeContiguous(userText, initSize >> PAGE_SHIFT);
    OsDeInitPCB(processCB);
    return ret;
}

It is found that the process of user mode init and creation is similar to kernel mode, and the priority of the user mode father process is 28, so low.

Leave two small questions for everyone to think about

 There are some code about memory in OsUserInitProcess, and the value cannot be found in the source code, such as:

    CHAR *userInitTextStart = (CHAR *)&__user_init_entry;
    CHAR *userInitBssStart = (CHAR *)&__user_init_bss;
    CHAR *userInitEnd = (CHAR *)&__user_init_end;

Why is this happening?

The PIDs of the other two fathers are 1 and 2. Where did the process number 0 in the process pool go?

Welcome everyone to reply in the comment area, and we will analyze in detail later. More articles  can be viewed in  Hongmeng system source code analysis (General Catalog)

 

Guess you like

Origin blog.csdn.net/kuangyufei/article/details/108595941