Switch to the Shell Process

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

4. Creation and Execution of Process 2 165

4.4 The System Gets to the Idle State

4.4.2 Switch to the Shell Process

As introduced in Section 4.4.1, the shell process has executed the first command in the rc file and created the process update. It will now execute the second command echo “/dev/

hd1/” >/etc/mtab and write string “/dev/hd1/” to the “/etc/mtab” file in the Ramdisk. After

\0\0\0 ROM BIOS

and VGA

Kernel

Enable interrupt

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Process status

Load/etc/rc to buffer block

Process 0 Process 1 Process 2(shell)

Interruptible Interruptible Ready

Current process

Figure 4.40 Load the “/etc/rc” file into the buffer.

Kernel

ROM BIOS

and VGA Enable

interrupt

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Kernel code area Kernel data area

The page that update function locates

Analyze instruction

The position of update process Task[64]

Process status

Process 0 Process 1 Process 2(shell) Process (update)

Interruptible Interruptible Ready Ready

Current process

Figure 4.41 The status of the update process.

ROM BIOS and VGA

Kernel

Enable interrupt

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Process status

Process 0 Process 1 Process 2(shell) Process (update)

Interruptible Interruptible Ready Interruptible

Current process Suspend

Figure 4.42 Changes in process state.

that, the shell program will continue to read the rc file. The read() function corresponds to the system call sys_read(). The code is as follows:

As the “/etc/rc” file is a common file, its return value should be -ERROR accordingly (the specific steps of reading a file will be introduced in Chapter 5). This return value will result in the exiting of the shell process, and the corresponding system call function is sys_exit(). The code is as follows:

After entering the function do_exit(), it starts to prepare for the exit of the shell pro- cess. The code is as follows:

The process of releasing the pages is shown in Figure 4.43.

//The code path:fs/read_write.c:

int sys_read(unsigned int fd,char * buf,int count) {

if (inode->i_pipe)//read the pipe file

return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;

if (S_ISCHR(inode->i_mode))//read the character device file

return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);

if (S_ISBLK(inode->i_mode))//read the block device file

return block_read(inode->i_zone[0],&file->f_pos,buf,count);

if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {//read the //common file if (count+file->f_pos > inode->i_size)

count=inode->i_size - file->f_pos;

if (count<=0) return 0;

return file_read(inode,file,buf,count);

}

printk(“(Read)inode->i_mode =%06o\n\r”,inode->i_mode);

return -EINVAL;

}

//The code path:kernel/exit.c:

int sys_exit(int error_code) {

return do_exit((error_code&0xff)<<8);

}

//The code path:kernel/exit.c:

int do_exit(long code) {

int i;

free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));

free_page_tables(get_base(current->ldt[2]),get_limit(0x17));//free the pages //that the code segment and data segment occupied of process shell for (i=0 ; i<NR_TASKS ; i++)//detect whether the process shell has a child

//process

if (task[i] && task[i]->father = = current->pid) {

The relationship of the shell process and other processes, files, terminals, and so on, as well as sending signal to the father process, is shown in Figure 4.44.

task[i]->father = 1;//before the exit of process shell, set the father //process of process update to process 1

if (task[i]->state = = TASK_ZOMBIE)//if the child process is in the //zombie state, then send termination signal /* assumption task[1] is always init */

(void) send_sig(SIGCHLD, task[1], 1);

}

for (i = 0 ; i<NR_OPEN ; i++)//the following is remove the relationship of //process shell with other process, files, //terminals, etc.

if (current->filp[i]) sys_close(i);

iput(current->pwd);

current->pwd = NULL;

iput(current->root);

current->root = NULL;

iput(current->executable);

current->executable = NULL;

if (current->leader && current->tty >=0) tty_table[current->tty].pgrp=0;

if (last_task_used_math==current) last_task_used_math = NULL;

if (current->leader) kill_session();

current->state = TASK_ZOMBIE;//set the current process to zombie state current->exit_code = code;

tell_father(current->father); //send signal to process 1, tell it the //process shell will exit

schedule(); //switch the process

return (-1); /* just to suppress warnings */

}

Kernel

ROM BIOS

and VGA Enable

interrupt

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Process status

Release the page and page table occupied by shell process

Process 0 Process 1 Process 2(shell) Process (update)

Interruptible Interruptible Ready Interruptible

Current process

Figure 4.43 Free the page and page table owned by the shell process.

The execution of the function tell_father and schedule is worth noting. In the func- tion tell_father, it will send the SIGCHLD signal to process 1 to notify it that a child pro- cess will exit. The code is as follows:

//The code path:kernel/exit.c:

static void tell_father(int pid)//notify the father process that the child //process will exit

{

int i;

if (pid)

for (i=0;i<NR_TASKS;i++) { if (!task[i])

continue;

if (task[i]->pid ! = pid) continue;

task[i]->signal | = (1<<(SIGCHLD-1));//send SIGCHLD //signal to the //father process return;

}

/* if we don’t find any fathers, we just release ourselves */

/* This is not really OK. Must change it to make father 1 */

printk(“BAD BAD - no father found\n\r”);

release(current);

} Kernel

ROM BIOS

and VGA Enable

interrupt

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

task_struct of

shell process task_struct of

process 1

The page that task_struct of process 1 resides

The page that task_struct of shell process resides

Set the file-related field

Signal bitmap

Send the signal of

"child processes exit" to process 1 Process status

Process 0 Process 1 Process 2(shell) Process (update)

Interruptible Interruptible Zombie Interruptible

Current process

Figure 4.44 Treatment after the shell process exits.

After the function tell_father(), the system will call the function schedule() to prepare to switch the process; the detection of the signal affects the process switch. The code is as follows:

As introduced in Section 4.2, when executing the function sys_waitpid(), process 1 calls schedule() and switches to process 2. When it switches to process 1, it will continue

//The code path:kernel/sched.c:

void schedule(void) {

int i,next,c;

struct task_struct ** p;

/* check alarm, wake up any interruptible tasks that have got a signal */

for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)//traverse all //processes if (*p) {

if ((*p)->alarm && (*p)->alarm < jiffies) { (*p)->signal | = (1<<(SIGALRM-1));

(*p)->alarm = 0;

}

if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&

(*p)->state==TASK_INTERRUPTIBLE)//the process 1 has //received the signal and //its state is interruptible (*p)->state=TASK_RUNNING;//set the process 1 to

//ready state }

/* this is the scheduler proper: */

while (1) { c = -1;

next = 0;

i = NR_TASKS;

p = &task[NR_TASKS];

while (-- i) { if (!*-- p)

continue;

if ((*p)->state==TASK_RUNNING && (*p)->counter > c) c = (*p)->counter, next = i;//find that only

//process 1 is in the //ready state

}

if (c) break;

for(p = &LAST_TASK ; p > &FIRST_TASK ;-- p) if (*p)

(*p)->counter = ((*p)->counter >> 1) + (*p)->priority;

}

switch_to(next);//decide to switch to the process 1 }

to follow the schedule() and eventually return to the function sys_waitpid (for more infor- mation, see Section 3.2). The code is as follows:

It is worth noting that the SIGCHLD signal that process 1 received was sent by the function tell_father(). The function sys_waitpid() continues to execute; at this time, there is a child process exit and it needs to be dealt with. The code is as follows:

//The code path:kernel/exit.c:

int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) //the system call function corresponding to wait()

{

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;

if ((*p)->father !=current->pid) continue;

……

}

if (flag) {

if (options & WNOHANG) return 0;

current->state=TASK_INTERRUPTIBLE;

schedule();//when complete, return to function sys_waitpid if (!(current->signal &=~(1<<(SIGCHLD-1))))

//receive the SIGCHLD signal, the child process //will exit

goto repeat;//repeat to deal with the child process exit else

return -EINTR;

}

return -ECHILD;

}

//The code path:kernel/exit.c:

int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) {

… repeat:

flag = 0;

for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if (!*p || *p==current)

continue;

if ((*p)->father != current->pid) continue;

……

Figure 4.45 shows an instance of releasing pages of shell’s task_struct.

switch ((*p)->state) { //continue to prepare for the exit of process shell

case TASK_ZOMBIE: //the process shell is in the zombie state current->cutime += (*p)->utime;

current->cstime += (*p)->stime;

flag = (*p)->pid; //record the pid of process 2, that is 2 code = (*p)->exit_code;

release(*p); //free the page occupied by the //task_struct of process 2 put_fs_long(code,stat_addr);

return flag; //return shell’s pid, that is 2

… } }

… }

ROM BIOS and VGA

Kernel

Process status

Process 0 Process 1 Process (update)

Interruptible Ready Interruptible

Current process

Enable interrupt

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Kernel code area Kernel data area

Task[64]

The position of shell process in task[64] is emptied

Release the page occupied by task_struct of shell process, which means this process will disappear forever

Figure 4.45 Process 1 clears the information of the shell process in task[64].

When the function sys_waitpid() is completed, it will return to the function wait() and will ultimately return to the function init(). Process 1 continues to execute. The code is as follows:

What we have introduced in Section 4.2 is worth noting: when process 2 is created, the pid value is 2, and the flag, which is the return value of sys_waitpid(), is also 2. That is, the function wait() returns 2; if the while is false, jump out of the loop.

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

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

(524 trang)