Kuregaku Shandong Normal University
"Linux kernel Analysis" MOOC course http://mooc.study.163.com/course/USTC-1000029000
First, the experimental process
1. Open GdB and set breakpoints
2. Trace to the Do_fork place
3. Trace to the Copy_process breakpoint.
4. Trace to Ret_from_fork child process creation complete.
Second, the code part of the analysis
The system call code for the fork is in LINUX/ARCH/I386/KERNEL/PROCESS.C:
asmlinkage int sys_fork (struct pt_regs regs) { Return Do_fork (SIGCHLD, regs.esp,®s, 0, NULL, NULL); } |
The Sys_fork system call is implemented by the Do_fork () function, which implements fork,clone,vfork by passing different clone_flags to the Do_fork () function.
The system call codes for Syn_clone and Syn_vfork are as follows:
asmlinkage int Sys_clone (struct pt_regs regs) { unsigned long clone_flags; unsigned long newsp; int __user *parent_tidptr, *child_tidptr; Clone_flags = REGS.EBX; NEWSP = REGS.ECX; parent_tidptr = (int __user *) Regs.edx; child_tidptr = (int __user *) Regs.edi; if (!NEWSP) NEWSP = Regs.esp; Return Do_fork (clone_flags, newsp,®s, 0, Parent_tidptr, child_tidptr); } asmlinkage int sys_vfork (struct pt_regs regs) { Return Do_fork (Clone_vfork | CLONE_VM | SIGCHLD, regs.esp,®s, 0, NULL, NULL); } |
Where Clone_flas is defined in Includelinuxsched.h
/* * Cloning Flags: */ #define Csignal 0X000000FF/* The signal to be passed when the process exits */ #define CLONE_VM 0x00000100/* Parent/child process shared address space */ #define CLONE_FS 0x00000200/* Parent-child process sharingFile SystemInformation */ #define Clone_files 0x00000400/* Parent/child process sharing open files */ #define Clone_sighand 0x00000800/* Parent-child process shared signal processing */ #define CLONE_PTRACE 0x00002000/* Continue debugging child processes */ #define CLONE_VFORK 0x00004000/* Call Vfork (), Parent process Hibernate */ #define Clone_parent 0x00008000/* Set a common parent process */ #define Clone_THREAD0x00010000/* Parent-child process in the sameThreadsGroup */ #define CLONE_NEWNS 0x00020000/* Create a new namespace for the child process */ #define CLONE_SYSVSEM 0x00040000/* Parent-child process sharing system V Sem_undo */ #define CLONE_SETTLS 0x00080000/* Creates a new for the child processTLS*/ #define CLONE_PARENT_SETTID 0x00100000/* Set Parent process TID */ #define CLONE_CHILD_CLEARTID 0x00200000/* Clear child process tid */ #define CLONE_DETACHED 0x00400000/* Unused, ignored */ #define CLONE_UNTRACED 0x00800000/* Not allowed to debug child processes */ #define CLONE_CHILD_SETTID 0x01000000/* Set child process Tid */ #define CLONE_STOPPED 0x02000000/* Set Process stop state */ #define CLONE_NEWUTS 0x04000000/* Create a new utsname group */ #define CLONE_NEWIPC 0x08000000/* Create a newIPC*/ |
Do_fork () is defined in KERNEL/FORK.C with the following code:
/* * Ok, this isThe main fork-routine. * * It copies the process, and if successful kick-starts * It and waits for it to finish using the VM if required. */ Long do_fork (unsigned long clone_flags, unsigned long Stack_start, struct Pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr) { struct Task_struct *p; int trace = 0; struct PID *pid = Alloc_pid (); Long nr; if (!pid) Return-eagain; NR = pid->nr; if (unlikely (Current->ptrace)) { trace = Fork_traceflag (clone_flags); if (trace) Clone_flags |= Clone_ptrace; } p = copy_process (Clone_flags, Stack_start, Regs, Stack_size, Parent_tidptr, Child_tidptr, PID); /* * Do this prior waking up the new thread-the thread pointer * Might get invalid after, if the thread exits quickly. */ if (!is_err (p)) { struct completion vfork; if (Clone_flags & clone_vfork) { P->vfork_done = &vfork; Init_completion (&vfork); } if ((P->ptrace & pt_ptraced) | | (Clone_flags & clone_stopped)) { /* * We'll start with a immediate SIGSTOP. */ Sigaddset (&p->pending.signal, SIGSTOP); Set_tsk_thread_flag (P, tif_sigpending); } if (! ( Clone_flags & clone_stopped)) Wake_up_new_task (P, clone_flags); Else P->state = task_stopped; if (unlikely (trace)) { Current->ptrace_message = nr; Ptrace_notify ((Trace << 8) | SIGTRAP); } if (Clone_flags & clone_vfork) { Freezer_do_not_count (); Wait_for_completion (&vfork); Freezer_count (); if (Unlikely (Current->ptrace & Pt_trace_vfork_done)) { Current->ptrace_message = nr; Ptrace_notify ((ptrace_event_vfork_done << 8) | SIGTRAP); } } } else { Free_pid (PID); NR = Ptr_err (p); } return nr; } |
The core of the Do_fork () function is the copy_process () function, which completes most of the work created by the process and is also defined in FORK.C, and the copy_process function is longer, looking down by paragraph:
The static struct task_struct *copy_process (unsigned long clone_flags, unsigned long Stack_start, struct Pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr, struct PID *pid) { int retval; struct Task_struct *p = NULL; if (Clone_flags & (clone_newns| CLONE_FS) = = (clone_newns| CLONE_FS)) Return Err_ptr (-einval); /* * Thread groups must share signals as well, and detached threads * Can only is started up within the thread group. */ if ((Clone_flags & Clone_thread) &&! ( Clone_flags & Clone_sighand)) Return Err_ptr (-einval); /* * Shared signal handlers imply shared VM. By the above, * Thread groups also imply shared VM. Blocking this case allows * for various simplifications and other code. */ if ((Clone_flags & Clone_sighand) &&! ( Clone_flags & CLONE_VM)) Return Err_ptr (-einval); retval = Security_task_create (clone_flags); if (retval) Goto Fork_out; retval =-enomem; p = dup_task_struct (current); if (!p) Goto Fork_out; Rt_mutex_init_task (P); #ifdef Config_trace_irqflags DEBUG_LOCKS_WARN_ON (!p->hardirqs_enabled); DEBUG_LOCKS_WARN_ON (!p->softirqs_enabled); #endif |
This code first checks the incoming Clone_flag, and then calls the Dup_task_struct () function, which has the primary function of creating a new kernel stack for the child process, copying the task_struct structure and thread_info structure, This is a complete copy of the structure, so the process descriptor of the child process is exactly the same as the parent process. Follow the dup_task_struct () function to see the code:
static struct task_struct *dup_task_struct (struct task_struct *orig) { struct Task_struct *tsk; struct Thread_info *ti; Prepare_to_copy (orig); Tsk = Alloc_task_struct (); if (!tsk) return NULL; TI = Alloc_thread_info (tsk); if (!ti) { Free_task_struct (TSK); return NULL; } *tsk = *orig; Tsk->stack = Ti; Setup_thread_stack (tsk, orig); #ifdef Config_cc_stackprotector Tsk->stack_canary = Get_random_int (); #endif /* One for us, one for whoever does the "Release_task ()" (usually parent) */ Atomic_set (&tsk->usage,2); Atomic_set (&tsk->fs_excl, 0); #ifdef Config_blk_dev_io_trace Tsk->btrace_seq = 0; #endif Tsk->splice_pipe = NULL; return tsk; } |
Iii. summarizing the process of creating a new process for Linux
The system allows a process to create a new process, the new process is a child process, and the child process can also create a new child process, forming a process tree structure model. All processes of the entire Linux system are also a tree-shaped structure. The roots are automatically constructed by the system, which is the NO. 0 process that executes in the kernel state, which is the ancestor of all processes. Created by process number No. 0 (kernel state), number 1th is responsible for performing some kernel initialization and system configuration, and creating several kernel threads for cache and virtual main memory management. The 1th process then calls Execve () to run the executable init, which becomes the user-state process 1th, the init process. It follows the requirements of the configuration file/etc/initab, completes the system to start the work, the creation number is numbered 1th, 2nd number ... A number of terminal registration process Getty. Each Getty process Sets its Process group identification number and monitors the interface lines that are configured to the system terminal. When the connection signal from the terminal is detected, the Getty process will execute the registration program via the function Execve () login, at which time the user can enter the registration name and password to enter the login process, if successful, by the login program through the function execv () execution shell, The shell process receives the PID of the Getty process, replacing the original Getty process. Other processes are generated directly or indirectly by the shell.
The above procedure can be described as: number No. 0 process->1 Kernel process->1 number kernel thread->1 number user process (init process)->getty process->shell Process
Create a trace of the process through the fork function to analyze the creation of the Linux kernel process