syscall_、do_xxxx——基本上都是中断调用的函数
1.内核的销毁流程
1.1 exit是销毁函数——一个系统调用——do_exit
首先该函数会释放进程的代码段和数据段占用的内存
1.2 关闭进程打开的所有文件,对当前的目录和inode进行同步(文件操作)
1.3 如果当前要销毁的进程有子进程,那么就让1号进程作为新的父进程(init进程)
1.4 如果当前进程是一个会话头进程,则会终止会话中的所有进程
1.5 改变当前进程的运行状态,变成TASK_ZOMBIE僵死状态,并且向其父进程发送SIGCHLD信号
2.1 父进程在运行子进程的时候,一般都会运行wait、waitpid这两个函数(父进程等待某个子进程终止的)
当父进程收到SIGCHLD信号时父进程会终止僵死状态的子进程
2.2 首先父进程会把子进程的运行时间累加到自己的进程变量中
2.3 把对应的子进程的进程描述结构体进行释放,置空任务数组中的空槽
void release(struct task_struct * p)
完成清空了任务描述表中的对应进程表项,释放对应的内存页(代码段、数据段、堆栈)
static inline int send_sig(long sig, struct task_struct * p, int priv)
给指定的p进程发送对应的sig信号
static void kill_session(void)
终止会话,终止当前进程的会话,给其发送SIGHUP
int sys_kill(int pid, int sig)
kill -不是杀死的意思,向对应的进程号或者进程组号发送任何信号
pid(
pid > 0 给对应的pid发送sig
pid = 0 给当前进程的进程组发送sig
pid = -1 给任何进程发送
pid < -1 给进程组号为-pid的进程组发送信号
)
// 如果pid=0,那么信号就会被发送给当前进程的进程组中的所有进程。
if (!pid)
while (--p > &FIRST_TASK)
{
if (*p && (*p)->pgrp == current->pid)
if (err = send_sig (sig, *p, 1))
retval = err;
}
// 如果pid 值>0,则信号被发送给pid。
else if (pid > 0)
while (--p > &FIRST_TASK)
{
if (*p && (*p)->pid == pid)
if (err = send_sig (sig, *p, 0))
retval = err;
}
// 如果pid=-1,则信号sig 就会发送给除第一个进程外的所有进程。
else if (pid == -1)
while (--p > &FIRST_TASK)
if (err = send_sig (sig, *p, 0))
retval = err;
// 如果pid < -1,则信号sig 将发送给进程组-pid 的所有进程。
else
while (--p > &FIRST_TASK)
if (*p && (*p)->pgrp == -pid)
if (err = send_sig (sig, *p, 0))
retval = err;
子进程向父进程发送SIGCHLD信号
static void tell_father(int pid)
int do_exit(long code)(重要!)
current->pid就是当前关闭的进程
程序退出处理程序。在系统调用的中断处理程序中被调用。
int do_exit (long code) // code 是错误码。
{
int i;
// 1.释放当前进程代码段和数据段所占的内存页(free_page_tables()在mm/memory.c,105 行)。
free_page_tables (get_base (current->ldt[1]), get_limit (0x0f));
free_page_tables (get_base (current->ldt[2]), get_limit (0x17));
// 2.如果当前进程有子进程,就将子进程的father 置为1(其父进程改为进程1)。如果该子进程已经
// 处于僵死(ZOMBIE)状态,则向进程1 发送子进程终止信号SIGCHLD。
for (i = 0; i < NR_TASKS; i++)
if (task[i] && task[i]->father == current->pid)
{
task[i]->father = 1;
if (task[i]->state == TASK_ZOMBIE)
/* assumption task[1] is always init */
(void) send_sig (SIGCHLD, task[1], 1);
}
// 3.关闭当前进程打开着的所有文件。
for (i = 0; i < NR_OPEN; i++)
if (current->filp[i])
sys_close (i);
// 4.对当前进程工作目录pwd、根目录root 以及运行程序的i 节点进行同步操作,并分别置空。
iput (current->pwd);
current->pwd = NULL;
iput (current->root);
current->root = NULL;
iput (current->executable);
current->executable = NULL;
// 5.如果当前进程是领头(leader)进程并且其有控制的终端,则释放该终端。
if (current->leader && current->tty >= 0)
tty_table[current->tty].pgrp = 0;
// 6.如果当前进程上次使用过协处理器,则将last_task_used_math 置空。
if (last_task_used_math == current)
last_task_used_math = NULL;
// 7.如果当前进程是leader 进程,则终止所有相关进程。
if (current->leader)
kill_session ();
// 8.把当前进程置为僵死状态,并设置退出码。
current->state = TASK_ZOMBIE;
current->exit_code = code;
// 9.通知父进程,也即向父进程发送信号SIGCHLD -- 子进程将停止或终止。
tell_father (current->father);
// 10.重新调度进程的运行。
schedule ();
return (-1); /* just to suppress warnings */
}
2.1 父进程在运行子进程的时候,一般都会运行wait、waitpid这两个函数(父进程等待某个子进程终止的)
当父进程收到SIGCHLD信号时父进程会终止僵死状态的子进程
2.2 首先父进程会把子进程的运行时间累加到自己的进程变量中
int sys_waitpid (pid_t pid, unsigned long *stat_addr, int options)
current->cutime += (*p)->utime; // 更新当前进程的子进程用户
current->cstime += (*p)->stime; // 态和核心态运行时间。
2.3 把对应的子进程的进程描述结构体进行释放,置空任务数组中的空槽
release (*p); // 释放该子进程。