4. Creation and Execution of Process 2 165
4.2 Fork Process 2 and Switch to Process 2 to Execute
Next, process 1 calls the function fork and creates process 2.
The code is as follows:
The process of mapping fork to sys_fork is similar to the procedure introduced in Section 3.1.1, that is, to call the function _find_empty_process to find free task item in task[64] for process 2 and call the function copy_process to copy the process.
ROM BIOS and VGA
Kernel
Enable interrupt The page that task_struct of process 1 resides task_struct of process 1
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
sys_open
file_table[64]
Duplicate file handle filp[20]
Process status
Process 0 Process 1
Interruptible Ready
Current process
Figure 4.10 Duplicate the filp[fd] and open std output device again.
The code is as follows:
An instance of finding a new free item for process 2 in task[64] is shown in Figure 4.11.
In copy_process, the kernel applies a free page for task_struct, the kernel stack of process 2, and copies task_struct. The task_struct of process 2 is specifically set, including the settings of every register, management of memory page, shared files, GDT table item, and so on. The process is similar to the process introduced in Section 3.1 where process 0 creates process 1.
//code path:kernel/system_call.s:
……
.align 2 _sys_execve:
lea EIP(%esp),%eax pushl%eax
call _do_execve addl $4,%esp ret
.align 2 _sys_fork:
call _find_empty_process //find free item in task[64] for //process 2 and set new process pid testl%eax,%eax
js 1f push%gs pushl%esi pushl%edi pushl%ebp pushl%eax
call _copy_process//copy process 2 addl $20,%esp
1: ret
_hd_interrupt:
pushl%eax pushl%ecx pushl%edx push%ds push%es push%fs
movl $0x10,%eax mov%ax,%ds mov%ax,%es movl $0x17,%eax mov%ax,%fs movb $0x20,%al
outb%al,$0xA0 # EOI to interrupt controller #1 jmp 1f # give port chance to breathe
……
The code is as follows:
//code path:kernel/system_call.s:
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) {
struct task_struct *p;
int i;
struct file *f;
p = (struct task_struct *) get_free_page(); //apply page for //process 2 if (!p)
return -EAGAIN;
task[nr] = p; //to ensure address pointer of task_struct of //process 2 is loaded into specified location in //task
ROM BIOS and VGA
Kernel
Enable interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
task[64]
The process slot applied for process 2 Process status
Process 0 Process 1
Interruptible Ready
Current process
Figure 4.11 Process 1 begins to fork process 2.
*p = *current; /* NOTE! this doesn’t copy the supervisor stack
*/ //copy task_struct
p->state = TASK_UNINTERRUPTIBLE;//set the process 2 as //uninterruptible state p->pid = last_pid; //personalize setting of the process 2 p->father = current->pid;
p->counter = p->priority;
p->signal = 0;
p->alarm = 0;
p->leader = 0; /* process leadership doesn’t inherit */
p->utime = p->stime = 0;
p->cutime = p->cstime = 0;
p->start_time = jiffies;
p->tss.back_link = 0;
p->tss.esp0 = PAGE_SIZE + (long) p;
p->tss.ss0 = 0x10;
p->tss.eip = eip;
p->tss.eflags = eflags;
p->tss.eax = 0;
p->tss.ecx = ecx;
p->tss.edx = edx;
p->tss.ebx = ebx;
p->tss.esp = esp;
p->tss.ebp = ebp;
p->tss.esi = esi;
p->tss.edi = edi;
p->tss.es = es & 0xffff;
p->tss.cs = cs & 0xffff;
p->tss.ss = ss & 0xffff;
p->tss.ds = ds & 0xffff;
p->tss.fs = fs & 0xffff;
p->tss.gs = gs & 0xffff;
p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = 0x80000000;
if (last_task_used_math == current)
__asm__(“clts ; fnsave%0”::”m” (p->tss.i387));
if (copy_mem(nr,p)) {//set up paging management of process 2 task[nr] = NULL;
free_page((long) p);
return -EAGAIN;
}
for (i = 0; i<NR_OPEN;i++)//The following is that the process //2 shares files of process 1 if (f=p->filp[i])
f->f_count++;
if (current->pwd)
current->pwd->i_count++;
if (current->root)
current->root->i_count++;
if (current->executable)
current->executable->i_count++;
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));//set entry of //process 2 in GDT table
The situation involving copy process and some specific settings is shown in Figure 4.12.
The situation involving copy page tables and setting page directory entry for process 2 is shown in Figure 4.13.
Figure 4.14 shows the situation concerning the adjusting process that process 2 shares files with process 1.
After creating process 2, fork() returns and the return value is 2. Thus, the value of
!(pid = fork()) is false (introduced in Section 3.1.7) and the calling wait(). Its function is as follows: if process 1 has a child process in the state of waiting for exit, prepare to exit this process; if process 1 has a child process not in the state of waiting for exit, switch the process; if the process has no child process, the function returns.
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->state = TASK_RUNNING; /* do this last, just in case */
//set process in ready state return last_pid;
}
ROM BIOS and VGA
Kernel
Enable interrupt task_struct
of process 2 task_struct
of process 1
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
Task[64]
Step1: hook up
Step 2: copy the task_struct of process 1 to process 2
Step 3: update every management member of new process Process status
Process 0 Process 1 Process 2
Uninterruptible Interruptible Ready
Current process
Figure 4.12 Copy task_struct of process 1 and adjust it for process 2.
The code is as follows:
//code path:init/main.c:
void init(void) {
int pid,i;
setup((void *) &drive_info);
(void) open(“/dev/tty0”,O_RDWR,0);
(void) dup(0);
(void) dup(0);
printf(“%d buffers =%d bytes buffer space\n\r”,NR_BUFFERS, NR_BUFFERS*BLOCK_SIZE);
printf(“Free mem:%d bytes\n\r”,memory_end-main_memory_start);
if (!(pid = fork())) { //the following is code of process 2 close(0);
if (open(“/etc/rc”,O_RDONLY,0)) _exit(1);
execve(“/bin/sh”,argv_rc,envp_rc);
_exit(2);
}
if (pid>0)
while (pid !=wait(&i)) //process 1 waits for exit of child process, //switch to process 2 finally.
/* nothing */;
……
}
ROM BIOS and VGA
Kernel
Enable interrupt The page table
of process 1
0 16 32 48 0 159 1023 0 159 1023
The page table of process 2
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Page directory
Set page directory item
copy Process status
Process 0 Process 1 Process 2
Interruptible Ready Uninterruptible
Current process
Figure 4.13 Copy the page table and set the page directory for process 2.
The function waiting is finally mapped to the system calling function sys_waitpid().
The mapping system is similar to that of the mapping fork() to sys_fork(). The function sys_waitpid() first checks all the processes to find out which process is a child process of process 1. Because process 1 creates a child process only, process 2 is chosen.
The code is as follows:
//code path:kernel/exit.c:
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) //wait() // corresponds to system call function sys_waitpid() {
int flag, code;
struct task_struct ** p;
verify_area(stat_addr,4);
repeat:
flag = 0;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if (!*p || *p == current)
continue;
Kernel
ROM BIOS
and VGA Enable
interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
The page that task_struct of process 1 resides
task_struct of process 1
file_table[64] Update some data of process 1 such as current directory
P rocess status +1
f_mode f_flags f_count f_inode f_pos
Process 0 Process 1 Process 2
Interruptible Ready Ready
Current process
Figure 4.14 Add the count of sharing file in file_table[64].
An instance of searching for a child process of process 1 is shown in Figure 4.15.
Next, the system analyzes process 2 to make sure that process 2 is not prepare to exit, thus setting flag bit as 1. This flag will lead to the process switching.
The code is as follows:
if ((*p)->father != current->pid) continue;
//select current process, namely the child process of process 1, now, //it is process 2
if (pid>0) {
if ((*p)->pid != pid) continue;
} else if (!pid) {
if ((*p)->pgrp != current->pgrp) continue;
} else if (pid != –1) { if ((*p)->pgrp != –pid)
continue;
}
switch ((*p)->state) {//judge state of process 2
case TASK_STOPPED://if process 2 is in stopping state, handle here if (!(options & WUNTRACED))
continue;
put_fs_long(0x7f,stat_addr);
return (*p)->pid;
case TASK_ZOMBIE://if process is in zombie state, handle here current->cutime += (*p)->utime;
current->cstime += (*p)->stime;
flag = (*p)->pid;
code = (*p)->exit_code;
release(*p);
put_fs_long(code,stat_addr);
return flag;
default: //if process 2 is in ready state,handle here and set //flag as 1 to jump out of hoop
flag=1;
continue;
} }
……
}
//code path:kernel/exit.c:
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) //wait() corresponds to system call function sys_waitpid() {
int flag, code;
struct task_struct ** p;
verify_area(stat_addr,4);
repeat:
flag=0;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if (!*p || *p == current)
According to the status of process 2, we set the flag, as shown in Figure 4.16.
continue;
if ((*p)->father != current->pid) continue;
//select current process, namely, the child process of process 1, //now, it is process 2
if (pid>0) {
if ((*p)->pid != pid) continue;
} else if (!pid) {
if ((*p)->pgrp != current->pgrp) continue;
} else if (pid != -1) { if ((*p)->pgrp != -pid)
continue;
}
switch ((*p)->state) {//judge state of process 2
case TASK_STOPPED://if process 2 is in stopping state, //handle here
if (!(options & WUNTRACED)) continue;
put_fs_long(0x7f,stat_addr);
return (*p)->pid;
case TASK_ZOMBIE: //if process 2 is in zombie state, //handle here
current->cutime += (*p)->utime;
current->cstime += (*p)->stime;
flag = (*p)->pid;
code = (*p)->exit_code;
release(*p);
put_fs_long(code,stat_addr);
return flag;
default: //if process 2 is in ready state,handle here and //set flag as 1 to jump out of hoop
flag = 1;
continue;
} }
……
}
ROM BIOS and VGA
Kernel
Enable interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Kernel code area Kernel data area
task[64]
Search right child process and find process 2 Process status
Process 0 Process 1 Process 2
Interruptible Ready Ready
Current process
Figure 4.15 Find the child process of process 1.
Kernel
Enable interrupt The page that task_struct of
process 1 resides
task_struct of process 1
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Process status
Process 0 Process 1 Process 2
Interruptible Ready Ready
set flag to be 1
ROM BIOS and VGA
Current process
Figure 4.16 Adjust the status of process 2.
Execute if (flag), first, the kernel sets the status of process 1 as interruptible, and then call the schedule function to switch to process 2 because only process 2 is ready, the pro- cess of schedule has been introduced in Section 3.2. The code is as follows:
The procedure on how to switch to process 2 is shown in Figure 4.17.
//The code path:kernel/exit.c:
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) {
……
switch ((*p)->state) { case TASK_STOPPED:
if (!(options & WUNTRACED)) continue;
put_fs_long(0x7f,stat_addr);
return (*p)->pid;
case TASK_ZOMBIE:
current->cutime += (*p)->utime;
current->cstime += (*p)->stime;
flag = (*p)->pid;
code = (*p)->exit_code;
release(*p);
put_fs_long(code,stat_addr);
return flag;
default:
flag = 1;
continue;
} }
if (flag) {
if (options & WNOHANG) return 0;
current->state=TASK_INTERRUPTIBLE;//set the state of process 1 as //interruptible, because there is no child process of process 1 prepare to exit.
schedule();//switch to process 2
if (!(current->signal &= ~(1<<(SIGCHLD-1)))) goto repeat;
else
return -EINTR;
}
return -ECHILD;
}
Kernel
Enable interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
Process status
Process 0 Process 1 Process 2
Interruptible Interruptible Ready
Current process
ROM BIOS and VGA
Figure 4.17 Switch to process 2.