Linux 執行緒ID
在沒有談到執行緒前,我們認為一個程序對應的是一個程序描述符PCB,對應一個程序ID。但現在我們引入了執行緒的概念後,一個使用者程序可以包含多個使用者態執行緒,每個執行緒作為一個獨立的排程實體在核心態都有自己的程序描述符PCB,因此Linux核心為了處理以上關係,引入執行緒組的概念。
我們之前在學習程序的過程中,學過一個函式getpid,作用是獲得當前程序的ID,同樣,也有一個函式gettid可以獲得執行緒ID,我們來了解這個函式。
檢視執行緒ID:
介紹幾個重要引數
PID:當前程序ID
PPID:當前程序的父程序ID
LWP:執行緒ID
NLWP:執行緒組內執行緒的個數
可以看出當前程序是多執行緒的,程序ID為2715,程序內有2個執行緒,執行緒ID分別為2715、2716,其中執行緒ID和程序ID一致的可以認為是主執行緒。所有程序的父程序都是bash。但是注意一點,執行緒是對等的,沒有類似於程序中的父程序的概念。
同一個執行緒組的執行緒,沒有層次關係。
關於執行緒ID獲取的幾個方法我們做以下的測試:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/syscall.h>
void* PrintPid(void* param)//子執行緒函式,用於列印子執行緒的資源
{
printf("child getpid()=%d,syscall(__NR_gettid)=%ld,\
pthread_self()=%ld\n",getpid(),\
(long int)syscall(__NR_gettid),pthread_self());
}
int main()
{
//列印主執行緒的資源
printf("main getpid()=%ld,syscall(__NR_gettid)=%ld,\
pthread_self()=%ld\n",getpid(),\
(long int)syscall(__NR_gettid),pthread_self());
pthread_t tid = 0;
//建立子執行緒
int ret = pthread_create(&tid, NULL, PrintPid, NULL);
if(ret == 0) {
printf("child tid=%ld\n",tid);
}else{
printf("create PrintPid is failed!\n");
}
while(1)
{
sleep(5);
}
return 0;
}
編譯後,我們看一下執行結果:
從執行結果我們可以看出,主執行緒和子執行緒呼叫的getpid返回值相同,syscall(__NR_gettid)的系統呼叫的結果不同,pthread_self的呼叫結果也不同。但子執行緒下的pthread_self返回值和主執行緒的tid值是相同的。那這是什麼原因呢?為什麼會得到的執行緒ID會出現不一致呢?
我們來解釋一下:
從getpid函式說起,函式原型是pid_t getpid(void)。
該函式返回的是當前程序的程序ID,即pid(int)型的程序識別碼,通常情況下,不論該函式在哪一個執行緒下執行,都獲取的是當前主執行緒的執行緒ID,同時也是當前程序ID。
syscall(__NR_gettid)系統呼叫,同syscall(SYS_gettid)。
該函式用於獲取當前執行緒的執行緒ID,該系統呼叫底層使用的是gettid函式,而該函式在glibc下是不提供的。
pthread_self函式為獲取當前執行緒自身的ID,返回值和pthread_create函式呼叫返回的pthread_t的值是相同的,即子執行緒下的pthread_self返回值和主執行緒的tid是相同的。
那麼,syscall(__NR_gettid)和pthread_self()的返回值都是執行緒ID,但結果為什麼會不同?
執行緒庫其實由兩部分組成:核心的執行緒支援、使用者的執行緒支援(glibc),Linux早期核心不支援執行緒時,glibc就在庫中(使用者態)以纖程(使用者態執行緒)的方式支援多執行緒了,POSIX thread只要求了使用者程式設計的呼叫介面對核心介面沒有要求。Linux下執行緒的實現就是在核心支援的基礎上以POSIX thread的方式對外封裝了介面,所以才會有兩個ID。