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:
- 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.
- 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, ¶m);// 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.