开源中文网

您的位置: 首页 > 编程开发 > C++语言编程 > 正文

gettid和pthread_self区别

来源: 千-山的博客  作者: 千-山

1 线程ID获取方法
linux下获取线程有两种方法:

1)gettid或者类似gettid的方法 

2)直接调用pthread_self()

gettid 获取的是内核中线程ID,而pthread_self 是posix描述的线程ID。

通过执行man手册,我们也能发现他们的区别:

SYNOPSIS
       #include <sys/types.h>
       pid_t gettid(void);
       Note: There is no glibc wrapper for this system call; see NOTES.
DESCRIPTION
       gettid()  returns the caller's thread ID (TID).  In a single-threaded process, the thread ID is equal to the process ID (PID, as returned by getpid(2)).  In
       a multithreaded process, all threads have the same PID, but each one has a unique TID.  For further details, see the discussion of CLONE_THREAD in clone(2).

对于单线程的进程,内核中tid==pid,对于多线程进程,他们有相同的pid,不同的tid。tid用于描述内核真实的pid和tid信息。

DESCRIPTION
       The  pthread_self()  function  returns  the ID of the calling thread.  This is the same value that is returned in *thread in the pthread_create(3) call that
       created this thread.
RETURN VALUE
       This function always succeeds, returning the calling thread's ID.
he thread ID returned by pthread_self() is not the same thing as the kernel thread ID returned by a call to gettid(2).
pthread_self返回的是posix定义的线程ID,man手册明确说明了和内核线程tid不同。它只是用来区分某个进程中不同的线程,当一个线程退出后,新创建的线程可以复用原来的id。



2  为什么需要两个ID描述线程?
通过执行如下代码, 我们也能发现他们的区别:


#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
//#include <sys/syscall.h>  
 
#define __NR_gettid 186 
void *f() 

    int status; 
    printf("begin: pid: %d, tid:%ld, self: %ld\n", getpid(), (long int)syscall(__NR_gettid), pthread_self()); 
    int ret = fork(); 
    if(ret == 0){ 
        printf("[child] pid: %d, tid:%ld, self: %ld\n", getpid(), (long int)syscall(__NR_gettid), pthread_self()); 
    }else if(ret > 0){ 
        printf("[parent] pid: %d, tid:%ld, self: %ld\n", getpid(), (long int)syscall(__NR_gettid), pthread_self()); 
        waitpid(-1, &status, 0); 
    } 

 
int main() 

     
    int i = 0; 
    pthread_t pth[1];  
    while(i++<1){ 
        pthread_create(&pth[i], NULL, f, NULL); 
        sleep(1); 
    } 
    pause(); 




描述线程的id,为什么需要两个不同的ID呢?这是因为线程库实际上由两部分组成:内核的线程支持+用户态的库支持(glibc),Linux在早期内核不支持线程的时候glibc就在库中(用户态)以纤程(就是用户态线程)的方式支持多线程了,POSIX thread只要求了用户编程的调用接口对内核接口没有要求。

linux上的线程实现就是在内核支持的基础上以POSIX thread的方式对外封装了接口,所以才会有两个ID的问题。


3 内部实现
glibc中并没有直接提供gettid函数,与之类似的方法是执行系统调用。

在头文件 /usr/include/x86_64-linux-gnu/asm/unistd_64.h 中找到__NR_gettid 的定义:

#define __NR_gettid 186

gettid的包裹实现: syscall(__NR_gettid) 
glibc中有如下调用:

[html] view plain copy
#define CHECK_TPP_PRIORITY(normal, boosted) \ 
  do                                \ 
    {                               \ 
      pid_t tid = syscall (__NR_gettid);            \ 
                                \ 
      struct sched_param cep_sp;                \ 
      int cep_policy;                       \ 
      if (pthread_getschedparam (pthread_self (), &cep_policy,  \ 
                 &cep_sp) != 0)         \ 
    {                           \ 
      puts ("getschedparam failed");            \ 
      ret = 1;                      \ 
    }                           \ 
      else if (cep_sp.sched_priority != (normal))       \ 
    {                           \ 
      printf ("unexpected priority %d != %d\n",     \ 
          cep_sp.sched_priority, (normal));     \ 
    }                           \ 
      if (syscall (__NR_sched_getparam, tid, &cep_sp) == 0  \ 
      && cep_sp.sched_priority != (boosted))        \ 
    {                           \ 
      printf ("unexpected boosted priority %d != %d\n", \ 
          cep_sp.sched_priority, (boosted));        \ 
      ret = 1;                      \ 
    }                           \ 
    }                               \ 
  while (0) 
tid在内核中就是一个普通进程。

在glibc源码中,发现posix中pthread_self的实现如下:


pthread_t 
__pthread_self (void) 

  return (pthread_t) THREAD_SELF; 

strong_alias (__pthread_self, pthread_self) 
[html] view plain copy
# define THREAD_SELF \ 
  ({ struct pthread *__self;                              \ 
     asm ("mov %%fs:%c1,%0" : "=r" (__self)                   \ 
      : "i" (offsetof (struct pthread, header.self)));            \ 
     __self;}) 

struct pthread 

  union 
  { 
#if !TLS_DTV_AT_TP 
    /* This overlaps the TCB as used for TLS without threads (see tls.h).  */ 
    tcbhead_t header; 
#else 
    struct 
    { 
      int multiple_threads; 
      int gscope_flag; 
# ifndef __ASSUME_PRIVATE_FUTEX 
      int private_futex; 
# endif 
    } header; 
#endif 
[html] view plain copy
typedef struct 

  void *tcb;        /* Pointer to the TCB.  Not necessarily the 
               thread descriptor used by libpthread.  */ 
  dtv_t *dtv; 
  void *self;       /* Pointer to the thread descriptor.  */ 
  int multiple_threads; 
  int gscope_flag; 
  uintptr_t sysinfo; 
  uintptr_t stack_guard; 
  uintptr_t pointer_guard; 
  unsigned long int vgetcpu_cache[2]; 
# ifndef __ASSUME_PRIVATE_FUTEX 
  int private_futex; 
# else 
  int __glibc_reserved1; 
# endif 
  int rtld_must_xmm_save; 
  /* Reservation of some values for the TM ABI.  */ 
  void *__private_tm[4]; 
  /* GCC split stack support.  */ 
  void *__private_ss; 
  long int __glibc_reserved2; 
  /* Have space for the post-AVX register size.  */ 
  __128bits rtld_savespace_sse[8][4] __attribute__ ((aligned (32))); 
 
  void *__padding[8]; 
} tcbhead_t; 
[html] view plain copy
#define offsetof(Type, Member) ((size_t) &((Type *) NULL)->Member) 
pthread_self 即是获取线程控制块tcb首地址 相对于进程数据的段的偏移, 注:pthread_create也是返回该值。


4 总结
gettid 获取的是内核中真实线程ID,  对于多线程进程来说,每个tid实际是不一样的。

而pthread_self获取的是相对于进程的线程控制块的首地址, 只是用来描述统一进程中的不同线程,

例子中,在线程中调用fork,只会将当前活动线程设置为活动(其他线程终止),且进程使用的都是虚拟地址,所以产生的pthread_self() 是相同的。

上述不匹配,对程序的实际运行,并没有影响,因为他们的tid是不同的。

Tags:gettid pthread_self
关于开源中文网 - 联系我们 - 广告服务 - 网站地图 - 版权声明