欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux 线程ID

程序员文章站 2022-05-03 19:19:49
...

在没有谈到线程前,我们认为一个进程对应的是一个进程描述符PCB,对应一个进程ID。但现在我们引入了线程的概念后,一个用户进程可以包含多个用户态线程,每个线程作为一个独立的调度实体在内核态都有自己的进程描述符PCB,因此Linux内核为了处理以上关系,引入线程组的概念。

我们之前在学习进程的过程中,学过一个函数getpid,作用是获得当前进程的ID,同样,也有一个函数gettid可以获得线程ID,我们来了解这个函数。
Linux 线程ID
查看线程ID:
Linux 线程ID
介绍几个重要参数
PID:当前进程ID
PPID:当前进程的父进程ID
LWP:线程ID
NLWP:线程组内线程的个数

可以看出当前进程是多线程的,进程ID为2715,进程内有2个线程,线程ID分别为2715、2716,其中线程ID和进程ID一致的可以认为是主线程。所有进程的父进程都是bash。但是注意一点,线程是对等的,没有类似于进程中的父进程的概念。
Linux 线程ID
同一个线程组的线程,没有层次关系。

关于线程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;
 }      

编译后,我们看一下执行结果:
Linux 线程ID
从运行结果我们可以看出,主线程和子线程调用的getpid返回值相同,syscall(__NR_gettid)的系统调用的结果不同,pthread_self的调用结果也不同。但子线程下的pthread_self返回值和主线程的tid值是相同的。那这是什么原因呢?为什么会得到的线程ID会出现不一致呢?

我们来解释一下:
getpid函数说起,函数原型是pid_t getpid(void)。
该函数返回的是当前进程的进程ID,即pid(int)型的进程识别码,通常情况下,不论该函数在哪一个线程下执行,都获取的是当前主线程的线程ID,同时也是当前进程ID。
Linux 线程ID
syscall(__NR_gettid)系统调用,同syscall(SYS_gettid)。
该函数用于获取当前线程的线程ID,该系统调用底层使用的是gettid函数,而该函数在glibc下是不提供的。
Linux 线程ID
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。

相关标签: 线程ID