Confucius in the township party, pye Xun such as also, like can not speak. Its in the ancestral temple court, poop speech, only respectfully er. The Analects of Confucius: Township Party chapter
A hundred blog series. This is:
V45. Xx HongMeng kernel source code analysis (Fork) | one call, return twice
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
The first time I saw fork, it was one call and two returns, and I was confused. Because this is enough to change the way we think about functions, function calls can also be played in such a way that the parent process calls once and the parent process returns once. And you can only tell which process is returning by the return value. So there are a couple of questions that are always on my mind.
- What is fork? How to use it correctly externally.
- Why fork? What is the nature and benefit of fork?
- How did you do that? Call fork() to make both parent and child processes return twice. What happened?
- why
pid = 0
Does it represent the return of the child process? Why doesn’t the parent process need to return 0?
I didn’t understand until I looked at the Linux kernel source code, but the positioning of this series is to dig through the kernel source code, so this article will delve into the fork function, using the kernel source code to explain these problems. It is advisable to read the rest of the series before reading this one. Such as (task switch, register, work mode, system call, etc.), with these foundations, it is easy to understand the implementation process of fork.
What is the fork
Let’s start with a snippet of code that’s often mentioned on the web as fork.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t pid;
char *message;
int n;
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(1);
}
if (pid == 0) {
message = "This is the child\n";
n = 6;
} else {
message = "This is the parent\n";
n = 3;
}
for(; n > 0; n--) {
printf(message);
sleep(1);
}
return 0;
}
Copy the code
pid < 0
The fork failurepid == 0
Fork succeeds, which is the return of the child processpid > 0
Fork succeeds, which is the return of the parent processfork
It makes sense to specify the return value of.fork
Returns 0 in the child, which can still be calledgetpid
The function gets its own process ID and can also be calledgetppid
The function gets the ID of the parent process. Used in the parent processgetpid
You can get your own process ID, but to get the child process ID, you can only get the child process IDfork
There is no other way.- The child process did not actually execute
fork()
Instead, the kernel took the return value in a very clever way and forcibly overwrote it to 0fork
The best part of the implementation.
The results
$ ./a.out This is the child This is the parent This is the child This is the parent This is the child This is the parent This is the child $ This is the child This is the childCopy the code
The running process of this program is shown below.
Interpretation of the
-
Fork () is a system call, so it switches to SVC mode. In SVC stack, the parent process copies a child process, and the PCB information of the parent process and child process is the same, as well as the user mode code and data.
-
As can be seen from the execution of the case, the parent process will execute after the fork, i.e. the code pointing (to the PC register) is the same. Fork is actually called only once by the parent process. The child process does not execute fork, but gets a return value pid == 0, which is very important. That is the focus of this illustration.
-
The parent prints This is the parent three times because n = 3. The child prints This is the child six times because n = 6. The subroutine does not execute the following code:
pid_t pid; char *message; int n; Copy the code
The child process starts from pid = fork(). Normally, it will not appear in the new task stack. In fact, it can use these variables successfully later, indicating that the parent process’s current user state data is also copied to the child process’s new task stack.
-
The first instruction to be run by a fork child is PID = 0, where 0 is the return value stored in register R0. Note The task context of the parent process is also copied. When the parent process returns from kernel mode to user mode, the context recovered by the parent process is the same as the task context of the child process, that is, the PC register points to the same, so that the code can be executed at the same location.
-
Sleep (1) is a task that gives up the right to use the CPU and suspends itself in the task waiting list. The parent’s sleep(1) is cut to the child and back again until n = 0 terminates the parent process.
-
But this example and my interpretation only explain what fork is and guess what it does, not why it does it or how the code is implemented. Formal combination of hongmeng source code to say why and how to do these two issues?
Why fork
The fork function is called once and returned twice, once in the parent process and once in the child process. As you can see in the figure above, we started with one control flow, but after the call to fork, we have two control flows, hence the name “fork”. With more than 40 articles written, it’s easy to understand that a program needs resources (memory, files, IPC, monitoring information, etc.) to run, resources need to be managed, and processes are containers for managing resources. These resources are equivalent to the work needs a variety of tools, tools are almost the same, there is no need to go through the process of one application, and the application will be found and others have the same tools, others have directly to use it does not smell? So the easiest way is to identify a father, and ask the father to make a copy of the work tools for you. All you need to do is focus on the task.
How is fork implemented?
/ / the fork system call, suggested to https://gitee.com/weharmony/kernel_liteos_a_note fork? : P
int SysFork(void)
{
returnOsClone (CLONE_SIGHAND,0.0);// The essence is cloning
}
LITE_OS_SEC_TEXT INT32 OsClone(UINT32 flags, UINTPTR sp, UINT32 size)
{
UINT32 cloneFlag = CLONE_PARENT | CLONE_THREAD | CLONE_VFORK | CLONE_VM;
if (flags & (~cloneFlag)) {
PRINT_WARN("Clone dont support some flags! \n");
}
returnOsCopyProcess (cloneFlag & flags,NULL, sp, size); }STATIC INT32 OsCopyProcess(UINT32 flags,constUINTPTR SP, UINT32 Size)
{UINT32 intSave, RET, processID; LosProcessCB *run = OsCurrProcessGet();// Get the current process
LosProcessCB *child = OsGetFreePCB();// Apply for a process control block from the process pool. The default value is 64
if (child == NULL) {
return -LOS_EAGAIN;
}
processID = child->processID;
ret = OsForkInitPCB(flags, child, name, sp, size);// Initialize the process control block
if(ret ! = LOS_OK) {goto ERROR_INIT;
}
ret = OsCopyProcessResources(flags, child, run);// Copy process resources, including virtual space, files, security, IPC ==
if(ret ! = LOS_OK) {gotoERROR_TASK; } ret = OsChildSetProcessGroupAndSched (child, run);// Set the process group and join the process scheduling ready queue
if(ret ! = LOS_OK) {goto ERROR_TASK;
}
LOS_MpSchedule(OS_MP_CPU_ALL);// Send a signal to each CPU to be ready to accept scheduling
if (OS_SCHEDULER_ACTIVE) {// The current CPU core is active
LOS_Schedule();// request scheduling
}
return processID;
ERROR_TASK:
SCHEDULER_LOCK(intSave);
(VOID)OsTaskDeleteUnsafe(OS_TCB_FROM_TID(child->threadGroupID), OS_PRO_EXIT_OK, intSave);
ERROR_INIT:
OsDeInitPCB(child);
return-ret; } # # #OsForkInitPCB
STATIC UINT32 (UINT32 FLAGS, LosProcessCB *child,constUINTPTR SP, UINT32 Size)
{
UINT32 ret;
LosProcessCB *run = OsCurrProcessGet();// Get the current processRet = OsInitPCB(child, run->processMode, OS_PROCESS_PRIORITY_LOWEST, LOS_SCHED_RR, name);// Initialize PCB information, process mode, priority, scheduling mode, name == information
if(ret ! = LOS_OK) {return ret;
}
ret = OsCopyParent(flags, child, run);// Copy your father's genetic information
if(ret ! = LOS_OK) {return ret;
}
return OsCopyTask(flags, child, name, sp, size);// Copy the task, set the task entry function, stack size
}
// Initialize the PCB block
STATIC UINT32 OsInitPCB(LosProcessCB *processCB, UINT32 Mode, UINT16 priority, UINT16 policy,const CHAR *name)
{
UINT32 count;
LosVmSpace *space = NULL;
LosVmPage *vmPage = NULL;
status_t status;
BOOL retVal = FALSE;
processCB->processMode = mode; // User process or kernel process
processCB->processStatus = OS_PROCESS_STATUS_INIT; // The initial state of the process
processCB->parentProcessID = OS_INVALID_VALUE; // Dad process, externally specified
processCB->threadGroupID = OS_INVALID_VALUE; // Belongs to a thread group
processCB->priority = priority; // Process priority
processCB->policy = policy; // Scheduling algorithm LOS_SCHED_RR
processCB->umask = OS_PROCESS_DEFAULT_UMASK; / / mask
processCB->timerID = (timer_t)(UINTPTR)MAX_INVALID_TIMER_VID;
LOS_ListInit(&processCB->threadSiblingList);// Initialize the child task/thread list with the child threads suspended from this fork see OsTaskCBInit LOS_ListTailInsert(&(processCB->threadSiblingList), &(taskCB->threadList));
LOS_ListInit(&processCB->childrenList); OsCopyParent LOS_ListTailInsert(&parentProcessCB->childrenList, &childProcessCB->siblingList);
LOS_ListInit(&processCB->exitChildList); See OsProcessNaturalExit LOS_ListTailInsert(&parentCB->exitChildList, &processCB->siblingList);
LOS_ListInit(&(processCB->waitList)); / / initialize waiting for the task list Hanging above are in a waiting in OsWaitInsertWaitLIstInOrder LOS_ListHeadInsert (& processCB - > waitList, & runTask - > pendList);
for (count = 0; count < OS_PRIORITY_QUEUE_NUM; ++count) { // Create queues based on the number of priorities
LOS_ListInit(&processCB->threadPriQueueList[count]); // Initialize a queue of ready threads/tasks
}/ / in HongMeng kernel task is the thread, in HongMeng series article has detailed source code analysis See https://my.oschina.net/u/3751245
if (OsProcessIsUserMode(processCB)) {// Whether it is a user mode processSpace = LOS_MemAlloc (m_aucSysMem0,sizeof(LosVmSpace));// Allocate a virtual space
if (space == NULL) {
PRINT_ERR("%s %d, alloc space failed\n", __FUNCTION__ __LINE__);return LOS_ENOMEM;
}
VADDR_T *ttb = LOS_PhysPagesAllocContiguous(1);// Allocate a physical page to store the L1 table.
if (ttb == NULL) {// Get the physical page TTB directly
PRINT_ERR("%s %d, alloc ttb or space failed\n", __FUNCTION__ __LINE__); (VOID) LOS_MemFree (m_aucSysMem0, space);returnLOS_ENOMEM; } (VOID)memset_s(TTB, PAGE_SIZE,0The PAGE_SIZE);// The memory is clearRetVal = OsUserVmSpaceInit(space, TTB);// Initialize the virtual space and process MMU
vmPage = OsVmVaddrToPage(ttb);// Get the page from the virtual address
if ((retVal == FALSE) || (vmPage == NULL)) {// Exception handling
PRINT_ERR("create space failed! ret: %d, vmPage: %#x\n", retVal vmPage); processCB->processStatus = OS_PROCESS_FLAG_UNUSED;// The process is unused and clean(VOID) LOS_MemFree (m_aucSysMem0, space);// Free virtual spaceLOS_PhysPagesFreeContiguous (TTB,1);// Free physical page, 4K
return LOS_EAGAIN;
}
processCB->vmSpace = space;// Set it to process virtual spaceLOS_ListAdd (& processCB - > vmSpace - > archMmu. PtList, & (vmPage - > node));// Attach the space mapping page table to the mmU L1 page table of the space. L1 is the header of the table
} else {
processCB->vmSpace = LOS_GetKVmSpace();// The kernel shares a virtual space, and kernel processes reside in memory
}
#ifdef LOSCFG_SECURITY_VID
status = VidMapListInit(processCB);
if(status ! = LOS_OK) { PRINT_ERR("VidMapListInit failed! \n");
return LOS_ENOMEM;
}
#endif
#ifdef LOSCFG_SECURITY_CAPABILITY
OsInitCapability(processCB);
#endif
if(OsSetProcessName (processCB, name)! = LOS_OK) {return LOS_ENOMEM;
}
return LOS_OK;
}
Copy the code
// Copy a Task procedure
STATIC UINT32 OsCopyTask(UINT32 FLAGS, LosProcessCB *childProcessCB,const CHAR *name, UINTPTR entry, UINT32 size)
{
LosTaskCB *childTaskCB = NULL;
TSK_INIT_PARAM_S childPara = { 0 };
UINT32 ret;
UINT32 intSave;
UINT32 taskID;
OsInitCopyTaskParam(childProcessCB, name, entry, size, &childPara);// Initialize Task parametersRet = LOS_TaskCreateOnly(&taskID, &childPara);// Create tasks without scheduling them
if(ret ! = LOS_OK) {if (ret == LOS_ERRNO_TSK_TCB_UNAVAILABLE) {
return LOS_EAGAIN;
}
return LOS_ENOMEM;
}
childTaskCB = OS_TCB_FROM_TID(taskID);// Get the task entity through taskId
childTaskCB->taskStatus = OsCurrTaskGet()->taskStatus;// The task status is synchronized first. Note that this is the assignment operation.... 01101001
if (childTaskCB->taskStatus & OS_TASK_STATUS_RUNNING) {// There can only be one running task, so if it is the same, change the 4 bit
childTaskCB->taskStatus &= ~OS_TASK_STATUS_RUNNING;// Change bit 4 to... 01100001
} else {// What happens in the non-running state?
if (OS_SCHEDULER_ACTIVE) {// The clone thread failed to run due to an error
LOS_Panic("Clone thread status not running error status: 0x%x\n"ChildTaskCB - > taskStatus); } childTaskCB->taskStatus &= ~OS_TASK_STATUS_UNUSED;// Clean Task
childProcessCB->priority = OS_PROCESS_PRIORITY_LOWEST;// Set the process to the lowest priority
}
if (OsProcessIsUserMode(childProcessCB)) {// Is it a user processSCHEDULER_LOCK(intSave); OsUserCloneParentStack (childTaskCB, OsCurrTaskGet ());// Copy the current task context to the new task
SCHEDULER_UNLOCK(intSave);
}
OS_TASK_PRI_QUEUE_ENQUEUE(childProcessCB, childTaskCB);// Add task to the ready queue of the child process
childTaskCB->taskStatus |= OS_TASK_STATUS_READY;// The task status is labeled ready
return LOS_OK;
}
// Clone parent task context to child task
LITE_OS_SEC_TEXT VOID OsUserCloneParentStack(LosTaskCB *childTaskCB, LosTaskCB *parentTaskCB)
{
TaskContext *context = (TaskContext *)childTaskCB->stackPointer;
VOID *cloneStack = (VOID *)(((UINTPTR)parentTaskCB->topOfStack + parentTaskCB->stackSize) - sizeof(TaskContext));
/ / point cloneStack TaskContext
LOS_ASSERT(parentTaskCB->taskStatus & OS_TASK_STATUS_RUNNING);// The current task must be a running task(VOID) memcpy_s (childTaskCB - > stackPointer,sizeof(TaskContext), cloneStack,sizeof(TaskContext));// Copy the task context directly
context->R[0] = 0;Pid = fork() pid == 0
}
Copy the code
Interpretation of the
- The system call is passed
CLONE_SIGHAND
To create a child process. The specific creation methods are as follows:#define CLONE_VM 0x00000100 // The child process runs in the same memory space as the parent process #define CLONE_FS 0x00000200 // The child process shares the same file system as the parent, including root, current directory, and umask #define CLONE_FILES 0x00000400 // The child process shares the same file descriptor table as the parent process #define CLONE_SIGHAND 0x00000800 // The child process shares the same signal handler table as the parent process #define CLONE_PTRACE 0x00002000 If the parent process is traced, the child process is also traced #define CLONE_VFORK 0x00004000 // The parent process is suspended until the child process releases virtual memory resources #define CLONE_PARENT 0x00008000 // The parent of the created child process is the parent of the caller. The new process becomes "brother" rather than "parent" to the process that created it. #define CLONE_THREAD 0x00010000 // Added in Linux 2.4 to support THE POSIX thread standard. Child processes share the same thread group as parent processes Copy the code
Processes send signals to each other for asynchronous communication. There is a special section on signals in this series.
- You can see that the body function of fork is
OsCopyProcess
, first apply for a clean PCB, which is equivalent to applying for a container to hold resources. - Initialize the container
OsForkInitPCB
.OsInitPCB
First clean the container, virtual space, address mapping table (L1 table), all linked list initialization, ready for the following content copy. OsCopyParent
Passing on family genes/relationships to child processes, who your ancestor is, who your sister-in-law is, will be on the list you have initialized.OsCopyTask
It is important to copy the parent’s current task data to the child’s new task. As mentioned in this series, it is the task (thread) that really does the CPU’s work, so the child needs to create a new taskLOS_TaskCreateOnly
To accept the data of the current task, which includes stack data, running code snippet pointing to,OsUserCloneParentStack
Put user – mode context dataTaskContext
Copy to the bottom of the stack of the new task in the child process, that is, the new task is running stack only context data. And it has the single most important line of codecontext->R[0] = 0;
It is mandatory to recover the context in the futureR0
The data in the register is changed to 0, which means that after the scheduling algorithm cuts to the child process’s task, the first thing the task does is restore the contextR0
The value of the register becomes 0, andR0=0
What does it mean? At the same timeLR/SP
The register values are the same as those of the parent process. What does that mean?- The return value is stored in register R0.
A()->B()
, A recognizes only the return value of BR0
The R0 register value is equal to 0, which is equivalent to the return value of 0, and the LR register points to the instructionPid = return values
The SP register records the position in the stack where the calculation starts, thus fully restoring the parent process callfork()
The only difference before the run scene is changedR0
The value of the register, that’s why we have itpid = 0;// return value of fork(), note that the child process does not perform fork(), it just retrieves a return value by restoring the context. if (pid == 0) { message = "This is the child\n"; n = 6; } Copy the code
This ensures that this is the return of the child process. This is a
fork()
The best part. Be sure to understand.OsCopyTask``OsUserCloneParentStack
It’ll blow your mind, and you’ll never forget it. - The return of the parent is
processID = child->processID;
Is the ID of the child process. The ID of any child process cannot be equal to 0, but must be greater than 0 if it succeeds. Failure is a negative numberreturn -ret;
OsCopyProcessResources
Used to assign various resources, including copy virtual space memory, copy open file list, IPC, and so on.OsChildSetProcessGroupAndSched
Set subprocess groups and scheduling preparations, join the scheduling queue, and prepare for scheduling.LOS_MpSchedule
It is an intercore interrupt that sends a scheduling signal to all cpus, causing all cpus to schedule once. The parent cedes CPU usage because the child’s scheduling priority is the same as the parent’s, while the child’s task is already at the head of the ready queueOS_PROCESS_PRI_QUEUE_ENQUEUE
The parent process takes precedence over the parent process, so the next scheduled task is the child process’s task until no other process or task of higher priority appears. That’s what we saw at the beginning of this article$ ./a.out This is the child This is the parent This is the child This is the parent This is the child This is the parent This is the child $ This is the child This is the childCopy the code
- The above is the whole implementation process of fork in the Hongmeng kernel, be sure to combine with the other parts of the series of understanding, once a thorough understanding, never forget.
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.