Chinaunix首页 | 论坛 | 博客
  • 博客访问: 710437
  • 博文数量: 31
  • 博客积分: 330
  • 博客等级: 一等列兵
  • 技术积分: 3004
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-05 22:38
个人简介

java开发工程师,专注于内核源码,算法,数据结构。 qq:630501400

文章分类
文章存档

2014年(2)

2013年(22)

2012年(7)

分类: C/C++

2013-02-19 16:34:55

内核signal_struct中notify_count变量的作用

     在进程或者线程执行exec函数的时候,由于exec函数会影响整个进程组的内存结构,所以其他线程的存在是没有意义的,这时需要杀死线程组中其他的线程,也包括主线程,杀死线程的任务交给了信号SIGKILL,同时执行exec函数的进程需要被挂起,等待所有线程结束后,在重新唤醒执行exec的进程。这一系列的控制,在signal_struct中,变量notify_countgroup_exit_task起到控制进程挂起和唤醒的作用。notify_count记录需要被结束的线程的个数,group_exit_task记录执行exec函数的进程,这个进程等待子线程结束的时候需要被挂起,每个线程在结束的时候,notify_count都会减一,在notify_count等于0的时候,表示线程都已经结束,group_exit_task需要被重新唤醒。继续执行exec函数后面的工作。

代码:fs/exec.c

          828 sig->group_exit_task = tsk;                              //tsk就是当前线程或者进程

          829 sig->notify_count = zap_other_threads(tsk); //notify_count就是线程组中线程的个数

          830 if (!thread_group_leader(tsk))                           //如果当前线程不是主线程,那么需要去除主线程

          831           sig->notify_count--;

          833 while (sig->notify_count) {                                //循环等待所有的子线程结束,不包括主线程

          834            __set_current_state(TASK_UNINTERRUPTIBLE);

          835           spin_unlock_irq(lock);

          836            schedule();

          837            spin_lock_irq(lock);

          838 }

      通过上面的代码可以看出group_exit_task就是调用exec函数的进程或者线程,zap_other_threads的作用是结束当前线程组中其他的线程,不包括自己,包括主线程。notify_count是需要结束线程的个数。由于线程组中的所有线程会共用一个signal_struct,那么这个notify_count在这个线程组中就是共用的。这里会判断如果当前task不是主线程,不用等待主线程退出。只等待其他线程退出即可,因为根本不确定当前task是否是当前线程组中的主线程,如果是的话,notify_count就不减一,等待notify_count等于零就表示所有的线程都已经退出。如果不是主线程,notify_count减一, 等待notify_count等于零就表示所有的线程不包括主线程都已经退出。在线程或者进程退出的时候会设置notify_count和判断它的值,如果notify_count等于0,表示所有的线程都已经结束了,这时候需要唤醒被挂起的group_exit_task

代码:kernel/exit.c

          79 static void __exit_signal(struct task_struct *tsk) // 进程结束的时候会调用这个函数

          80 {

          81            struct signal_struct *sig = tsk->signal;

          82            bool group_dead = thread_group_leader(tsk); //判断tsk是否是主线程

          83            struct sighand_struct *sighand;

          84            struct tty_struct *uninitialized_var(tty);

          85

          86            sighand = rcu_dereference_check(tsk->sighand,

          87            rcu_read_lock_held() ||

          88            lockdep_tasklist_lock_is_held());

          89            spin_lock(&sighand->siglock);

          90

          91            posix_cpu_timers_exit(tsk);

          92            if (group_dead) {

          93                      posix_cpu_timers_exit_group(tsk);

          94                      tty = sig->tty;

          95                      sig->tty = NULL;

          96            } else {                                              //如果不是主线程

          97 /*

          98 * This can only happen if the caller is de_thread().

          99 * FIXME: this is the temporary hack, we should teach

          100 * posix-cpu-timers to handle this case correctly.

          101 */

          102            if (unlikely(has_group_leader_pid(tsk)))

          103                      posix_cpu_timers_exit_group(tsk);

          104

          105 /*

          106 * If there is any task waiting for the group exit

          107 * then notify it:

          108 */

          109            if (sig->notify_count > 0 && !--sig->notify_count)     //如果notify_count大于0,那么notify_count减一,表示结束了一个线程,如果同时减一后如果notify_count0,那么就表示线程组中所有的进程都已经结束了,那么是时候唤醒执行de_thread函数的那个进程了,就是group_exit_task

          110                      wake_up_process(sig->group_exit_task);


      __exit_signal函数会在release_task中调用到,也就是释放进程的剩余内存结构的时候会改变notify_count的值,同时如果所有线程组中的其他线程都结束,会唤醒group_exit_task进程。至于一个线程在退出的时候并不会向父进程发送结束的信号SIGCHLD,而是选择直接结束,直接release_task,这个和exit_signal变量有关,这个变量的作用后面再讨论。

     如果是线程组中的主线程退出,那么这个线程不会调用release_task来释放内存结构,同时调用de_thread函数的线程会将notify_count置为-1,这样主线程在退出的时候会看到这个变量,如果为-1,那么会唤醒那个调用de_thread函数的线程。

代码:fs/exec.c

          846 if (!thread_group_leader(tsk)) {

          847            struct task_struct *leader = tsk->group_leader;

          848

          849            sig->notify_count = -1; /* for exit_notify() */ //notify_count置为-1

          850            for (;;) {                                                               //等待主线程开始退出

          851                      write_lock_irq(&tasklist_lock);

          852                      if (likely(leader->exit_state))                  //exit_state大于0表示线程已经开始退出

          853                                break;

          854                      __set_current_state(TASK_UNINTERRUPTIBLE);

          855                      write_unlock_irq(&tasklist_lock);

          856                      schedule();

          857            }

      de_thread函数会判断当前task是否是主线程,如果不是主线程,要等待主线程结束,如果主线程的exit_state大于0,表示主线程已经进入了退出流程了,如果小于0表示还没有退出,那么需要挂起当前线程来等待主线程结束。主线程会判断如果notify_count小于0,那么就会尝试唤醒那个被挂起的线程。

下面这段代码在exit_notify函数中

代码:fs/exec.c

          860 /* mt-exec, de_thread() is waiting for group leader */

          861 if (unlikely(tsk->signal->notify_count < 0))

          862 wake_up_process(tsk->signal->group_exit_task);

总结:

       在执行exec函数的时候,notify_count记录了需要被结束的线程的个数,从大于0变到0的时候,表示所有的线程都已经结束,这个时候去唤醒group_exit_task,使得exec函数继续执行,exit_signal变量的作用后面会一步一步的分析。以上分析代表个人观点,个人水平有限,不正确的地方希望大家指出,积极讨论。

参考文章:

1.http://blog.csdn.net/dog250/article/details/5303673

阅读(4415) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~