今天完成了我 Linux 进程学习计划中的最后一个内容:进程的终止。
先了解一下当我们或者系统终止一个进程的时候, Linux0.11 内核是怎么处理的。当我们在程序中要退出这个程序的时候,会直接或者间接的调用 exit() 这个 C 库函数,这个库函数实际上执行 linux 中 sys_exit() 的系统调用,这个系统调用会调用内核的 do_exit() 函 数完成大部分资源的释放、与此进程相关进程的处理,将此进程置成僵死状态,最后向父进程发送进程终止信号,并执行进程的调度;当这个父进程在某个时候系统 调用返回的时候,会发现自己收到了进程终止的信号,就会释放这个子进程的进程数据结构,完成进程的完全释放。当然,父进程如果调用了 waitpid ()函数,也会完成子进程的完全释放。下面我就具体说一下 do_exit() 的具体工作过程:
1) 释放代码段和数据段所占的内存。
2) 处理其子进程:将子进程的父进程改为 init 进程,并判断如果子进程已经处于僵死状态,则向 init 进程(这些进程的新父进程)发送子进程终止信号。也就是说。
3) 释放 pwd 、 root 、 i 节点。
4) 如果当前进程是会话头:释放终端;挂起该会话中的进程。
5) 将当前进程置为僵死状态。
6) 向父进程发送子进程终止信号。
7) 执行 schedule()
在这里涉及到了两个函数,一个是 6 )中向父进程发送子进程终止信号的函数: tell_father() ;另一个是 4 )中挂起会话中进程的函数: kill_session() 。 Tell_father() 函数做的就是将当前进程的父进程的信号中的 sigchld (子进程终止信号)位置位。 Kill_session() 做的就是,扫描系统的所有任务,如果哪个任务的 session 与当前进程的 session 相同,就将这个进程的的 sighup 信号位置位。
在这里我还要介绍一下也是与进程释放相关的系统调用: waitpid() 。这个函数的主要功能是挂起当前进程,直到它所等待的进程终止或者僵死了,或者它需要调用某个信号句柄。它的参数 pid 指明了它等待的进程号,这个进程号不但可以指定哪个进程,还可以指定某些进程。大体工作过程是这样的:
1) 扫描系统的任务数组,当发现与传入的 pid 与扫描到的进程相符(不一定相同)的时候,就会做一下处理:
判断这进程的状态,如果是停止转状态,再根据传入的另一个参数 option 判断是立即返回还是继续扫描;如果是僵死状态,就释放该进程的任务数据结构 task_struct ;
2 )如果扫描结束后,发现等待的进程没有处在停止或者僵死状态的,就根据 option 判断是返回还是继续等待。如果是继续等待则做一下工作:将此进程置位可中断的睡眠状态,并执行进程调度程序 shedule (),在 schedulue 之后是当此进程被重新执行的时候的处理:如果自己除了 sigchld 这外没收到其它信号(是否需要调度信号处理句柄),就转向开头进行再一次的扫描,如果发现此时收到了信号,就返回系统调用中断出错码,用户针对这个出错号应该再继续调用本函数。
以上就是我对今晨终止的理解。至此,我对 Linux0.11 核进程部分的学习告一段落了,下面我打算开始内存管理的学习。