The Master said, “In the beginning I listened to what men said and believed what they did; now I listen to what men said and see what they did. To give and change.” “The Analects of Confucius” : Gong Ye chang

A hundred blog series. This paper is: the v02. Xx HongMeng kernel source code analysis (process management articles) | who in the management of core resources

Process management:

  • V02. Xx HongMeng kernel source code analysis (process management) | who in the management of core resources

  • V24. Xx HongMeng kernel source code analysis concept (process) | process in which resources management

  • V45. Xx HongMeng kernel source code analysis (Fork) | one call, return twice

  • V46. Xx HongMeng kernel source code analysis (special) | mouse son can make hole

  • V47. Xx HongMeng kernel source code analysis (process recycling) | how to entrust an orphan to ancestor dying

  • V48. Xx HongMeng kernel source code analysis (signal) | the year lead half hundred, still energetic

  • V49. Xx HongMeng kernel source code analysis (signal) | who let CPU run for four times in the stack

  • V71. Xx HongMeng kernel source code analysis (Shell editor) | two tasks, three stages

  • V72. Xx HongMeng kernel source code analysis (Shell) | watching the kernel of the application window

Official basic concept

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

  • The process module of the OpenHarmony kernel can provide users with multiple processes, realizing the switch and communication between processes, and helping users manage business program flows. This allows users to focus more on the implementation of business functions.

  • The process in OpenHarmony kernel adopts preemptive scheduling mechanism, supporting time slice rotation scheduling mode and FIFO scheduling mechanism.

  • OpenHarmony kernel processes have a total of 32 priorities (0-31). User processes can configure 22 priorities (10-31), with the highest priority being 10 and the lowest priority being 31.

  • A process with a higher priority can preempt a process with a lower priority. A process with a lower priority can be scheduled only after the process with a higher priority blocks or finishes.

  • Each user-mode process has its own independent process space, invisible to each other, to achieve inter-process isolation.

  • The user root process init is created by the kernel process. All other user processes are fork by the init process.

Description of process status:

  • Init: The process is being created.

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

  • Running: The process is Running.

  • Blocked (Pend) : The process is blocked and suspended. A process is blocked and suspended when all threads in the process are blocked.

  • Zombies: The process stops running and waits for the parent process to reclaim its control block resources.

  • Init – > Ready:

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

  • Ready to Running:

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

  • Running to Pend:

    When all threads in a process are blocked, the process synchronously enters the blocked state when the last thread becomes blocked, and then a process switch occurs.

  • Pend – Ready/Pend – Running:

    When any thread in the blocked process restores to the ready state, the process is added to the ready queue and synchronized to the ready state. If process switchover occurs at this time, the state of the process changes from the ready state to the running state.

  • Ready to Pend:

    When the last ready thread in the process is blocked, the process is removed from the ready list and the process changes from ready to blocked.

  • Running – > Ready:

    A process can change from a running state to a ready state in either of the following ways:

    1. After a process with a higher priority is created or restored, process scheduling occurs. At this moment, the process with the highest priority in the ready list becomes running, and the previously running process changes from running state to ready state.
    2. If a process with a SCHED_RR scheduling policy exists and another process with the same priority is in ready state, that process goes from running to ready state and another process with the same priority goes from ready to running when its time slice runs out.
  • Running – > Zombies:

    When the main thread or all threads of a process finish running, the process changes from running state to zombie state and waits for the parent process to reclaim resources.

Usage scenarios

After a process is created, users can only operate resources in their own process space, but cannot operate resources (except shared resources) of other processes. The user mode allows operations such as process suspension, recovery, and delay. In addition, you can set and obtain the process scheduling priority and policy in user mode. When the process terminates, the process releases its held process resources. However, the held process PID resources must be reclaimed by the parent process through wait/ WaitPID or when the parent process exits.

Start formal analysis

For Uncle Zhang’s story, the processes are those queued up in 32 queues outside the stadium, and those queues are the ready queues of processes.

Note that a process is a resource management unit, not a final scheduler. Who is the scheduler? If yes, check the official state definition

#define OS_PROCESS_STATUS_INIT           0x0010U    // The initial state of the process
#define OS_PROCESS_STATUS_READY          0x0020U    // The process is ready
#define OS_PROCESS_STATUS_RUNNING        0x0040U    // Process running status
#define OS_PROCESS_STATUS_PEND           0x0080U    // The process is blocked
#define OS_PROCESS_STATUS_ZOMBIES        0x100U     // The process is dead
Copy the code

The process from creation to death in the kernel must be extremely complex. In order to facilitate the understanding of the process, the author of the whole series of articles will use Uncle Zhang’s story as an analogy, from the example of life to the mysterious system kernel externalized anatomy out for everyone to see. Something this complex must have a complex structure to support it, LosProcessCB, with long code that must be taken out altogether.


LITE_OS_SEC_BSS LosProcessCB *g_runProcess[LOSCFG_KERNEL_CORE_NUM]; LOSCFG_KERNEL_CORE_NUM is the number of CPU cores
LITE_OS_SEC_BSS LosProcessCB *g_processCBArray = NULL;// Process pool. The maximum number of processes is 64
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_freeProcess;// Records the list of idle processes
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_processRecyleList;// Record the list of recycled processes

typedef struct ProcessCB {
    CHAR                 processName[OS_PCB_NAME_LEN]; /**< Process name */ // Process name
    UINT32               processID;                    /**< process ID = leader thread ID */ // process ID, assigned by the process pool, range [0,64]
    UINT16               processStatus;                /**< [15:4] process Status; [3:0] The number of threads currently running in the process */// The design here is clever. A 16 represents two logical levels of quantities and states. Thumbs up!
    UINT16               priority;                     /**< process priority */ // Process priority
    UINT16               policy;                       /**< process policy */ // Process scheduling mode, preemptive by default
    UINT16               timeSlice;                    /**< Remaining time slice */// Process time slice, default 2 tick
    UINT16               consoleID;                    /**< The console id of task belongs */// The console ID of the task
    UINT16               processMode;                  /**< Kernel Mode:0; User Mode:1; * / // Mode is specified as kernel or user process
    UINT32               parentProcessID;              /**< Parent process ID */ // ID of the parent process
    UINT32               exitCode;                     /**< process exit status */ // Process exit status code
    LOS_DL_LIST          pendList;                     /**< Block list to which the process belongs */ // The blocking list to which the process belongs. If the lock fails, this node is attached to the list
    LOS_DL_LIST          childrenList;                 /**< my children process list */ // The child processes hang here, forming a double-loop linked list
    LOS_DL_LIST          exitChildList;                /**< my exit children process list */ // Those who want to quit the child process and hang here, gray hair to see black hair.
    LOS_DL_LIST          siblingList;                  /**< linkage in my parent's children list */ // All 56 races are from the same parent process.
    ProcessGroup         *group;                       /**< Process group to which a process belongs */ // Owning process group
    LOS_DL_LIST          subordinateGroupList;         /**< linkage in my group list */ // What team processes are available when the process is the group leader
    UINT32               threadGroupID;                /**< Which thread group , is the main thread ID of the process */ // Which thread group is the main thread ID of the process
    UINT32               threadScheduleMap;            /**< The scheduling bitmap table for the thread group of the process */ // Process scheduling bitmap for each thread
    LOS_DL_LIST          threadSiblingList;            /**< List of threads under this process */// List of threads (tasks) for the process
    LOS_DL_LIST          threadPriQueueList[OS_PRIORITY_QUEUE_NUM]; /**< The process's thread group schedules the priority hash table */ // The thread group scheduling priority hash table of the process
    volatile UINT32      threadNumber; /**< Number of threads alive under this process */ // The number of active threads in this process
    UINT32               threadCount;  /**< Total number of threads created under this process */ // The total number of threads created under this process
    LOS_DL_LIST          waitList;     /**< The process holds the waitLits to support wait/waitpid */// Processes hold wait lists to support wait/ waitPID
#if (LOSCFG_KERNEL_SMP == YES)
    UINT32               timerCpu;     /**< CPU core number of this task is delayed or pended */// Count the time each thread is delayed or blocked
#endif
    UINTPTR              sigHandler;   /**< signal handler */ // Signal processing functions, such as SIGSYS
    sigset_t             sigShare;     /**< signal share bit */ // Signals share bits
#if (LOSCFG_KERNEL_LITEIPC == YES)
    ProcIpcInfo         ipcInfo;       /**< memory pool for lite ipc */ // A virtual device file system for interprocess communication. The device mount point is /dev/lite_ipc
#endif
    LosVmSpace          *vmSpace;       /**< VMM space for processes */ // Virtual space, a data structure that describes a process's virtual memory. Linux calls it a memory descriptor
#ifdef LOSCFG_FS_VFS
    struct files_struct *files;        /**< Files held by the process */ // All files held by the process are called the file manager of the process
#endif // Each process has its own file manager, which records operations on files. Note: A file can be manipulated by more than one process
    timer_t             timerID;       /**< iTimer */

#ifdef LOSCFG_SECURITY_CAPABILITY // Security capability
    User                *user;  // Process owner
    UINT32              capability; // The security capability range corresponds to CAP_SETGID
#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;

Copy the code

There are two modes of process, kernel-mode and user-mode, and it can be thought that the main function must create a kernel-mode process with the highest priority, which is KProcess

Run the task command to check the running status of the task. You can see that KProcess is a kernel process created when the system starts. In the figure, you can see that there are more than 10 tasks in KProcess

How is a process module initialized

In Uncle Zhang’s story, KProcess is equivalent to the staff of the venue. They also need to queue up to enter the venue under Uncle Zhang’s dispatch, but their priority is the highest level 0. After entering the venue, they need to complete the preparation work of the venue before opening the business. If you need more than one worker, you need to fork it. Simply say, you need to copy it. The premise of copying is that there needs to be one. So how do users get here? The same is true for those who actually queue. A user ancestor is created, and the rest of the users are fork from the ancestor. Note that user processes and kernel processes have different ancestors. they have their own ancestral roots. G_userInitProcess (number 1) and g_kernelInitProcess(number 2)

/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Concurrent (Concurrent) : run multiple threads in a single core, at the same time a thread running, Parallel. Each thread is allocated to a separate CPU core. Parallel can be implemented by running multiple processes on a single CPU at the same time, or within multiple threads. LITE_OS_SEC_BSS and LITE_OS_SEC_DATA_INIT tell the compiler which data segment to place these global variables in * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
LITE_OS_SEC_BSS LosProcessCB *g_runProcess[LOSCFG_KERNEL_CORE_NUM];// The number of CPU cores exceeds one to achieve parallelism
LITE_OS_SEC_BSS LosProcessCB *g_processCBArray = NULL; // Process pool array
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_freeProcess;g_freeprocessList@note_thinking g_freeprocessList@note_thinking g_freeprocessList@note_thinking
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_processRecyleList;// List of processes that need to be reclaimed
LITE_OS_SEC_BSS UINT32 g_userInitProcess = OS_INVALID_VALUE;// The initial init process in user mode forks other processes in user mode
LITE_OS_SEC_BSS UINT32 g_kernelInitProcess = OS_INVALID_VALUE;// The kernel-state initiates the Kprocess process, which forks other kernel-state processes
LITE_OS_SEC_BSS UINT32 g_kernelIdleProcess = OS_INVALID_VALUE;// Kernel-mode idle process, Kprocess fork
LITE_OS_SEC_BSS UINT32 g_processMaxNum;// Maximum number of processes, 64 by default
LITE_OS_SEC_BSS ProcessGroup *g_processGroup = NULL;// Global process group, responsible for managing all process groups

// The process module is initialized and compiled into code snippet.init
LITE_OS_SEC_TEXT_INIT UINT32 OsProcessInit(VOID)
{
    UINT32 index;
    UINT32 size;

    g_processMaxNum = LOSCFG_BASE_CORE_PROCESS_LIMIT;// 64 processes are supported by default
    size = g_processMaxNum * sizeof(LosProcessCB);// Calculate the total size

    g_processCBArray = (LosProcessCB *)LOS_MemAlloc(m_aucSysMem1, size);// Process pool, occupying the kernel heap, memory pool allocation
    if (g_processCBArray == NULL) {
        return LOS_NOK;
    }
    (VOID)memset_s(g_processCBArray, size, 0, size);// Safe mode reset clear 0

    LOS_ListInit(&g_freeProcess);// Process free list initialization, which is used to request a process descriptor from g_freeProcess when creating a process
    LOS_ListInit(&g_processRecyleList);// The process recycle list is initialized, and when the recycle is complete, the process enters g_freeProcess to wait for it to be used again

    for (index = 0; index < g_processMaxNum; index++) {// Process pool is created in a loop
        g_processCBArray[index].processID = index;// Process ID[0-g_processMaxNum] specifies the process ID
        g_processCBArray[index].processStatus = OS_PROCESS_FLAG_UNUSED;// The default is a blank sheet of paper labeled unused
        LOS_ListTailInsert(&g_freeProcess, &g_processCBArray[index].pendList);OS_PCB_FROM_PENDLIST specifies the process entity to be found.
    }

    g_userInitProcess = 1; /* 1: The root process ID of the user-mode process is fixed at 1 */// User mode root process
    LOS_ListDelete(&g_processCBArray[g_userInitProcess].pendList);// Clear the g_userInitProcess pend list

    g_kernelInitProcess = 2; /* 2: The root process ID of the kernel-mode process is fixed at 2 */// Kernel mode root process
    LOS_ListDelete(&g_processCBArray[g_kernelInitProcess].pendList);// Clear the g_kernelInitProcess pend list

    return LOS_OK;
}

Copy the code

The code is clear enough to create a pool of 64 processes by default, which is a maximum of 64 processes without modifying the macro LOSCFG_BASE_CORE_PROCESS_LIMIT. However, two processes are occupied first, one user and one kernel, and they are the root of the subsequent process creation. So there are only 62 processes left outside to create at most, and the last two root processes of the code have their task blocking list cleared, since no blocking tasks have to be cleared.

Kernel root process creation process

Create the “Kprocess” process, i.e., process 2 in the thread pool g_kernelInitProcess, with the highest priority set to 0

// Initialize process 2, the kernel root process
LITE_OS_SEC_TEXT_INIT UINT32 OsKernelInitProcess(VOID)
{
    LosProcessCB *processCB = NULL;
    UINT32 ret;

    ret = OsProcessInit(a);// Initializes all variables of the process module to create a bidirectional list of each cycle
    if(ret ! = LOS_OK) {return ret;
    }

    processCB = OS_PCB_FROM_PID(g_kernelInitProcess);// Get a process in PID mode
    ret = OsProcessCreateInit(processCB, OS_KERNEL_MODE, "KProcess".0);// The initial process has the highest priority of 0. There are a total of 32 priorities (0-31), including 0-9 for kernel processes, and 22 for user processes.
    if(ret ! = LOS_OK) {return ret;
    }

    processCB->processStatus &= ~OS_PROCESS_STATUS_INIT;// Set the process initialization bit to 1
    g_processGroup = processCB->group;// The global process group refers to the process group in which KProcess resides
    LOS_ListInit(&g_processGroup->groupList);// Process group list initializes
    OsCurrProcessSet(processCB);// Set to the current process

    return OsCreateIdleProcess(a);// Create an idle process
}

// Create a process named "KIdle" to be used when the CPU is free
STATIC UINT32 OsCreateIdleProcess(VOID)
{
    UINT32 ret;
    CHAR *idleName = "Idle";
    LosProcessCB *idleProcess = NULL;
    Percpu *perCpu = OsPercpuGet(a); UINT32 *idleTaskID = &perCpu->idleTaskID;// Get the IDLE task of the CPU

    ret = OsCreateResourceFreeTask(a);// Create a resource reclamation task with priority 5 to reclaim resources when the process exits
    if(ret ! = LOS_OK) {return ret;
    }
 // Create a process named "KIdle" and create a idle task. When the CPU is idle, it stays in idle task and waits to be woken up
    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;// Returns the process ID

    idleProcess = OS_PCB_FROM_PID(g_kernelIdleProcess);// Get the process entity by ID
    *idleTaskID = idleProcess->threadGroupID;// Bind CPU IdleTask, or change CPU idle tasks
    OS_TCB_FROM_TID(*idleTaskID)->taskStatus |= OS_TASK_FLAG_SYSTEM_TASK;// Set Idle task to a system task
#if (LOSCFG_KERNEL_SMP == YES)
    OS_TCB_FROM_TID(*idleTaskID)->cpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());// Multi-core CPU task assignment, to prevent random string, note that multi-core will have parallel processing
#endif
    (VOID)memset_s(OS_TCB_FROM_TID(*idleTaskID)->taskName, OS_TCB_NAME_LEN, 0, OS_TCB_NAME_LEN);// Task name is 0
    (VOID)memcpy_s(OS_TCB_FROM_TID(*idleTaskID)->taskName, OS_TCB_NAME_LEN, idleName, strlen(idleName));// The task name is idle
    return LOS_OK;
}

Copy the code

User root process creation process

Create Init process g_userInitProcess, thread pool 1, priority 28

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

// All user processes use the same user code segment descriptor and user data segment descriptor, which are __USER_CS and __USER_DS. That is, each process in user mode has the same value in its CS register and DS register. When any process or interrupt exception enters the kernel, the same kernel code segment descriptors and kernel data segment descriptors are used, which are __KERNEL_CS and __KERNEL_DS. It is important to remember that the kernel data segment is actually the kernel stack segment.
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;// Start position of code area, all processes
    CHAR *userInitBssStart = (CHAR *)&__user_init_bss;// Uninitialized data area (BSS). Change its value at run time
    CHAR *userInitEnd = (CHAR *)&__user_init_end;// End address
    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);// Initializes the user process, which will be the parent process of all applications
    if(ret ! = LOS_OK) {return ret;
    }

    userText = LOS_PhysPagesAllocContiguous(initSize >> PAGE_SHIFT);// Allocate contiguous physical pages
    if (userText == NULL) {
        ret = LOS_NOK;
        goto ERROR;
    }

    (VOID)memcpy_s(userText, initSize, (VOID *)&__user_init_load_addr, initSize);__user_init_load_addr -> userText
    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);// Mapping between virtual addresses and physical addresses
    if (ret < 0) {
        goto ERROR;
    }

    (VOID)memset_s((VOID *)((UINTPTR)userText + userInitBssStart - userInitTextStart), initBssSize, 0, initBssSize);// Clear all but the code segment

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

    param.pfnTaskEntry = (TSK_ENTRY_FUNC)userInitTextStart;// Execution starts in the code area, where the application main function is located
    param.userParam.userSP = (UINTPTR)stack + size;// point to the top of the stack
    param.userParam.userMapBase = (UINTPTR)stack;/ / the bottom of the stack
    param.userParam.userMapSize = size;/ / stack size
    param.uwResved = OS_TASK_FLAG_PTHREAD_JOIN;// Joinable can be reclaimed and killed by other threads
    ret = OsUserInitProcessStart(g_userInitProcess, &param);// Create a task to run main
    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);// Free physical memory blocks
    OsDeInitPCB(processCB);// Delete the PCB block
    return ret;
}
Copy the code

Intensive reading of the kernel source code

Four code stores synchronous annotation kernel source code, >> view the Gitee repository

Analysis of 100 blogs. Dig deep into the core

Add comments to hongmeng kernel source code process, sort out the following article. Content based on the source code, often in life scene analogy as much as possible into the kernel knowledge of a scene, with a pictorial sense, easy to understand memory. It’s important to speak in a way that others can understand! The 100 blogs are by no means a bunch of ridiculously difficult concepts being put forward by Baidu. That’s not interesting. More hope to make the kernel become lifelike, feel more intimate. It’s hard, it’s hard, but there’s no turning back. 😛 and code bugs need to be constantly debug, there will be many mistakes and omissions in the article and annotation content, please forgive, but will be repeatedly amended, continuous update. Xx represents the number of modifications, refined, concise and comprehensive, and strive to create high-quality content.

Compile build The fundamental tools Loading operation Process management
Compile environment

The build process

Environment script

Build tools

Designed.the gn application

Ninja ninja

Two-way linked list

Bitmap management

In the stack way

The timer

Atomic operation

Time management

The ELF format

The ELF parsing

Static link

relocation

Process image

Process management

Process concept

Fork

Special process

Process recycling

Signal production

Signal consumption

Shell editor

Shell parsing

Process of communication Memory management Ins and outs Task management
spinlocks

The mutex

Process of communication

A semaphore

Incident control

The message queue

Memory allocation

Memory management

Memory assembly

The memory mapping

Rules of memory

Physical memory

Total directory

Scheduling the story

Main memory slave

The source code comments

Source structure

Static site

The clock task

Task scheduling

Task management

The scheduling queue

Scheduling mechanism

Thread concept

Concurrent parallel

The system calls

Task switching

The file system Hardware architecture
File concept

The file system

The index node

Mount the directory

Root file system

Character device

VFS

File handle

Pipeline file

Compilation basis

Assembly and the cords

Working mode

register

Anomaly over

Assembly summary

Interrupt switch

Interrupt concept

Interrupt management

HongMeng station | into a little bit every day, the original is not easy, welcome to reprint, please indicate the source.