gettid和pthread_self区别

113 篇文章 0 订阅
107 篇文章 1 订阅

gettid和pthread_self区别

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

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

 

#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中有如下调用:

 

#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)
# 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

 

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;
#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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pthread_create函数用于创建一个新的线程,其函数原型为: ```c int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); ``` 其中,参数thread是一个指向pthread_t类型的指针,用于存储新线程的ID;参数attr是一个指向pthread_attr_t类型的指针,用于设置线程的属性,一般为NULL;参数start_routine是一个指向函数的指针,该函数将作为新线程的入口点;参数arg是一个指向void类型的指针,用于传递给start_routine函数的参数。 pthread_self函数用于获取当前线程的ID,其函数原型为: ```c pthread_t pthread_self(void); ``` 下面是一个示例程序,演示了pthread_create函数和pthread_self函数的用法和程序的执行顺序: ```c #include <stdio.h> #include <pthread.h> void *thread_func(void *arg) { pthread_t tid = pthread_self(); printf("New thread created with ID %lu\n", tid); return NULL; } int main() { pthread_t tid; printf("Main thread ID is %lu\n", pthread_self()); pthread_create(&tid, NULL, thread_func, NULL); pthread_join(tid, NULL); return 0; } ``` 程序首先输出了主线程的ID,然后调用pthread_create函数创建了一个新线程,并将其ID存储在tid变量中。pthread_create函数的第三个参数是一个指向函数的指针,该函数将作为新线程的入口点,这里我们传递了thread_func函数的地址。thread_func函数中调用了pthread_self函数获取当前线程的ID,并输出到控制台。最后,主线程调用pthread_join函数等待新线程结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值