一 Linux--多線程( 三 )

運行結果如下:

一 Linux--多線程

文章插圖
可以看到六個線程的PID是一樣的,同屬于一個進程,但是它們還有一個表示,LWP(light wighted process),輕量級進程的ID 。下面詳細介紹 。
進程ID和線程ID
  • 在Linux下,線程是由Native POSIX Thread Library 實現的 , 在這種實現下,線程又被稱為輕量級進程(LWP) 。在用戶態的每個進程,內核中都有一個與之對應的調度實體(擁有自己的task_struct結構體) 。
  • 在沒有線程之前 , 一個進程對應內核里的一個進程描述符,對應一個進程ID 。引入線程概念之后,一個用戶進程下管理多個用戶態線程 , 每個線程作為一個獨立的調度實體,在內核中都有自己的進程描述符 。進程和內核的描述符變成了1:N的關系 。
  • 多線程的進程,又被稱為線程組 。線程組內的每一個線程在內核中都有一個進程描述符與之對應 。進程描述符結構體表面上看是進程的pid,其實它對應的是線程ID;進程描述符中的tpid , 含義是線程組ID,該值對應的是用戶層面的進程ID 。
    struct task_struct { ... pid_t pid;// 對應的是線程ID,就是我們看到的lwp pid_t tgid;// 線程組ID,該值對應的是用戶層面的進程ID ... struct task_struct *group_leader; ... struct list_head thread_group; ...};
  • 具體關系如下:
用戶態系統調用內核進程描述符中對應的結構線程IDpid_t gettid(void)pid_t pid進程IDpid_d getpid(void)pid_t tgid注意: 這里的線程ID和創建線程得到的ID不是一回事,這里的線程ID是用來唯一標識線程的一個整形變量 。
如何查看線程ID?
1.使用ps命令,帶-L選項,可以查看到lwp
2.Linux提供了gettid系統調用來返回其線程ID,可是glibc并沒有將該系統調用封裝起來,在開放接口來供程序員使用 。如果確實需要獲得線程ID,可以采用如下方法:
#include <sys/syscall.h>pid_t tid; tid = syscall(SYS_gettid);在前面的一張圖片中(如下) , 我們可以發現的是,有一個線程的ID和進程ID是一樣的,這個線程就是主線程 。在內核中被稱為group leader,內核在創建第一個線程時,會將線程組的ID的值設置成第一個線程的線程ID , group_leader指針則指向自身,既主線程的進程描述符 。所以線程組內存在一個線程ID等于進程ID,而該線程即為線程組的主線程 。
一 Linux--多線程

文章插圖
注意: 線程和進程不一樣,進程有父進程的概念,但是在線程組中,所有的線程都是對等關系 。
線程ID和進程地址空間布局pthread_create產生的線程ID和gettid獲得的id不是一回事 。后者屬于進程調度范疇,用來標識輕量級進程 。前者的線程id是一個地址,指向的是一個虛擬內存單元,這個地址就是線程的ID 。屬于線程庫的范疇,線程庫后序對線程操作使用的就是這個ID 。對于目前實現的NPTL而言,pthread_t的類型是線程ID,本質是進程地址空間的一個地址:
一 Linux--多線程

文章插圖
這里的每一個線程ID都代表的是每一個線程控制塊的起始地址,pthread_create返回的就是線程控制塊的起始地址 。這些線程控制塊都是struct pthread類型的,所以所有的線程可以看成是一個大的數組,被描述組織起來 。
線程退出在線程中我們可以調用exit函數或者_exit函數來結束進程,在一個線程中我們可以通過以下三種方式在不終止整個進程的情況下停止它的控制流 。
  • 從線程函數return 。這種方法對主線程不適用,從main函數return相當于調用exit 。
  • 線程可以調用pthread_exit終止自己
  • 一個線程可以調用pthread_ cancel終止同一進程中的另一個線程
注意:線程不能用exit(0)來退出,exit是用來退出進程的,如果在線程中調用exit,那么當線程結束的時候 , 該線程的進程也就結束退出了 。
示例1:return退出線程調度函數
#include <stdio.h>#include <pthread.h>#include <unistd.h>void* pthreadrun(void* arg){  int count = 0;  while (1){    printf(" new threaad is running, pid is %d, thread id is %p\n", getpid(), pthread_self());    sleep(1);    if (count++ == 5){      return (void*)10;    }  }}int main(){  pthread_t thread;  pthread_create(&thread, NULL, pthreadrun, NULL);  while (1){    printf("main thread is running, pid is %d, thread id is %p\n", getpid(), pthread_self());    sleep(1);  }  return 0;}

推薦閱讀