Linux 系统下线程ID概念讲解,linux线程


在没有谈到线程前,我们认为一个进程对应的是一个进程描述符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 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 

 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。

相关内容