日期:2014-05-16  浏览次数:20792 次

linux进程解析--进程的退出及销毁
一进程的退出:
当一个进程运行完毕或者因为触发系统异常而退出时,最终会调用到内核中的函数do_exit(),在do_exit()函数中会清理一些进程使用的文件描述符,会释放掉进程用户态使用的相关的物理内存,清理页表,同时进程会调整其子进程的父子关系,会根据实际的情况向父进程发送SIG_CHLD信号。
下面是经过简化的内核代码,去掉了一些不用太关注的东西。
fastcall NORET_TYPE void do_exit(long code)
{
struct task_struct *tsk = current;
int group_dead;


//设置进程的状态为pf_exiting
tsk->flags |= PF_EXITING;
//从定时器队列中删除该进程
del_timer_sync(&tsk->real_timer);
//当前进程需要被trace相应的exit,将该进程的exit事件通过信号
//发送给父进程,也就是trace 进程
if (unlikely(current->ptrace & PT_TRACE_EXIT)) {
current->ptrace_message = code;
ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP);
}


//下面的几个exit是将进程同各个描述符分离,主要有内存描述符,信号量,文件描述符,文件系统,命名空间,若相关描述符不再有任何进程使用,会释放掉,后面会分析一下__exit_mm()和__exit_files()函数
__exit_mm(tsk);
exit_sem(tsk);
__exit_files(tsk);
__exit_fs(tsk);
exit_namespace(tsk);
exit_thread();
exit_keys(tsk);


if (group_dead && tsk->signal->leader)
disassociate_ctty(1);
//exit_code中存放进程的退出码
tsk->exit_code = code;
//调整进程子进程的父子关系,向相关进程发出SIG_CHLD信号
exit_notify(tsk);
//进程处于zombie或者dead状态,在此调用schedule,该进程就永远回不来了^O^
schedule();
for (;;) ;
}


static inline void __exit_mm(struct task_struct * tsk)
{
struct mm_struct *mm = tsk->mm;
//对于vfork来说,父进程会等待,直到子进程退出,在这里唤醒父进程
mm_release(tsk, mm);
if (!mm)
return;
/* more a memory barrier than a real lock */
task_lock(tsk);
//将进程的内存描述符设为空
tsk->mm = NULL;
up_read(&mm->mmap_sem);
//使当前的cpu进入懒惰tlb模式
enter_lazy_tlb(mm, current);
task_unlock(tsk);
//在这里真正的去释放内存描述符及相关所属资源
mmput(mm);
}
void mmput(struct mm_struct *mm)
{
//在多线程的情况下,可能多个线程会共享同一个进程描述符,mm_users就是指明了有多少个线程正在使用该描述符
if (atomic_dec_and_test(&mm->mm_users)) {
exit_aio(mm);
//没有进程使用该内存描述符了,应该可以释放掉该内存描述符所描述的一些进程用户态空间内存,并释放掉所有的vm_area_struct
exit_mmap(mm);
if (!list_empty(&mm->mmlist)) {