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.