Fork Process 2 and Switch to Process 2 to Execute

Một phần của tài liệu The art of linux kernel design (Trang 202 - 213)

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.

Một phần của tài liệu The art of linux kernel design (Trang 202 - 213)

Tải bản đầy đủ (PDF)

(524 trang)