Linux0.11 kernel -- fork process analysis, linux0.11 kernel -- fork

Source: Internet
Author: User

Linux0.11 kernel -- fork process analysis, linux0.11 kernel -- fork

[All Rights Reserved. For more information, see the source. Source: http://www.cnblogs.com/joey-hua/p/5597818.html]

It is said that the fork sub-process in the Android app can prevent the app from being killed. The general principle is that the sub-process will send signals to the parent process if it is killed.

First, the fork () function is a system call. In sys. h:

Extern int sys_fork (); // create a process. (Kernel/system_call.s, 208) // The system call function pointer table. Used to call the interrupt handler (int 0x80) as a jump table. Fn_ptr sys_call_table [] = {sys_setup, sys_exit, sys_fork ,...}

The previous article has made a detailed analysis of system calls, in main. c:

static inline _syscall0 (int, fork)

Bind _ NR_fork, that is, the sys_fork function of the above array, in system_call.s:

#### Sys_fork () call is used to create a sub-process, which is system_call function 2. The original form is in include/linux/sys. h. # First call the C function find_empty_process () to obtain a process ID pid. If a negative number is returned, the current task array # is full. Then call copy_process () to copy the process .. Align 2_sys_fork: call _ find_empty_process # call find_empty_process () (kernel/fork. c, 135 ). Testl % eax, % eaxjs 1 fpush % gspushl % esipushl % edipushl % ebppushl % eaxcall _ copy_process # Call the C function copy_process () (kernel/fork. c, 68 ). Addl $20, % esp # discard all the pressure stack content here. 1: ret

First, call find_empty_process to find the number not used in the task array. In fork. c:

// Obtain the non-duplicate process ID last_pid for the new process and return the task id (array index) in the task array ). Int find_empty_process (void) {int I; repeat: // If last_pid increases by 1 and exceeds its positive number range, the pid Number is used again from 1. If (++ last_pid) <0) last_pid = 1; // In the task array, search for whether the set pid has been used by any task. If yes, obtain a new pid Number. For (I = 0; I <NR_TASKS; I ++) if (task [I] & task [I]-> pid = last_pid) goto repeat; // find an idle item for the new task in the task array and return the item number. Last_pid is a global variable and does not need to be returned. For (I = 1; I <NR_TASKS; I ++) // task 0 is excluded. If (! Task [I]) return I; // if all the 64 items in the task array are occupied, the return code is returned. Return-EAGAIN ;}

This function is easy to understand. Next we can see that the find_empty_process return value is saved in eax. If it is a negative number, sys_fork is directly thrown out. Otherwise, a bunch of commands are pushed as the parameter of copy_process, which is also in fork. c:

/** OK. The following is the main fork subprogram. It copies system process information (task [n]) and sets necessary registers. * It also copies the entire data segment. * /// Copy the process. // The nr parameter indicates the task array item number allocated by calling find_empty_process. None is the return address that is pushed into the stack when calling // sys_call_table in system_call.s. Intcopy_process (int nr, long ebp, long edi, long esi, long gs, long none, long ebx, long ecx, long edx, long fs, long es, long ds, long eip, long cs, long eflags, long esp, long ss) {struct task_struct * p; int I; struct file * f; p = (struct task_struct *) get_free_page (); // allocate memory for the new task data structure. If (! P) // if an error occurs in memory allocation, the error code is returned and the system exits. Return-EAGAIN; task [nr] = p; // Add the new task structure pointer to the task array. // Where nr is the task number, which is returned by find_empty_process. * P = * current;/* NOTE! This doesn' t copy the supervisor stack * // * Note! In this way, the superuser stack is not copied * // (only the content of the current process is copied ). P-> state = TASK_UNINTERRUPTIBLE; // set the status of the new process to the non-interrupted waiting state. P-> pid = last_pid; // The new process ID. Obtained by calling find_empty_process. P-> father = current-> pid; // set the parent process number. P-> counter = p-> priority; p-> signal = 0; // set the bitmap to 0. P-> alarm = 0; // alarm timing value (number of tick answers ). P-> leader = 0;/* process leadership doesn' t inherit * // * the process's leadership cannot be inherited */p-> utime = p-> stime = 0; // initialize the user State time and core state time. P-> cutime = p-> cstime = 0; // initialize the subprocess user State and core state time. P-> start_time = jiffies; // The current answer time. // Set the data required by TSS in the task Status section (see the description after the list ). P-> tss. back_link = 0; // because the new memory is allocated to the task structure p, esp0 points to the top of the page. Ss0: esp0 is used as the stack when the program is executed in kernel state. P-> tss. esp0 = PAGE_SIZE + (long) p; // kernel state Stack pointer (because 1 page is allocated to task structure p // new memory, so esp0 points to the top of the page ). P-> tss. ss0 = 0x10; // The stack segment selector (same as the kernel data segment) [?]. P-> tss. eip = eip; // command code pointer. P-> tss. eflags = eflags; // The flag register. P-> tss. eax = 0; // This is why the new process returns 0 when fork () is returned. P-> tss. ecx = ecx; p-> tss. edx = edx; p-> tss. ebx = ebx; p-> tss. esp = esp; // The new process completely copies the stack content of the parent process. Therefore, the stack of task0 p-> tss. ebp = ebp; // is "clean ". P-> tss. esi = esi; p-> tss. edi = edi; p-> tss. es = es & 0 xffff; // The segment register is valid only for 16 bits. P-> tss. cs = cs & 0 xffff; p-> tss. ss = ss & 0 xffff; p-> tss. ds = ds & 0 xffff; p-> tss. fs = fs & 0 xffff; p-> tss. gs = gs & 0 xffff; p-> tss. ldt = _ LDT (nr); // set the selection operator of the Local Descriptor Table of the new task (LDT descriptor in GDT ). P-> tss. trace_bitmap = 0x80000000; // (16-bit high ). // If the current task uses a coprocessor, save the context. The Assembly command clts is used to clear the task // exchanged (TS) flag in the control register CR0. This flag is set for the CPU whenever a task is switched. This flag is used to manage the mathematical coprocessor: If // this flag is set, each ESC command will be captured. If the coprocessor has a flag and is also set to a bit, the // WAIT command will be captured. Therefore, if a task switchover occurs after an ESC command starts to be executed, the content in the coprocessor may need to be saved before executing the new ESC command. The error handling handle saves the content of the coprocessor and resets the TS flag. // The command fnsave is used to save all the states of the coprocessor to the memory area specified by the destination operand (tss. i387 ). If (last_task_used_math = current) _ asm _ ("clts; fnsave % 0": "m" (p-> tss. i387); // set the code and Data Segment Base Address, length limit for the new task, and copy the page table. If an error occurs (the returned value is not 0), reset the corresponding items in the task array and release the Memory Page allocated for the new task. If (copy_mem (nr, p) {// if the returned value is not 0, an error occurs. Task [nr] = NULL; free_page (long) p); return-EAGAIN;} // if a file in the parent process is opened, the number of opened files increases by 1. For (I = 0; I <NR_OPEN; I ++) if (f = p-> filp [I]) f-> f_count ++; // increase the number of pwd, root, and executable references of the current process (parent process) by 1. If (current-> pwd) current-> pwd-> I _count ++; if (current-> root) current-> root-> I _count ++; if (current-> executable) current-> executable-> I _count ++; // set the TSS and LDT descriptor items of the new task in GDT, and the data is retrieved from the task structure. // During task switching, the task register tr is automatically loaded by the CPU. Set_tss_desc (gdt + (nr <1) + FIRST_TSS_ENTRY, & (p-> tss); set_ldt_desc (gdt + (nr <1) + FIRST_LDT_ENTRY, & (p-> ldt); p-> state = TASK_RUNNING;/* do this last, just in case * // * and then set the new task to a runable state, in case of */return last_pid; // return the new process number (which is different from the task number ).}

If you have any questions, please note that copy_process has so many parameters that sys_fork pushes five registers. This is because the system_call function called before sys_fork is called according to the system call mechanism, A stack of registers has been pushed into the stack.

FirstAllocate memory for the new task data structure (note that the data structure is not the task itself), Get_free_page is stored in the following memory Management Article. fork functions are closely related to memory management memory. c. Here, you only need to know that this function is to get a free page of the main memory area and return the address of this page.

The next step is better understood. Copy the process descriptor of the current process to the new task and assign a value to each attribute. It is worth noting that p-> father = current-> pid indicates that the parent process of the new task is the current process.

Next, set esp0 to point to the top of the newly allocated page memory.Ss0 is the child of the kernel data segment. Because the base address in the kernel data segment descriptor is 0, ss0: esp0 is used as the stack for program execution in the kernel state.

Next, p-> tss. ldt = _ LDT (nr); set the index number of ldt, that is, the Selection Sub-of LDT in GDT.

The following is the most critical function copy_mem:

// Set the code and Data Segment Base Address and limit for the new task, and copy the page table. // Nr is the new task number; p is the pointer to the data structure of the new task. Intcopy_mem (int nr, struct task_struct * p) {unsigned long old_data_base, new_data_base, data_limit; unsigned long old_code_base, new_code_base, code_limit; // obtain the segment length (in bytes) of the descriptor in the Local Descriptor Table of the current process ). Code_limit = get_limit (0x0f); // The length of the middle section of the descriptor in the partial Descriptor Table. Data_limit = get_limit (0x17); // The length of the middle section of the data segment descriptor in the Local Descriptor Table. // Obtain the base address of the current process code segment and data segment in the linear address space. Old_code_base = get_base (current-> ldt [1]); // obtain the base address of the original code segment. Old_data_base = get_base (current-> ldt [2]); // obtain the base address of the original data segment. If (old_data_base! = Old_code_base) // version 0.11 does not support code and Data Segment separation. Panic ("We don't support separate I & D"); if (data_limit <code_limit) // if the length of the Data Segment <segment length is incorrect. Panic ("Bad data_limit"); // The base address of the new process in the linear address space is equal to 64 MB * its task number. New_data_base = new_code_base = nr * 0x4000000; // new base address = Task Number * 64 Mb (Task size ). P-> start_code = new_code_base; // you can specify the base address in the middle descriptor of the Local Descriptor Table of the new process. Set_base (p-> ldt [1], new_code_base); // you can specify the base domain in the code snippet descriptor. Set_base (p-> ldt [2], new_data_base); // you can specify the base address field in the data segment descriptor. // Set the page Directory and page table items for the new process. That is, the linear address Memory Page of the new process is mapped to the actual physical address Memory Page. If (copy_page_tables (old_data_base, new_data_base, data_limit) {// copy the code and data segment. Free_page_tables (new_data_base, data_limit); // release the applied memory if an error occurs. Return-ENOMEM;} return 0 ;}

First, take the limit length in the Code and data segment descriptor in the Local Descriptor Table (LDT's own Descriptor Table), in sched. h:

// The length of the segment selected by segment. // % 0-length of stored segments (number of bytes); % 1-segment selection operator segment. # Define get_limit (segment) ({\ unsigned long _ limit; \__ asm _ ("lsll % 1, % 0 \ n \ tincl % 0 ": "= r" (_ limit): "r" (segment); \__ limit ;})

Because there is

Struct desc_struct ldt [3]; // struct desc_struct ldt [3] local table descriptor of this task. 0-null, 1-code segment cs, 2-data and stack segment ds & ss.

This indicates the LDT Descriptor Table itself. The first descriptor is null, and the second descriptor is 8-15 bytes, because the 0-15 bits of the descriptor are the segment length, therefore, 0x0f is used, and the third descriptor, that is, 16-23 bytes, is the data segment. Therefore, 0x17 is used.

Next we will take the base address of the ldt code segment of the current process:

// Obtain the base address of the segment from the descriptor of the address addr. The function is the opposite of _ set_base. // Edx-storage base address (_ base); % 1-address addr offset 2; % 2-address addr offset 4; % 3-addr offset 7. # Define _ get_base (addr) ({\ unsigned long _ base; \__ asm _ ("movb % 3, % dh \ n \ t "\ // take the high 8-bit (31-24) of the 16-bit base address at [addr + 7 )?? Dh. "Movb % 2, % dl \ n \ t" \ // get the low 8 bits (23-16 bits) of the 16-bit base address at [addr + 4 )?? Dl. "Shll $16, % edx \ n \ t" // The base address height is 16 to the Top 16 position in edx. "Movw % 1, % dx" \ // get the 16-bit low base address (15-0) at [addr + 2 )?? Dx. : "= D" (_ base) \ // The edx contains a 32-bit segment base address. : "M" (* (addr) + 2), "m" (* (addr) + 4), "m" (* (addr) + 7) ;__ base ;}// obtain the base address of the segment descriptor referred to by ldt in the Local Descriptor Table. # Define get_base (ldt) _ get_base (char *) & (ldt )))

Current-> ldt [1] is the content of the ldt code segment descriptor of the current process, so it is easy to understand that it is to extract the base address from the content of the descriptor.

Next, set the base address of the linear address of the new process, linusDivide each program (process) into 64 MB virtual memory spaceThe new base address is the task number * 64 MB.

The next step is to set the base address to the segment descriptor in the LDT table of the new process. The principle is similar.

Copy_page_tables and free_page_tables will be explained later.

The last step is to set the TSS and LDT descriptor items for the new task, which is explained in the initialization of process scheduling.

The new process number is returned.

Now the fork function analysis is complete.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.