linux内核情景分析之exit与Wait

  1. //第一层系统调用
  2. asmlinkage long sys_exit(int error_code)
  3. {
  4. do_exit((error_code&0xff)<<8);
  5. }

其主体是do_exit,接下来我们来看看do_exit的实现

  1. NORET_TYPE void do_exit(long code)
  2. {
  3. struct task_struct *tsk = current;//获取当前进程描述符
  4. if (in_interrupt())//禁止中断时调用do_exit
  5. panic("Aiee, killing interrupt handler!");
  6. if (!tsk->pid)//空转进程也就是0号进程禁止退出
  7. panic("Attempted to kill the idle task!");
  8. if (tsk->pid == 1)//1号进程禁止退出
  9. panic("Attempted to kill init!");
  10. tsk->flags |= PF_EXITING;//退出进程时,设置此标志位
  11. /*
  12. 进程退出时,可能已经设置了实时定时器,real_timer已挂载到内核定时器队列,
  13. 现在进程要退出,没必要存在了,就把当前进程从定时器队列中脱离出来
  14. */
  15. del_timer_sync(&tsk->real_timer);
  16. fake_volatile:
  17. #ifdef CONFIG_BSD_PROCESS_ACCT
  18. acct_process(code);
  19. #endif
  20. /*如果是指针共享,那就只是减少mm->mm_users,
  21. 如果有独立的进程空间,那就直接释放页表,mm_struct,vm_struct
  22. 以及所有的vma*/
  23. __exit_mm(tsk);
  24. //加锁
  25. lock_kernel();
  26. //如果调用exit()之前该信号量还没退出,那就把它撤销
  27. sem_exit();
  28. //如果只是指针共享,那就减少files_struct->count,如果是独享,那就销毁
  29. __exit_files(tsk);
  30. //以上相同,释放fs->count
  31. __exit_fs(tsk);
  32. //释放信号处理函数表
  33. exit_sighand(tsk);
  34. //空函数
  35. exit_thread();
  36. ///表示进程是否为会话主管
  37. if (current->leader)
  38. disassociate_ctty(1);//删除终端,释放tty
  39. //若正在执行的代码是符合iBCS2标准的程序,则减少相对应模块的引用数目
  40. put_exec_domain(tsk->exec_domain);
  41. /* 若正在执行的代码属于全局执行文
  42. 件结构格则减少相对应模块的引用数目 */
  43. if (tsk->binfmt && tsk->binfmt->module)
  44. __MOD_DEC_USE_COUNT(tsk->binfmt->module);
  45. tsk->exit_code = code;
  46. //将当前进程设置为僵死状态;并给父进程发信号;其当前进程的子进程的父进程设置为init进程或者其他线程
  47. exit_notify();
  48. schedule();
  49. BUG();

接着挨个分析释放资源相关函数(信号量就等到进程间通信学完再分析)

  1. static inline void __exit_mm(struct task_struct * tsk)
  2. {
  3. struct mm_struct * mm = tsk->mm;//获取当前进程的内存描述符
  4. mm_release();//唤醒睡眠的父进程
  5. if (mm) {
  6. atomic_inc(&mm->mm_count);
  7. if (mm != tsk->active_mm) BUG();//确保mm与active_mm一样
  8. /* more a memory barrier than a real lock */
  9. task_lock(tsk);
  10. tsk->mm = NULL;//设置为NULL
  11. task_unlock(tsk);
  12.      //刷新tlb
  13. enter_lazy_tlb(mm, current, smp_processor_id());
  14. mmput(mm);//释放页表等等
  15. }
  16. }

以上资源释放完后,进程设置为僵尸状态,还保留pcb以及内核栈,自己并不释放而是由父进程负责,将调用exit_notify()通知其父进程

原因:让父进程可以统计信息,接下来看看exit_notify()

  1. /*
  2. * Send signals to all our closest relatives so that they know
  3. * to properly mourn us..
  4. */
  5. static void exit_notify(void)
  6. {
  7. struct task_struct * p, *t;
  8. //其当前进程的子进程的父进程设置为init进程,如果父进程是线程,那就托孤给其他线程
  9. forget_original_parent(current);
  10. /*
  11. * Check to see if any process groups have become orphaned
  12. * as a result of our exiting, and if they have any stopped
  13. * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
  14. *
  15. * Case i: Our father is in a different pgrp than we are
  16. * and we were the only connection outside, so our pgrp
  17. * is about to become orphaned.
  18. */
  19. t = current->p_pptr;//获取其养父
  20. /*
  21. 如果当前进程与父进程属于相同的会话,又处于不同的组,当前进程挂了,整个组如果成了孤儿组,那就要
  22. 给这个进程组的所有进程发送一个SIGHUP跟SIGCONT信号
  23. */
  24. if ((t->pgrp != current->pgrp) &&//组不同而会话相同
  25. (t->session == current->session) &&
  26. will_become_orphaned_pgrp(current->pgrp, current) &&//判断是否是孤儿进程组
  27. has_stopped_jobs(current->pgrp)) {////如果进程组中有处于TASK_STOP状态的进程
  28. kill_pg(current->pgrp,SIGHUP,1);//先发送SIGHUP在发送SIGCONT
  29. kill_pg(current->pgrp,SIGCONT,1);
  30. }
  31. /* Let father know we died
  32. *
  33. * Thread signals are configurable, but you aren‘t going to use
  34. * that to send signals to arbitary processes.
  35. * That stops right now.
  36. *
  37. * If the parent exec id doesn‘t match the exec id we saved
  38. * when we started then we know the parent has changed security
  39. * domain.
  40. *
  41. * If our self_exec id doesn‘t match our parent_exec_id then
  42. * we have changed execution domain as these two values started
  43. * the same after a fork.
  44. *
  45. */
  46. if(current->exit_signal != SIGCHLD &&
  47. ( current->parent_exec_id != t->self_exec_id ||
  48. current->self_exec_id != current->parent_exec_id)
  49. && !capable(CAP_KILL))
  50. current->exit_signal = SIGCHLD;//给父进程发的信号是SIGCHLD /*
  51. * This loop does two things:
  52. *
  53. * A. Make init inherit all the child processes
  54. * B. Check to see if any process groups have become orphaned
  55. * as a result of our exiting, and if they have any stopped
  56. * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
  57. */
  58. write_lock_irq(&tasklist_lock);
  59. current->state = TASK_ZOMBIE;//设置为僵尸进程
  60. do_notify_parent(current, current->exit_signal);//由父进程来料理后事
  61. //将子进程队列中的每个进程都转移到托孤的父进程的子进程队列中去
  62. while (current->p_cptr != NULL) {//p_cptr表示子进程
  63. p = current->p_cptr;//p指向子进程
  64. current->p_cptr = p->p_osptr;//子进程指向子进程他哥,形成一个队列
  65. p->p_ysptr = NULL;//子进程的滴滴设置为0
  66. p->ptrace = 0;
  67. p->p_pptr = p->p_opptr;//将养父改为亲父
  68. p->p_osptr = p->p_pptr->p_cptr;//子进程的哥哥改为子进程的养父的子进程,移到子进程队列
  69. if (p->p_osptr)
  70. p->p_osptr->p_ysptr = p;
  71. p->p_pptr->p_cptr = p;
  72. if (p->state == TASK_ZOMBIE)//并且判断每个子进程是否是僵尸状态
  73. do_notify_parent(p, p->exit_signal);
  74. /*
  75. * process group orphan check
  76. * Case ii: Our child is in a different pgrp
  77. * than we are, and it was the only connection
  78. * outside, so the child pgrp is now orphaned.
  79. 孤儿进程组: 一个进程组中的所有进程的父进程要么是该进程组的一个进程,
  80. 要么不是该进程组所在的会话中的进程。 一个进程组不是孤儿进程组的条件是,
  81. 该组中有一个进程其父进程在属于同一个会话的另一个组中。
  82. */
  83. if ((p->pgrp != current->pgrp) &&
  84. (p->session == current->session)) {
  85. int pgrp = p->pgrp;
  86. write_unlock_irq(&tasklist_lock);
  87. //父进程所在的组是否是孤儿进程组,以及是否含有stop进程
  88. if (is_orphaned_pgrp(pgrp) && has_stopped_jobs(pgrp)) {
  89. kill_pg(pgrp,SIGHUP,1);
  90. kill_pg(pgrp,SIGCONT,1);
  91. }
  92. write_lock_irq(&tasklist_lock);
  93. }
  94. }
  95. write_unlock_irq(&tasklist_lock);
  96. }

子进程托孤给其他进程(如果该进程是线程,也就是含有其他线程),否则托孤给init进程

  1. /*
  2. * When we die, we re-parent all our children.
  3. * Try to give them to another thread in our process
  4. * group, and if no such member exists, give it to
  5. * the global child reaper process (ie "init")
  6. */
  7. static inline void forget_original_parent(struct task_struct * father)
  8. {
  9. struct task_struct * p, *reaper;
  10. read_lock(&tasklist_lock);
  11. /* 获取当前用户空间的下一线程 */
  12. reaper = next_thread(father);
  13. if (reaper == father)//如果相等说明是进程,不是线程组,那就只能托孤给init进程
  14. reaper = child_reaper;//init进程
  15. for_each_task(p) {
  16. if (p->p_opptr == father) {//搜索所有task_struct数据结构,发现其进程生父就是要退出的进程
  17. /* We dont want people slaying init */
  18. p->exit_signal = SIGCHLD;//设置发送信号
  19. p->self_exec_id++;
  20. p->p_opptr = reaper;//将要死的进程的子进程托孤给reaper(当前线程的其他线程或者init进程?
  21. if (p->pdeath_signal) 
  22.             send_sig(p->pdeath_signal, p, 0);//发送信号,告知儿子死了
  23. }
  24. }
  25. read_unlock(&tasklist_lock);
  26. }

接下来查看do_notify_parent发送信号给父进程

  1. /*
  2. * Let a parent know about a status change of a child.
  3. 让一个父亲知道有关儿子的改变
  4. 参数为当前要退出进程,以及信号
  5. */
  6. void do_notify_parent(struct task_struct *tsk, int sig)
  7. {
  8. struct siginfo info;
  9. int why, status;
  10. info.si_signo = sig;
  11. info.si_errno = 0;
  12. info.si_pid = tsk->pid;
  13. info.si_uid = tsk->uid;
  14. /* FIXME: find out whether or not this is supposed to be c*time. */
  15. info.si_utime = tsk->times.tms_utime;
  16. info.si_stime = tsk->times.tms_stime;
  17. status = tsk->exit_code & 0x7f;
  18. why = SI_KERNEL; /* shouldn‘t happen */
  19. switch (tsk->state) {
  20. case TASK_STOPPED:
  21. /* FIXME -- can we deduce CLD_TRAPPED or CLD_CONTINUED? */
  22. if (tsk->ptrace & PT_PTRACED)
  23. why = CLD_TRAPPED;
  24. else
  25. why = CLD_STOPPED;
  26. break;
  27. default:
  28. if (tsk->exit_code & 0x80)
  29. why = CLD_DUMPED;
  30. else if (tsk->exit_code & 0x7f)
  31. why = CLD_KILLED;
  32. else {
  33. why = CLD_EXITED;
  34. status = tsk->exit_code >> 8;
  35. }
  36. break;
  37. }
  38. info.si_code = why;
  39. info.si_status = status;
  40. send_sig_info(sig, &info, tsk->p_pptr);//发送信号
  41. wake_up_parent(tsk->p_pptr);//唤醒父进程
  42. }
  1. /*
  2. * This function is typically called only by the session leader, when
  3. * it wants to disassociate itself from its controlling tty.
  4. *
  5. * It performs the following functions:
  6. * (1) Sends a SIGHUP and SIGCONT to the foreground process group
  7. * (2) Clears the tty from being controlling the session
  8. * (3) Clears the controlling tty for all processes in the
  9. * session group.
  10. *当前进程是一个会话的主进程(current->leader非0)那就还要将整个session与中断切断,并释放tty,pcb有个tty指针
  11. * The argument on_exit is set to 1 if called when a process is
  12. * exiting; it is 0 if called by the ioctl TIOCNOTTY.
  13. */
  14. void disassociate_ctty(int on_exit)
  15. {
  16. struct tty_struct *tty = current->tty;//获取当前进程的tty
  17. struct task_struct *p;
  18. int tty_pgrp = -1;
  19. if (tty) {
  20. tty_pgrp = tty->pgrp;//获取进程组的tty
  21. if (on_exit && tty->driver.type != TTY_DRIVER_TYPE_PTY)//统计tty设备打开的次数
  22. tty_vhangup(tty);
  23. } else {
  24. if (current->tty_old_pgrp) {
  25. kill_pg(current->tty_old_pgrp, SIGHUP, on_exit);//给当前进程组发送sighup与sigcont信号
  26. kill_pg(current->tty_old_pgrp, SIGCONT, on_exit);
  27. }
  28. return;
  29. }
  30. if (tty_pgrp > 0) {
  31. kill_pg(tty_pgrp, SIGHUP, on_exit);
  32. if (!on_exit)
  33. kill_pg(tty_pgrp, SIGCONT, on_exit);
  34. }
  35. current->tty_old_pgrp = 0;//进程控制终端所在的组标识设置为0
  36. tty->session = 0;//会话设置为0
  37. tty->pgrp = -1;//组设置为-1
  38. read_lock(&tasklist_lock);
  39. for_each_task(p)//遍历每个进程是否位于同一会话
  40. if (p->session == current->session)//当前进程是会话的主进程
  41. p->tty = NULL;//切断tty终端
  42. read_unlock(&tasklist_lock);
  43. }

do_exit流程:

禁止中断调用,0号进程,1号进程退出

如果有独立空间那就删除独立空间,释放页表,释放信号量,释放文件对象,释放信号处理函数表

如果是会话控制进程,删除终端,释放tty,接下来调用exit_notify()函数

如果当前进程是是线程(也就包含其他线程,非独享),托孤给其他线程,否则托孤给init进程

判断当前进程退出是否会导致孤儿进程组出现

设置发送信号为SIGCHLD,将当前进程设置为僵尸状态,接着调用do_notify_parent发送信号给父进程,并唤醒父进程

并将僵尸进程的所有子进程的队列移到托孤的队列.最后shedule()

  1. //等待子进程的pid
  2. asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru)
  3. {
  4. int flag, retval;
  5. DECLARE_WAITQUEUE(wait, current);//为当前进程分配一个waitqueue结构
  6. struct task_struct *tsk;
  7. if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
  8. return -EINVAL;
  9. //添加到当前进程的waitchldexit对列中
  10. add_wait_queue(&current->wait_chldexit,&wait);
  11. repeat:
  12. flag = 0;
  13. current->state = TASK_INTERRUPTIBLE;//设置为睡眠,让其他进程先运行,等待子进程挂了
  14. read_lock(&tasklist_lock);
  15. tsk = current;
  16. do {
  17. struct task_struct *p;
  18. for (p = tsk->p_cptr ; p ; p = p->p_osptr) {//p表示当前进程的子进程
  19. if (pid>0) {
  20. if (p->pid != pid)//是否等于参数pid,不等于就继续
  21. continue;
  22. } else if (!pid) {//不是0号进程
  23. if (p->pgrp != current->pgrp)
  24. continue;
  25. } else if (pid != -1) {//不是-1(随便)
  26. if (p->pgrp != -pid)
  27. continue;
  28. }
  29. /* Wait for all children (clone and not) if __WALL is set;
  30. * otherwise, wait for clone children *only* if __WCLONE is
  31. * set; otherwise, wait for non-clone children *only*. (Note:
  32. * A "clone" child here is one that reports to its parent
  33. * using a signal other than SIGCHLD.) */
  34. //判断子进程的信号是否是sigchld
  35. if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
  36. && !(options & __WALL))
  37. continue;
  38. flag = 1;//表示是当前进程的子进程
  39. switch (p->state) {
  40. case TASK_STOPPED://等待子进程被跟踪
  41. if (!p->exit_code)//是否设置了退出码
  42. continue;
  43. if (!(options & WUNTRACED) && !(p->ptrace & PT_PTRACED))//判断条件是否跟踪
  44. continue;
  45. read_unlock(&tasklist_lock);
  46. retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
  47. if (!retval && stat_addr)
  48. retval = put_user((p->exit_code << 8) | 0x7f, stat_addr);
  49. if (!retval) {
  50. p->exit_code = 0;
  51. retval = p->pid;
  52. }
  53. goto end_wait4;//满足直接跳到end_wait4
  54. case TASK_ZOMBIE://僵尸状态
  55. current->times.tms_cutime += p->times.tms_utime + p->times.tms_cutime;
  56. current->times.tms_cstime += p->times.tms_stime + p->times.tms_cstime;
  57. read_unlock(&tasklist_lock);
  58. retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
  59. if (!retval && stat_addr)
  60. retval = put_user(p->exit_code, stat_addr);//指定位置保存退出码
  61. if (retval)
  62. goto end_wait4;
  63. retval = p->pid;
  64. if (p->p_opptr != p->p_pptr) {//生父与养父是否相同
  65. write_lock_irq(&tasklist_lock);
  66. REMOVE_LINKS(p);//将task_struct从养父队列中脱离出来
  67. p->p_pptr = p->p_opptr;//将养父设置为生父
  68. SET_LINKS(p);
  69. do_notify_parent(p, SIGCHLD);//通知生父进程自己挂了
  70. write_unlock_irq(&tasklist_lock);
  71. } else
  72. release_task(p);//释放残留的资源如pcb等等
  73. goto end_wait4;//子进程处于僵死状态,goto end_wait4
  74. default:
  75. continue;
  76. }
  77. }
  78. if (options & __WNOTHREAD)//如果设置了wnothread直接跳出
  79. break;
  80. tsk = next_thread(tsk);//到同一进程的寻找下一个线程,一线程创建的子进程挂了,其他线程调用wait应该没用吧?
  81. } while (tsk != current);
  82. read_unlock(&tasklist_lock);
  83. if (flag) {//如果pid不是当前进程的子进程,直接到end_wait4
  84. retval = 0;
  85. if (options & WNOHANG)//设置了wnohang
  86. goto end_wait4;
  87. retval = -ERESTARTSYS;
  88. if (signal_pending(current))//当前进程是否有信号未处理
  89. goto end_wait4;
  90. schedule();//被调度.等待被子进程唤醒
  91. goto repeat;
  92. }
  93. retval = -ECHILD;
  94. end_wait4:
  95. current->state = TASK_RUNNING;//将当前进程改为可运行状态
  96. remove_wait_queue(&current->wait_chldexit,&wait);
  97. return retval;
  98. }

下列条件之一得到满足时才结束,goto end_wait4:

1、所等待的子进程的状态变成TASK_STOPPED,TASK_ZOMBIE;

2、所等待的子进程存在,可不在上述两个状态,而调用参数options中的WHONANG标志位为1,或者当前进程接受到了其他的信号;

3、进程号pid的那个进程根本不存在,或者不是当前进程的子进程。

否则,当前进程将其自身的状态设成TASK_INTERRUPTIBLE,并调用schedule()。

释放残余的子进程资源

  1. static void release_task(struct task_struct * p)//释放子进程留下的资源
  2. {
  3. if (p != current) {
  4. #ifdef CONFIG_SMP
  5. /*
  6. * Wait to make sure the process isn‘t on the
  7. * runqueue (active on some other CPU still)
  8. */
  9. for (;;) {
  10. task_lock(p);
  11. if (!p->has_cpu)
  12. break;
  13. task_unlock(p);
  14. do {
  15. barrier();
  16. } while (p->has_cpu);
  17. }
  18. task_unlock(p);
  19. #endif
  20. atomic_dec(&p->user->processes);//子进程数目减少
  21. free_uid(p->user);//是否uid
  22. unhash_process(p);//把子进程的pcb从队列摘下来
  23. release_thread(p);//检查进程的LDT是否已释放
  24. current->cmin_flt += p->min_flt + p->cmin_flt;
  25. current->cmaj_flt += p->maj_flt + p->cmaj_flt;
  26. current->cnswap += p->nswap + p->cnswap;
  27. /*
  28. * Potentially available timeslices are retrieved
  29. * here - this way the parent does not get penalized
  30. * for creating too many processes.
  31. *
  32. * (this cannot be used to artificially ‘generate‘
  33. * timeslices, because any timeslice recovered here
  34. * was given away by the parent in the first place.)
  35. */
  36. current->counter += p->counter;
  37. if (current->counter >= MAX_COUNTER)
  38. current->counter = MAX_COUNTER;
  39. free_task_struct(p);//将2个物理页大小的pcb释放
  40. } else {
  41. printk("task releasing itself\n");
  42. }
  43. }

来自为知笔记(Wiz)

时间: 2024-10-29 19:11:06

linux内核情景分析之exit与Wait的相关文章

linux内核情景分析之execve()

用来描述用户态的cpu寄存器在内核栈中保存情况.可以获取用户空间的信息 struct pt_regs { long ebx; //可执行文件路径的指针(regs.ebx中 long ecx; //命令行参数的指针(regs.ecx中) long edx; //环境变量的指针(regs.edx中). long esi; long edi; long ebp; long eax; int xds; int xes; long orig_eax; long eip; int xcs; long efl

Linux内核情景分析之异常访问,用户堆栈的扩展

情景假设: 在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存.也就是所说的野指针访问. 当cpu产生页面错误时,会把失败的线性地址放在cr2寄存器.线性地址缺页异常的4种情况 1.如果cpu访问的行现地址在内核态,那么很可能访问的是非连续区,需要vmalloc_fault处理. 2.缺页异常发生在中断或者内核线程时,直接失败,因为不可修改页表 3.地址在一个区间内,那就可能是已经物理地址映射了但权限问题(错误处理)或者其物理地址没有分配(分配物理内存) 4.如果找到一个在线性地址

Linux内核情景分析的alloc_pages

NUMA结构的alloc_pages ==================== mm/numa.c 43 43 ==================== 43 #ifdef CONFIG_DISCONTIGMEM ==================== mm/numa.c 91 128 ==================== 91 /* 92 * This can be refined. Currently, tries to do round robin, instead 93 * sho

linux内核情景分析之强制性调度

从系统调用返回到用户空间是否调度,从ret_with_reschedule可看出,是否真正调度,取决于当前进程的pcb中的need_resched是否设置为1,那如何设置为1取决于以下几种情况: 时间中断处理程序,发现当前进程运行时间过长:每次发生时间中断,都要递减该进程的时间片,一旦count为0,强制调度,剥夺当前进程运行 void update_process_times(int user_tick) { struct task_struct *p = current; int cpu =

《linux 内核完全剖析》 exit.c 代码分析笔记

exit.c 代码分析笔记 release 释放进程的函数release() 主要根据指定进程的任务数据结构指针,在任务数组中删除指定的进程指针,释放相关内存页,并立刻让内核重新调度进程的运行. void release(struct task_struct * p) //释放p指向的进程 { int i; if (!p) //常规检测p是否为0 return; if (p == current) { //不能把自己给释放了 printk("task releasing itself\n\r&q

Linux源代码情景分析读书笔记 物理页面的分配

函数 alloc_pages流程图 Linux源代码情景分析读书笔记 物理页面的分配,布布扣,bubuko.com

linux 内核源代码分析 - 获取数组的大小

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 測试程序: #include<stdio.h> #include<stdlib.h> struct dev { int a; char b; float c; }; struct dev devs[]= { { 1,'a',7.0, }, { 1,'a',7.0, }, { 1,'a',7.0, }, }; int main() { printf("int is %d \

Linux内核源代码分析方法

Linux内核源代码分析方法   一.内核源代码之我见 Linux内核代码的庞大令不少人"望而生畏",也正由于如此,使得人们对Linux的了解仅处于泛泛的层次.假设想透析Linux,深入操作系统的本质,阅读内核源代码是最有效的途径.我们都知道,想成为优秀的程序猿,须要大量的实践和代码的编写.编程固然重要,可是往往仅仅编程的人非常easy把自己局限在自己的知识领域内.假设要扩展自己知识的广度,我们须要多接触其它人编写的代码,尤其是水平比我们更高的人编写的代码.通过这样的途径,我们能够跳出

Linux内核Crash分析

转载自:http://linux.cn/article-3475-1.html 在工作中经常会遇到一些内核crash的情况,本文就是根据内核出现crash后的打印信息,对其进行了分析,使用的内核版本为:Linux2.6.32. 每一个进程的生命周期内,其生命周期的范围为几毫秒到几个月.一般都是和内核有交互,例如用户空间程序使用系统调用进入内核空间.这时使用的不再是用户空 间的栈空间,使用对应的内核栈空间.对每一个进程来说,Linux内核都会把两个不同的数据结构紧凑的存放在一个单独为进程分配的存储