• gettid和pthread_self区别


    http://blog.csdn.net/rsyp2008/article/details/45150621

    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描述线程?

    通过执行如下代码, 我们也能发现他们的区别:

    [cpp] view plain copy
    1. #include <stdio.h>  
    2. #include <unistd.h>  
    3. #include <stdlib.h>  
    4. #include <pthread.h>  
    5. #include <sys/types.h>  
    6. #include <sys/wait.h>  
    7. //#include <sys/syscall.h>   
    8.   
    9. #define __NR_gettid 186  
    10. void *f()  
    11. {  
    12.     int status;  
    13.     printf("begin: pid: %d, tid:%ld, self: %ld ", getpid(), (long int)syscall(__NR_gettid), pthread_self());  
    14.     int ret = fork();  
    15.     if(ret == 0){  
    16.         printf("[child] pid: %d, tid:%ld, self: %ld ", getpid(), (long int)syscall(__NR_gettid), pthread_self());  
    17.     }else if(ret > 0){  
    18.         printf("[parent] pid: %d, tid:%ld, self: %ld ", getpid(), (long int)syscall(__NR_gettid), pthread_self());  
    19.         waitpid(-1, &status, 0);  
    20.     }  
    21. }  
    22.   
    23. int main()  
    24. {  
    25.       
    26.     int i = 0;  
    27.     pthread_t pth[1];   
    28.     while(i++<1){  
    29.         pthread_create(&pth[i], NULL, f, NULL);  
    30.         sleep(1);  
    31.     }  
    32.     pause();  
    33. }  

    描述线程的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

    [cpp] view plain copy
    1. gettid的包裹实现: syscall(__NR_gettid)  

    glibc中有如下调用:

    [html] view plain copy
    1. #define CHECK_TPP_PRIORITY(normal, boosted)   
    2.   do                                  
    3.     {                                 
    4.       pid_t tid = syscall (__NR_gettid);              
    5.                                   
    6.       struct sched_param cep_sp;                  
    7.       int cep_policy;                         
    8.       if (pthread_getschedparam (pthread_self (), &cep_policy,    
    9.                  &cep_sp) != 0)           
    10.     {                             
    11.       puts ("getschedparam failed");              
    12.       ret = 1;                        
    13.     }                             
    14.       else if (cep_sp.sched_priority != (normal))         
    15.     {                             
    16.       printf ("unexpected priority %d != %d ",       
    17.           cep_sp.sched_priority, (normal));       
    18.     }                             
    19.       if (syscall (__NR_sched_getparam, tid, &cep_sp) == 0    
    20.       && cep_sp.sched_priority != (boosted))          
    21.     {                             
    22.       printf ("unexpected boosted priority %d != %d ",   
    23.           cep_sp.sched_priority, (boosted));          
    24.       ret = 1;                        
    25.     }                             
    26.     }                                 
    27.   while (0)  

    tid在内核中就是一个普通进程。

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

    [cpp] view plain copy
    1. pthread_t  
    2. __pthread_self (void)  
    3. {  
    4.   return (pthread_t) THREAD_SELF;  
    5. }  
    6. strong_alias (__pthread_self, pthread_self)  
    [html] view plain copy
    1. # define THREAD_SELF   
    2.   ({ struct pthread *__self;                                
    3.      asm ("mov %%fs:%c1,%0" : "=r" (__self)                     
    4.       : "i" (offsetof (struct pthread, header.self)));              
    5.      __self;})  
    [cpp] view plain copy
    1. struct pthread  
    2. {  
    3.   union  
    4.   {  
    5. #if !TLS_DTV_AT_TP  
    6.     /* This overlaps the TCB as used for TLS without threads (see tls.h).  */  
    7.     tcbhead_t header;  
    8. #else  
    9.     struct  
    10.     {  
    11.       int multiple_threads;  
    12.       int gscope_flag;  
    13. # ifndef __ASSUME_PRIVATE_FUTEX  
    14.       int private_futex;  
    15. # endif  
    16.     } header;  
    17. #endif  
    [html] view plain copy
    1. typedef struct  
    2. {  
    3.   void *tcb;        /* Pointer to the TCB.  Not necessarily the  
    4.                thread descriptor used by libpthread.  */  
    5.   dtv_t *dtv;  
    6.   void *self;       /* Pointer to the thread descriptor.  */  
    7.   int multiple_threads;  
    8.   int gscope_flag;  
    9.   uintptr_t sysinfo;  
    10.   uintptr_t stack_guard;  
    11.   uintptr_t pointer_guard;  
    12.   unsigned long int vgetcpu_cache[2];  
    13. # ifndef __ASSUME_PRIVATE_FUTEX  
    14.   int private_futex;  
    15. # else  
    16.   int __glibc_reserved1;  
    17. # endif  
    18.   int rtld_must_xmm_save;  
    19.   /* Reservation of some values for the TM ABI.  */  
    20.   void *__private_tm[4];  
    21.   /* GCC split stack support.  */  
    22.   void *__private_ss;  
    23.   long int __glibc_reserved2;  
    24.   /* Have space for the post-AVX register size.  */  
    25.   __128bits rtld_savespace_sse[8][4] __attribute__ ((aligned (32)));  
    26.   
    27.   void *__padding[8];  
    28. } tcbhead_t;  
    [html] view plain copy
    1. #define offsetof(Type, Member) ((size_t) &((Type *) NULL)->Member)  

    pthread_self 即是获取线程控制块tcb首地址 相对于进程数据的段的偏移, 注:pthread_create也是返回该值。

    4 总结

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

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

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

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

    本文中有关线程模型的基础知识,请参见:

    http://www.ibm.com/developerworks/cn/linux/l-threading.html

  • 相关阅读:
    JAVA基础(十六)Super关键字
    JAVA基础(十五)this关键字
    JAVA基础(十四2.0)
    JAVA基础(十三)多态
    JAVA基础(十二)抽象类与接口
    git基本使用
    vuetify中treeview部分属性梳理
    vuetify初次使用心得
    react-常见面试题
    maven中profile的使用
  • 原文地址:https://www.cnblogs.com/feng9exe/p/7047981.html
Copyright © 2020-2023  润新知