3. Creation and Execution of Process 1 85
3.1.7 Process 1 Is in Ready State to Complete the Creation of
When process 1 is set as ready, it participates in process scheduling and returns pid 1.
Please notice the progress bar representing the process in the middle of Figure 3.10.
Process 1 is in ready state. The code is as follows:
current->executable->i_count++;
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); //set item in gdt,referring to //sched.c
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->state = TASK_RUNNING; /* do this last, just in case */ //set child process as //state of ready
……
}
ROM BIOS and VGA
Kernel
Enable interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area task_struct of process 1
The page that task_struct of process 1 resides
GDT
LDT Process status TSS
Process 0 Process 1
Ready Ready
Current process
Kernel code area Kernel data area
The page that task_struct of process 1 resides
GDT
After hooking process 1 to GDT Before hooking process 1 to GDT
Figure 3.10 Hook managing structure of process 1 to GDT.
Now, we have completed the creation of process 1, and process 1 now has all the abilities of process 0.
After creating process 1, the function copy_process has been executed. Then, return to the line next to call_copy_process in sys_fork. The code is as follows:
We need to clear the values of five registers pushed in sys_fork, that is, the first five parameters of copy_process(): gs, esi, edi, ebp, and eax. Note that eax corresponds to the first parameter nr of copy_process(), which is the return value last_pid of copy_
process() and pid of process 1. Then, return to the line pushl%eax next to call _sys_call_
table(,%eax,4) in _system_call to execute.
First, inspect whether the current process is process 0 or not. Notice that the line pushl%eax pushes the pid of process 1 and executes at the line _ret_from_sys_call:.
The code is as follows:
//Code path:kernel/fork.c:
int copy_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) {
……
p->state = TASK_RUNNING; /* do this last, just in case */ //set child process as //ready state
return last_pid;
}
//Code path:kernel/system_call.s:
……
_sys_fork:
call _find_empty_process testl%eax,%eax
js 1f push%gs pushl%esi pushl%edi pushl%ebp pushl%eax call _copy_process
addl $20,%esp //copy_process returns to this line,and esp+ = 20 means clear //20-byte stack
//that is the value of gs, esi, edi, ebp and eax pushed before 1: ret //Notice that there remains data in kernel
//stack, and return
//to pushl%eax in _system_call to execute
……
//Code path:kernel/system_call.s:
……
_system_call:
……
call _sys_call_table(,%eax,4)
pushl%eax #sys_fork returns here. eax is return value last_pid
#of copy_process()
movl _current,%eax #current process is Process 0 cmpl $0,state(%eax) # state
jne reschedule #if Process is not in ready state, schedule process cmpl $0,counter(%eax) # counter
Because process 0 is the current process, jump to label 3 to revert the values of all registers pushed before. The process of clearing stack in init_task is shown in the first step of Figure 3.11. Note that the line popl%eax means reverting the pid of process 1 mentioned before to the value of eax, which is 1, in the CPU.
Next, the interruption iret returns. The CPU hardware automatically popped values of ss, esp, eflags, cs, and eip pushed when int 0x80 happens to the corresponding registers in the CPU. The execution changes from kernel code in privilege level 0 to process 0 code in privilege level 3. cs:eip points to the line if(_res> = 0) next to line int 0x80 in fork() to execute.
The code is as follows:
Before analyzing how to execute if(_res> = 0), let us look at “ = a” (__res), which assigns the value of _res to eax. The line if(_res> = 0) is used to judge the value of eax. As introduced recently, the value of eax is pid 1 of process 1 and return(type)_res returns as 1.
je reschedule #if Process has no time slice, schedule process ret_from_sys_call:
movl _current,%eax # task[0] cannot have signals cmpl _task,%eax
je 3f #if Process 0 is current process, jump to 3: below cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
jne 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? jne 3f
movl signal(%eax),%ebx movl blocked(%eax),%ecx notl%ecx
andl%ebx,%ecx bsfl%ecx,%ecx je 3f btrl%ecx,%ebx movl%ebx,signal(%eax) incl%ecx
pushl%ecx call _do_signal popl%eax
3: popl%eax #pop values of 7 registers to CPU popl%ebx
popl%ecx popl%edx pop%fs pop%es pop%ds
iret #CPU hardware pops values of ss,esp,eflags,cs and eip pushed
#when int 0x80 happens to
#corresponding registers in CPU
…… # cs:eip points to the line if(_res> = 0) next to line int 0x80
#in fork() to execute
//Code path:include/unistd.h:
int fork(void) {
long __res;
__asm__ volatile (“int $0x80”
: “ = a” (__res) //value of __res is eax and returning value last_
//pid(1) of copy_process() : “0” (__NR_ fork));
if (__res > = 0) //execute here after iret. _res is eax and the value //is 1
return (int) __res; //return to 1 errno = -__res;
return -1;
}
Process 0 returns to call site if(!fork()) of the function fork to execute, because !1 is false. Then, process 0 continues to execute and comes to the line for(;;)pause().
The code is as follows:
The above process is shown in the second step of Figure 3.11.
//Code path:init/main.c:
……
void main(void) {
sti();
move_to_user_mode();
if (!fork()) { //the returning value of fork is 1,so if(!1) is //false/* we count on this going ok */
init(); //this line isn’t executed }
……
for(;;) pause(); //execute this line }
Kernel
Enable interrupt ROM BIOS
and VGA
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
Main if(!fork()) for(;;)
Clear stack Process status
Process 0 Process 1
Ready Ready
Current process
After creating process 1 Before creating process 1 task_struct of
process 0
init_task
init_task
Figure 3.11 How the OS distinguishes between process 0 and process 1.