现在的位置: 首页 > 综合 > 正文

[Pthread] Linux上获得线程Id的方法

2013年12月11日 ⁄ 综合 ⁄ 共 2100字 ⁄ 字号 评论关闭
在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中的库函数。
    使用方式:syscall(__NR_gettid), 其中__NR_gettid就是224,同上。
    syscall的实现要到glibc中去找,不同的硬件平台有不同的实现版本,在i386上的实现在syscall.S中:
    #include <sysdep.h>
    .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)
    其中ENTRY也是一个宏,展开了相当的长,主要用于在链接的时候让gcc能够"看见"并调用这段用汇编写成的syscall()函数。

第三种: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中维护了,但是没有提供访问方法的数据。

Pthread  07/12/13

抱歉!评论已关闭.