`
sunzixun
  • 浏览: 74781 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

rdtsc

阅读更多

 

       static void *
       thread_start(void *arg)
       {
           printf("Subthread starting infinite loop\n");
           for (;;)
               continue;
       }

       static void
       pclock(char *msg, clockid_t cid)
       {
           struct timespec ts;

           printf("%s", msg);
           if (clock_gettime(cid, &ts) == -1)
               handle_error("clock_gettime");
           printf("%4ld.%03ld\n", ts.tv_sec, ts.tv_nsec / 1000000);
       }

       int
       main(int argc, char *argv[])
       {
           pthread_t thread;
           clockid_t cid;
           int j, s;

           s = pthread_create(&thread, NULL, thread_start, NULL);
           if (s != 0)
               handle_error_en(s, "pthread_create");

           printf("Main thread sleeping\n");
           sleep(1);

           printf("Main thread consuming some CPU time...\n");
           for (j = 0; j < 2000000; j++)
               getppid();

           pclock("Process total CPU time: ", CLOCK_PROCESS_CPUTIME_ID);

           s = pthread_getcpuclockid(pthread_self(), &cid);
           if (s != 0)
               handle_error_en(s, "pthread_getcpuclockid");
           pclock("Main thread CPU time:   ", cid);

           /* The preceding 4 lines of code could have been replaced by:
              pclock("Main thread CPU time:   ", CLOCK_THREAD_CPUTIME_ID); */

           s = pthread_getcpuclockid(thread, &cid);
           if (s != 0)
               handle_error_en(s, "pthread_getcpuclockid");
           pclock("Subthread CPU time: 1    ", cid);

           exit(EXIT_SUCCESS);         /* Terminates both threads */
       }

 

 

 pthread_getcpuclockid (threadid, clockid) 
    pthread_t threadid;
     clockid_t *clockid;
{
  struct pthread *pd = (struct pthread *) threadid;

  /* Make sure the descriptor is valid.  */
  if (INVALID_TD_P (pd))
    /* Not a valid thread handle.  */
    return ESRCH;

#ifdef CLOCK_THREAD_CPUTIME_ID
  /* We need to store the thread ID in the CLOCKID variable together
     with a number identifying the clock.  We reserve the low 3 bits
     for the clock ID and the rest for the thread ID.  This is
     problematic if the thread ID is too large.  But 29 bits should be
     fine.
将gettid() 放在了 CLOCKID变量里面
     If some day more clock IDs are needed the ID part can be
     enlarged.  The IDs are entirely internal.  */
  if (pd->tid >= 1 << (8 * sizeof (*clockid) - CLOCK_IDFIELD_SIZE))
    return ERANGE;

  /* Store the number. 用后面3位来保存 C_T_C_I ,如果gettid()太大就失效了所以上面做了判断 */
  *clockid = CLOCK_THREAD_CPUTIME_ID | (pd->tid << CLOCK_IDFIELD_SIZE);

  return 0;
#else
  /* We don't have a timer for that.  */
  return ENOENT;
#endif
}

 

然后看一下 clock_gettime()的  CLOCK_PROCESS_CPUTIME_ID 部分的处理

 

 

int
clock_gettime (clockid_t clock_id, struct timespec *tp)
{
/*检测上面的设置位*/
//...
#if HP_TIMING_AVAIL
      if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1))
	  == CLOCK_THREAD_CPUTIME_ID)
	retval = hp_timing_gettime (clock_id, tp);
      else
#endif
   //...

  return retval;
}

 

 这里先省掉了一些 无关代码。 

在有 timestamp register 的情况下 测试 HP_TIMING_AVAIL 就是ture,

所以我们来看看  

 

 retval = hp_timing_gettime (clock_id, tp);

 

 

这块之前的glibc 还没有独立出来 ,其实一样 关键在于

 

__pthread_clock_gettime(); 

 

先把hp_timing_gettime 放在一边,来看看里面的关键函数 

 

int
__pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq,
			 struct timespec *tp)
{
  hp_timing_t tsc;

  /* Get the current counter.  */
  HP_TIMING_NOW (tsc);

  /* This is the ID of the thread we are looking for.  */
  pid_t tid = ((unsigned int) clock_id) >> CLOCK_IDFIELD_SIZE;

  /* Compute the offset since the start time of the process. 
THREAD_GETMEM 里面存放的是线程启动时候记录下的 counter
所有 */
  if (tid == 0 || tid == THREAD_GETMEM (THREAD_SELF, tid))
    /*  如果调用的不是当前thread, 那就要通过__find_thread_by_id (gettid())来获得 对应的pthread结构,很可惜这个宏被 _attribute__ ((visibility ("hidden"))) 放在了hiddern区 所以只能内部用,外部动态链接没办法使用 。。主要因为涉及了内部太多结构  */
    tsc -= THREAD_GETMEM (THREAD_SELF, cpuclock_offset);
  else
    {
      /* This is more complicated.  We have to locate the thread based
	 on the ID.  This means walking the list of existing
	 threads.  */
      struct pthread *thread = __find_thread_by_id (tid);
//...

      /* There is a race here.  The thread might terminate and the stack
	 become unusable.  But this is the user's problem.  */
      tsc -= thread->cpuclock_offset;
    }

  /* Compute the seconds.  */
  tp->tv_sec = tsc / freq;

  /* And the nanoseconds.  This computation should be stable until
     we get machines with about 16GHz frequency.  */
  tp->tv_nsec = ((tsc % freq) * 1000000000ull) / freq;

  return 0;
}

 

 

对线程结构特定成员的赋值和取值操作 都要通过

THREAD_SETMEM 和THREAD_GETMEM 来操作

 

可以简单看一眼 THREAD_SETMEM

 

# define THREAD_SETMEM(descr, member, value)
//... 
 else if (sizeof (descr->member) == 4)				      \
       asm volatile ("movl %0,%%gs:%P1" :				      \
		     : "ir" (value),					      \
		       "i" (offsetof (struct pthread, member)));	    
 

 

 

先判断成员长度 ,然后 把立即数(ir) value 放在gs段偏移为 offsetof (struct pthread, member))的位置, 也就完成了复制

 

补充一下: 为什么是gs段 :

 

其实这是gs段寄存器 每一个  pthread 都有一下的私有数据,一个是pthread_t类似于PCB ,一个是自身的堆栈可以用pthread_setstack 来设置,还有一个是TLS线程本地数据了 ,这些东西放在一个mmap区域中 ,用各自的gs寄存器地址来指向

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics