Linux線程ID和進程ID

1. Linux線程的線程ID和進程ID

Linux內核並不支持真正意義上的線程,Linux線程庫是用與普通進程具有同樣內核調度視圖的輕量級進程來實現線程支持的。這些輕量級進程擁有獨立的進程id,在進程調度、信號處理、IO等方面享有與普通進程一樣的能力。
每個Linux線程都同時具有線程id和進程id,其中進程id就是內核所維護的進程號,而線程id則由線程庫分配和維護。
1)每個進程都有自己的PID,這是唯一的。
2)除此之外,每個進程還有其它的ID標識,比如處於某個線程組中的所有進程都有一個統一的線程組ID(TGID),如果進程沒有使用線程,PID和TGID是相同的,其實主線程的PID和TGID就是相同的。
3)獨立進程可以合併成爲進程組。
4)幾個進程組可以合併成一個會話,會話中的所有進程都有同樣的會話ID。

重點關注一下 task_struct 中與進程ID相關的兩個成員:
struct task_struct{
   pid_t pid;
   pid_t tgid;
}
屬於同一個進程的所有線程,tgid是相同的,都指向線程組第一個進程(主線程)的PID值。
可以分別通過getpid(), syscall(SYS_gettid)獲取對應的值。結合下面第2部分的程序,能夠更直觀地看出getpid()獲取tgid、syscall()獲取PID。

因此,對於Linux而言,所謂線程組共享進程ID,其實是通過 tgid 表示的,而線程組中的每個線程本質上是有自己的進程ID號的。

2. 通過程序查看線程相關的ID

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <linux/unistd.h>

pid_t gettid()  
{  
  return syscall(__NR_gettid);  
} 

static void *phandle(void *arg)
{
  pid_t pid_chld, pid_chld1;
  pthread_t tid;
	
  pid_chld = getpid();
  pid_chld1 = gettid();
  tid = pthread_self();
	
  printf("<child pthread>@(pid=%d/ptid=%d/tid=%lu) Child pthread ready to sleep 5 \r\n", pid_chld, pid_chld1,tid);
  sleep(2);
  printf("<child pthread>@(pid=%d/ptid=%d/tid=%lu) Child pthread exit\r\n", pid_chld, pid_chld1,tid);

  return NULL;
}

int main(int argc, char *argv[])
{
  int cnt;
  pid_t pid;
  pthread_t g_tid,tid;

  pid = getpid();
  g_tid = pthread_self();
  printf("[[main pthread]] (pid=%lu/tid_mian=%lu) start! \r\n", pid, g_tid);
	
  for(cnt=0; cnt<2; cnt++)
  {
    pthread_create(&tid,NULL,phandle,NULL);
    printf("[[main pthread]] create a child pthread_%d(%lu)\r\n",cnt,tid);
  }
  
  sleep(5);
  printf("[[main pthread]] (pid=%lu/tid_mian=%lu) exit!\r\n", pid, g_tid);
  exit(0);
}

執行結果如下:
# gcc -o pid pthread_pid_tid.c -lpthread 
# ./pid 
[[main pthread]] (pid=3221/tid_mian=3076355776) start! 
[[main pthread]] create a child pthread_0(3076352832)
[[main pthread]] create a child pthread_1(3067960128)
<child pthread>@(pid=3221/ptid=3223/tid=3067960128) Child pthread ready to sleep 5 
<child pthread>@(pid=3221/ptid=3222/tid=3076352832) Child pthread ready to sleep 5 
<child pthread>@(pid=3221/ptid=3223/tid=3067960128) Child pthread exit
<child pthread>@(pid=3221/ptid=3222/tid=3076352832) Child pthread exit
[[main pthread]] (pid=3221/tid_mian=3076355776) exit!

3. 轉載網上的一篇關於線程ID和進程ID的文章

原文:http://blog.chinaunix.net/uid-24567872-id-100482.html   點擊打開鏈接
在linux中,線程與進程最大的區別就是是否共享同一塊地址空間,而且共享同一塊地址空間的那一組線程將顯現相同的PID號。
       在實際編程應用中,我們會很容易發現並證明,一組同源線程的PID都是一樣的,但它們的PID真的一樣麼?
       在linux中,線程的創建和普通進程的創建類似,只不過在調用clone()的時候需要傳遞一些參數標誌來指明需要共享的資源:
  1. clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);
       上面的代碼產生的結果和調用fork()差不多,只是父子倆共享地址空間、文件系統資源、文件描述符和信號處理程序。換個說法就是,新建的進程和它的父進程就是流行的所謂線程。  
       對比一下,一個普通的fork()的實現是:
  1. clone(SIGCHLD, 0);
      而vfork()的實現是:
  1. clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0);
      傳遞給clone()的參數標誌決定了新創建進程的行爲方式和父子進程之間共享的資源種類。下面列舉部分clone()參數標誌,這些是在<linux/sched.h>中定義的。
       CLONE_FILES     父子進程共享打開的文件
       CLONE_FS    父子進程共享文件系統信息
       CLONE_SIGHAND      父子進程共享信號處理函數
       CLONE_VM     父子進程共享地址空間
       CLONE_VFORK    調用vfork(),所以父進程準備睡眠等待子進程將其喚醒

下面用一段程序測試一下,代碼如下:
這段測試程序是主線程創建10個線程,並分別打印PID及TID。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <pthread.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <string.h>

  7. pthread_t tid[10];

  8. void * thread_handler(void *arg)
  9. {
  10.     printf("tid%d:%u,pid:%u\n", (int)arg, (unsigned)pthread_self(), 
  11.         (unsigned)getpid());
  12.     while(1){
  13.         sleep(1);
  14.     }
  15.     return NULL;
  16. }

  17. int main(void)
  18. {
  19.     int i, ret;
  20.     pid_t pid;
  21.     printf("main tid:%u,pid:%u\n", (unsigned)pthread_self(),
  22.         (unsigned)getpid());
  23.     for(= 0; i < 10; i++){
  24.         if((ret = pthread_create(&tid[i], NULL, thread_handler, 
  25.             (void *)i)) != 0){
  26.             fprintf(stderr, "pthread_create:%s\n",
  27.                 strerror(ret));
  28.             exit(1);
  29.         }
  30.     }
  31.     sleep(3);
  32.     pid = fork();
  33.     if(pid == 0){
  34.         printf("son tid:%u,pid:%u\n", (unsigned)pthread_self(),
  35.             (unsigned)getpid());
  36.         while(1);
  37.             sleep(1);
  38.     }
  39.     while(1)
  40.         sleep(2);
  41.     exit(0);
  42. }
編譯運行:
  1. zx@zhangxu:~/lianxi/apue$ gcc -o test pthreadid.-lpthread
  2. zx@zhangxu:~/lianxi/apue$ ./test
  3. main tid:3077888816,pid:2418
    tid0:3077880688,pid:2418
    tid1:3069487984,pid:2418
    tid2:3061095280,pid:2418
    tid3:3052702576,pid:2418
    tid4:3044309872,pid:2418
    tid5:3035917168,pid:2418
    tid6:3027524464,pid:2418
    tid7:3019131760,pid:2418
    tid8:3010739056,pid:2418
    tid9:3002346352,pid:2418
    son tid:3077888816,pid:2429
從結果可以看出,測試程序中所有線程的PID都相同。

在另一個終端調用pstree -pu
  1. ├─gnome-terminal(1624,zx)─┬─bash(1628)
    │                         ├─bash(1704)───pstree(2430)
    │                         ├─bash(1927)───test(2418)─┬─test(2429)
    │                         │                         ├─{test}(2419)
    │                         │                         ├─{test}(2420)
    │                         │                         ├─{test}(2421)
    │                         │                         ├─{test}(2422)
    │                         │                         ├─{test}(2423)
    │                         │                         ├─{test}(2424)
    │                         │                         ├─{test}(2425)
    │                         │                         ├─{test}(2426)
    │                         │                         ├─{test}(2427)
    │                         │                         └─{test}(2428)

從中可以看出,每個線程的PID其實是不同的,因爲測試程序是理想狀態,只有一個主線程在創建線程,所以PID的值都是連續的。


發佈了28 篇原創文章 · 獲贊 8 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章