Linux2.6執行緒模式NPTL實現

來源:互聯網
上載者:User
Linux2.6核心實現的是NPTL執行緒模式,依然是用進程來類比線程,但新引入了線程組(進程組)的概念,使得實現效率更好。

在2.4核心中,不存線上程組的概念,當運行一個多線程得程式時,使用ps命令,可以看到有許多個進程,在ps命令看來,線程基本上是等同於進程,在訊號處理中,情況也是如此,只有指定進程號的線程,可以接收到訊號。在2.6核心中引入了線程組的概念,在2.6核心中,如果使用ps命令看,一個多線程的進程,只會顯示一個進程,在給線程組中的任何一個線程發送訊號的時候,整個線程組中的進程都能收到訊號。
在核心task_struct中相關欄位如下(位於include/linux/sched.h):

代碼全選

937   struct task_struct  {
         ...
993      pid_t pid;
994      pid_t tgid;
         ...
1013     struct task_struct
*group_leader;   /* threadgroup leader */

         ...
1017     struct list_head
thread_group;

         ...
1198  };

pid,從字面上是process id,但其實是thread id。
tgid,從字面上,應該是thread group id,也就是真正的process id。
這一點,可以從系統調用getpid和gettid中看出來(位於kernel/timer.c)。

代碼全選

954   asmlinkage long sys_getpid(void)
955   {
956      return
current->tgid;

957   }
1100  asmlinkage long sys_gettid(void)
1101  {
1102     return
current->pid;

1103  }

group_leader欄位,指向線程組中的第一個線程,建立第一個線程的時候,group_leader指向自己,建立其後的線程時,指向第一個線程的task_struct結構;
thread_group,當前進程所有線程的隊列,對於group_leader,這是個隊列頭,對於其後的進程而言,通過這個欄位,掛入隊列中,可以通過此隊列,遍曆所有線程。
線程組中各個線程的關係,是在do_fork中設定的,具體的代碼在copy_process中(位於kernel/fork.c):代碼全選

959   copy_process()
960   {
         ...
1112     p->tgid =
p->pid;

1113     if (clone_flags &
CLONE_THREAD)

1114      
 p->tgid = current->tgid;

         ...
1181     p->group_leader =
p;

1182   
 INIT_LIST_HEAD(&p->thread_group);

         ...
1234     if (clone_flags &
CLONE_THREAD) {

1235      
 p->group_leader = current->group_leader;

1236      
 list_add_tail_rcu(&p->thread_group,
&p->group_leader->thread_group);

      ...
1252     }

1254     if (likely(p->pid))
{

          
 ...

1259        if
(thread_group_leader(p)) {

          
    ...

1266         
 list_add_tail_rcu(&p->tasks, &init_task.tasks);

          
    ...

1268        }
          
 ...

1271     }
         ...
1320  }

1113-1114行說明在建立線程時,從父進程擷取tgid,表明他們在同一個線程組中;1181-1182則對group_leader和thread_group初始化,對於第一個線程,則group_leader就是它自己;1234-1236行,將新建立的線程的group_leader設定成為父進程得group_leader,無論父進程是不是線程組中的第一個線程,它的group_leader都是指向第一個線程的task_struct,同時通過thread_group欄位,掛入到第一個線程的thread_group隊列中;1266行表明只有線程組中的第一個線程,才會通過tasks欄位,掛入到init_task隊列中。
在引入線程組概念後,退出部分也引入了一個新的系統調用exit_group(位於kernel/exit.c)

1055  NORET_TYPE void
1056  do_group_exit(int exit_code)
1057  {
1058     BUG_ON(exit_code &
0x80); /* core dumps don't get here */
1059
1060     if
(current->signal->flags & SIGNAL_GROUP_EXIT)
1061        exit_code =
current->signal->group_exit_code;
1062     else if
(!thread_group_empty(current)) {
1063        struct
signal_struct *const sig = current->signal;
1064        struct
sighand_struct *const sighand = current->sighand;
1065      
 spin_lock_irq(&sighand->siglock);
1066        if
(sig->flags & SIGNAL_GROUP_EXIT)
1067         
 /* Another thread got here before we took the lock.  */
1068         
 exit_code = sig->group_exit_code;
1069        else {
1070         
 sig->group_exit_code = exit_code;
1071         
 zap_other_threads(current);
1072        }
1073      
 spin_unlock_irq(&sighand->siglock);
1074   }
1075
1076     do_exit(exit_code);
1077     /* NOTREACHED */
1078  }

在1060行中,current->signal其實是線程組中所有線程共用的,對於調用exit_group的那個線程,如果是一個多線程的進程,就會進入1062-1074這部分代碼,如果是單線程,則直接進入do_exit退出進程。這部分代碼的主要操作在zap_other_threads中(位於kernel/signal.c)

   void zap_other_threads(struct task_struct *p)
982   {
983      struct task_struct
*t;
984   
985    
 p->signal->flags = SIGNAL_GROUP_EXIT;
986    
 p->signal->group_stop_count = 0;
987
988      if
(thread_group_empty(p))
989         return;
990
991      for (t =
next_thread(p); t != p; t = next_thread(t)) {
992         /*
993          * Don't
bother with already dead threads
994          */
995         if
(t->exit_state)
996          
 continue;
997
998         /*
SIGKILL will be handled before any pending SIGSTOP */
999       
 sigaddset(&t->pending.signal, SIGKILL);
1000      
 signal_wake_up(t, 1);
1001     }
1002  }

next_thread定義在include/linux/sched.h中,如下

651  static inline struct task_struct *next_thread(const struct
task_struct *p)
1652  {
1653     return
list_entry(rcu_dereference(p->thread_group.next),
1654         
   struct task_struct, thread_group);
1655  }

其實就是通過task_struct中的thread_group隊列來遍曆線程組中的所有線程。
在其中,會在signal->flags中設定SIGNAL_GROUP_EXIT,同時,搜尋線程組中所有進程,在每個線程中掛上一個SIGKILL訊號,這樣,當那些線程調度到啟動並執行時候,就會處理SIGKILL訊號,對於SIGKILL訊號的處理,會調用do_group_exit,不過,當這次調用到do_group_exit的時候,將運行到1061行,然後就到了1076行的do_exit。這樣,當線程組中的每個線程都運行過一遍後,整個線程組就退出了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.