在linux上获得线程id的方法

在linux2.4版本后,linux使用了NPTL作为自己的线程库,为了兼容POSIX标准,所以在内核task中有两个域tgid和tid,前者是进程id,后者是线程id。在linux上获得线程id的方法,目前我所知的有三种,当然这里的三种是指在用户态的程序中,否则除非自己写的kernel module, 都是调用编号224的系统调用实现的(2.6版本)。 

第一种: gettid(), man gettid 可以看到gettid的使用方式。 

使用时要先定义:_syscall0(pid_t, gettid) 

其中_syscall0是一个宏(由于参数的不同还有_syscall1,_syscall2...),定义如下: 

#define _syscall0(type,name) \ 
type name(void) \ 
{ \ 
long __res; \ 
__asm__ volatile ("int $0x80" \   //int 80, 软中断 
        : "=a" (__res) \          //输入输出都用的eax 
        : "0" (__NR_##name)); \   //#define __NR_gettid 224 
__syscall_return(type,__res); \   //返回tid 
} 

编译时,宏展开之后,相当于定义了一个pid_t gettid(void)函数,用内嵌汇编实现,在程序中就可以使用gettid()获得线程id了。 


第二种:syscall(), 名字叫syscall(),却是glibc中的库函数。 (recommend)

使用方式:syscall(__NR_gettid), 其中__NR_gettid就是224,同上。 
syscall的实现要到glibc中去找,不同的硬件平台有不同的实现版本,在i386上的实现在syscall.S中: 

#include  
.text 
ENTRY (syscall) 
PUSHARGS_6 /* Save register contents.  */ 
_DOARGS_6(44) /* Load arguments.  */ 
movl 20(%esp), %eax /* Load syscall number into %eax.  */ 
ENTER_KERNEL /* Do the system call.  */ 
POPARGS_6 /* Restore register contents.  */ 
cmpl $-4095, %eax /* Check %eax for error.  */ 
jae SYSCALL_ERROR_LABEL /* Jump to error handler if error.  */ 
L(pseudo_end): 
ret /* Return to caller.  */ 
PSEUDO_END (syscall) 

eg:

#include 
#include 
#include 
#include 
#include 
#include  /* must include this file */

pid_t gettid(void)
{
	return syscall(SYS_gettid);
}

/* thread */
void *thread_function(void *arg)
{
	static int i = 0;
	int j;

	i++;
	j = i;
	while (1) {
		printf("thread: %5u\n", gettid());
		sleep(1);
	}
}


int main()
{
	int res;
	pthread_t a_thread;
	pthread_t b_thread;
	void *thread_result;


	res = pthread_create(&a_thread, NULL, thread_function, NULL);
	if (res != 0) {
		perror("Thread creadtion failed");
		exit(EXIT_FAILURE);
	}

	res = pthread_create(&b_thread, NULL, thread_function, NULL);
	if (res != 0) {
		perror("Thread creadtion failed");
		exit(EXIT_FAILURE);
	}

	printf("[%d]waiting for thread to finish...\n", getpid());
	pthread_join(a_thread, &thread_result);
	if (res != 0){
		perror("Thread a join failed");
		exit(EXIT_FAILURE);
	}
	printf("Thread joined, it returned %s\n", (char *)thread_result);

	pthread_join(b_thread, &thread_result);
	if (res != 0){
		perror("Thread b join failed");
		exit(EXIT_FAILURE);
	}
	printf("Thread joined, it returned %s\n", (char *)thread_result);

	exit(EXIT_SUCCESS);

}

第三种:pthread_self() 



同样是一个glibc提供的函数,在linux的manual中说返回的是当前线程的thread ID.但是实际你看到的是一个很长的,似乎没有规律的值。什么原因得看看它的实现: 
在glibc中,pthread_self()返回的是THREAD_SELF,这又是一个宏 
定义如下 
# define THREAD_SELF \ 
  ({ struct pthread *__self;      \ 
      asm ("movl %%gs:%c1,%0" : "=r" (__self)      \ 
 : "i" (offsetof (struct pthread, header.self)));      \ 
      __self;}) 


这段代码返回了当前线程的descriptor,pthread_self()得到的就是这个descriptor的地址, 也就是unsigned long int类型的pthread_t。知道了这一点就好办了,找到thread descriptor的定义: 
struct pthread 

... 
         pid_t tid; 
... 



接下来知道怎么做了吗?算好长度n,构造一个假的pthread结构。 
struct pthread_fake 

void *nothing[n]; 
pid_t tid; 
}; 

用(struct pthread_fake *) pthread_self()->tid得到线程id了 
相比前两种做法,这种无疑是最繁琐的,但是同理,可以获取很多glibc中维护了,但是没有提供访问方法的数据。


原文:http://blog.chinaunix.net/space.php?uid=223060&do=blog&id=2127436

你可能感兴趣的:(Linux系统编程)