进程调用exit()会终结当前进程,可以显式调用,
也可以隐式: c语言main函数结束时编译器会自动加入exit调用

exit是系统调用,对应内核里的sys_exit() -> do_exit()

fastcall NORET_TYPE void do_exit(long code)
{
struct task_struct *tsk = current;
int group_dead; profile_task_exit(tsk); WARN_ON(atomic_read(&tsk->fs_excl)); if (unlikely(in_interrupt()))
panic("Aiee, killing interrupt handler!");
if (unlikely(!tsk->pid))
panic("Attempted to kill the idle task!"); if (unlikely(current->ptrace & PT_TRACE_EXIT)) {
current->ptrace_message = code;
ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP);
} /*
* We're taking recursive faults here in do_exit. Safest is to just
* leave this task alone and wait for reboot.
*/
if (unlikely(tsk->flags & PF_EXITING)) {
printk(KERN_ALERT
"Fixing recursive fault but reboot is needed!\n");
/*
* We can do this unlocked here. The futex code uses
* this flag just to verify whether the pi state
* cleanup has been done or not. In the worst case it
* loops once more. We pretend that the cleanup was
* done as there is no way to return. Either the
* OWNER_DIED bit is set by now or we push the blocked
* task into the wait for ever nirwana as well.
*/
tsk->flags |= PF_EXITPIDONE;
if (tsk->io_context)
exit_io_context();
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
} tsk->flags |= PF_EXITING; //更改标志位, 表示进程正在退出
/*
* tsk->flags are checked in the futex code to protect against
* an exiting task cleaning up the robust pi futexes.
*/
smp_mb();
spin_unlock_wait(&tsk->pi_lock); if (unlikely(in_atomic()))
printk(KERN_INFO "note: %s[%d] exited with preempt_count %d\n",
current->comm, task_pid_nr(current),
preempt_count()); acct_update_integrals(tsk);
if (tsk->mm) {
update_hiwater_rss(tsk->mm);
update_hiwater_vm(tsk->mm);
}
group_dead = atomic_dec_and_test(&tsk->signal->live);
if (group_dead) {
exit_child_reaper(tsk);
hrtimer_cancel(&tsk->signal->real_timer);
exit_itimers(tsk->signal);
}
acct_collect(code, group_dead);
#ifdef CONFIG_FUTEX
if (unlikely(tsk->robust_list))
exit_robust_list(tsk);
#ifdef CONFIG_COMPAT
if (unlikely(tsk->compat_robust_list))
compat_exit_robust_list(tsk);
#endif
#endif
if (group_dead)
tty_audit_exit();
if (unlikely(tsk->audit_context))
audit_free(tsk); tsk->exit_code = code;
taskstats_exit(tsk, group_dead); exit_mm(tsk); //释放 mm_struct if (group_dead)
acct_process();
//释放各种资源
exit_sem(tsk);
__exit_files(tsk);
__exit_fs(tsk);
check_stack_usage();
exit_thread();
cgroup_exit(tsk, 1);
exit_keys(tsk); if (group_dead && tsk->signal->leader)
disassociate_ctty(1); //减少模块的引用计数
module_put(task_thread_info(tsk)->exec_domain->module);
if (tsk->binfmt)
module_put(tsk->binfmt->module); proc_exit_connector(tsk);
//1 更新进程的亲属关系, 在进程组里为子进程重新找parent,如果找不到,则设parent为init
//2 给父进程发送相应的信号
exit_notify(tsk);
#ifdef CONFIG_NUMA
mpol_free(tsk->mempolicy);
tsk->mempolicy = NULL;
#endif
#ifdef CONFIG_FUTEX
/*
* This must happen late, after the PID is not
* hashed anymore:
*/
if (unlikely(!list_empty(&tsk->pi_state_list)))
exit_pi_state_list(tsk);
if (unlikely(current->pi_state_cache))
kfree(current->pi_state_cache);
#endif
/*
* Make sure we are holding no locks:
*/
debug_check_no_locks_held(tsk);
/*
* We can do this unlocked here. The futex code uses this flag
* just to verify whether the pi state cleanup has been done
* or not. In the worst case it loops once more.
*/
tsk->flags |= PF_EXITPIDONE; //进程退出已经完成了,设置PF_EXITPIDONE if (tsk->io_context)
exit_io_context(); if (tsk->splice_pipe)
__free_pipe_info(tsk->splice_pipe); preempt_disable();
/* causes final put_task_struct in finish_task_switch(). */
tsk->state = TASK_DEAD; //设置进程的状态为TASK_DEAD schedule(); ////调度另一个进程运行
BUG();
/* Avoid "noreturn function does return". */
for (;;)
cpu_relax(); /* For when BUG is null */
}

  

总结:
    当子进程终结时,它会通知父进程,并清空自己所占据的内存,并在内核里留下自己的退出信息(exit code,如果顺利运行,为0;如果有错误或异常状况,为>0的整数)。
    在这个信息里,会解释该进程为什么退出。父进程在得知子进程终结时,有责任对该子进程使用wait系统调用。这个wait函数能从内核中取出子进程的退出信息,并清空该信息在内核中所占据的空间(内核栈, thread_info, task_struct)。 但是,如果父进程早于子进程终结,子进程就会成为一个孤儿(orphand)进程。孤儿进程会被过继给init进程,init进程也就成了该进程的父进程。init进程负责该子进程终结时调用wait函数。
    当然,一个糟糕的程序也完全可能造成子进程的退出信息滞留在内核中的状况(父进程不对子进程调用wait函数),这样的情况下,子进程成为僵尸(zombie)进程。当大量僵尸进程积累时,内存空间会被挤占。

最新文章

  1. java 打印流(PrintStream)
  2. salesforce 零基础学习(三十五) 通过Process Builder和Approval Processes锁定记录(Lock Record)
  3. 跨越千年的RSA算法
  4. Order Independent Transparency
  5. 转:Docker学习---挂载本地目录
  6. 更换光纤后路由器端口映射 -VPN相关
  7. Pinyin Comparison 拼音辨别 V1.1.2
  8. c 判断水仙花数,质数(素数)
  9. leetcode[89] Merge Sorted Array
  10. bash元字符(上)
  11. XAF-Domain Components 技术 使用接口来定义ORM业务对象
  12. WPF中利用控件的DataContext属性为多个TextBox绑定数据
  13. Problem A: STL——灵活的线性表
  14. 极路由hosts
  15. delphi 中record 的类操作符重载简介
  16. jdk 10.0.2 bug修复
  17. [转]Apache 配置虚拟主机三种方式
  18. (转)android媒体--stagefright概述【一】
  19. gdb 调试带参数程序
  20. [iOS笔试600题]二、常识篇(共有72题)

热门文章

  1. Maven中央仓库地址大全,Maven中央仓库配置示例
  2. springmvc:常用注解
  3. uwsgi: invalid option -- &#39;x&#39;
  4. TZ_10_spring-sucrity 服务器和页面的权限控制
  5. Vue-cli3.x在开发环境中(router采用 history模式)出现Failed to resolve async component default: Error: Loading chunk {/d} failed.或者Uncaught SyntaxError: Unexpected token &lt;错误
  6. Java review-basic4
  7. 【大数据】Hadoop常用启动命令
  8. ssh连接超时中断问题解决方案
  9. 2019-9-2-win10-uwp-保存用户选择文件夹
  10. 洛谷 P1951 收费站_NOI导刊2009提高(2) 最短路+二分