1. 程式人生 > >Linux下從訊號量看執行緒排程時間

Linux下從訊號量看執行緒排程時間

前幾天寫了一篇文章關於Linux下程序排程時間的,本意是想測試下實時效能的,包括中斷響應時間等等,這個可能需要藉助於硬體發出終端來測試,

那片文章是講的是通過傳送訊號給另一個程序,然後測量傳送訊號到進入訊號處理程式之間的時間

訊號只是針對程序來說的,今天講的主要是通過訊號量semaphore來測試一下執行緒間切換的時間

首先看下基礎知識:

1.Linux下的程序和執行緒

 linux下執行緒分為使用者級執行緒和核心級執行緒,在核心來看,執行緒和程序是一樣的,本質上沒有區別

 核心提供的是建立程序的介面do_fork()。核心提供了兩個系統呼叫clone()和fork(),最終都用不同的引數呼叫do_fork()核內API。當然,要想實現執行緒,沒有核心對多程序(

其實是輕量級程序)共享資料段的支援是不行的,因此,do_fork()提供了很多引數,包括CLONE_VM(共享記憶體空間)、CLONE_FS(共享檔案系統資訊)、 CLONE_FILES(共享檔案描述符表)、CLONE_SIGHAND(共享訊號控制代碼表)和CLONE_PID(共享程序ID,僅對核內程序,即0號程序有效)。當使用fork系統呼叫時,核心呼叫do_fork()不使用任何共享屬性,程序擁有獨立的執行環境,而使用 pthread_create()來建立執行緒時,則最終設定了所有這些屬性來呼叫__clone(),而這些引數又全部傳給核內的do_fork(),從而建立的“程序”擁有共享的執行環境,只有棧是獨立的,由__clone()傳入。

具體可以參考<<深入理解Linux核心>>第三版,講的非常詳細

2. Linux下的訊號量

訊號量和互斥量一般用來同步訪問某個全域性變數等,防止多個執行緒同時操作一個全域性物件,互斥量mutex就是一把鎖,訊號量還有一個用途就是用來出發另外一個執行緒。訊號量samophore本質上是一個計數器,當其大於0時可以訪問,小於0等於0時不能訪問,相應訪問訊號量的執行緒掛起直到其大於0 。

怎麼由一個執行緒出發另外一個執行緒,先來看看下面兩個函式:

函式sem_post( sem_t *sem )用來增加訊號量的值。當有執行緒阻塞在這個訊號量上時,呼叫這個函式會使其中的一個執行緒不在阻塞,選擇機制同樣是由執行緒的排程策略決定的。  

函式sem_wait( sem_t *sem )被用來阻塞當前執行緒直到訊號量sem的值大於0,解除阻塞後將sem的值減一,表明公共資源經使用後減少。函式sem_trywait ( sem_t *sem )是函式sem_wait()的非阻塞版本,它直接將訊號量sem的值減一。  

當一個執行緒呼叫sem_wait()函式等待一個訊號量,而這個訊號量不大於0,則這個執行緒就會被核心掛起,此時我們可以用另外一個執行緒呼叫sem_post()函式來增加那個訊號量的值到大於0,那麼剛才那個被阻塞的執行緒就會被執行,這樣就觸發了另外一個執行緒,我們可以用這種辦法來測試Linux下執行緒排程的大概時間,考慮時間精度,我們使用了TSC計數器,該計數器可以提供CPU主頻精度的時間,下面看原始碼

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>


int Number = 0;
sem_t sem;
int64_t end = 0;
int64_t start = 0;
float CPU_tick_count_per_second = 3093.059;


#if defined(__i386__)
static __inline__ unsigned long long rdtsc(void)
{
  unsigned long long int x;
     __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); 
     return x;                                                               //
}  
                                                                              
#elif defined(__x86_64__)
static __inline__ unsigned long long rdtsc(void)
{
  unsigned hi, lo;
  __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
  return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#endif




static void pthread_fun_1(void)
{
 while(1)
  {
   printf("This is pthread 1.\n");
   sleep(1);
   sem_post(&sem);      //執行緒1傳送訊號量sem,也就是將該訊號量加1
   start = rdtsc();
  }
 pthread_exit(0);
}


static void pthread_fun_2(void)
{
 while(1)
  {
   printf("This is pthread 2.\n");
   sem_wait(&sem);       //執行緒2等待sem訊號量
   end = rdtsc();           //計算從傳送訊號量到執行緒2被重新執行的時間
   printf("The time shift between sigpost and sigwait is:%f us\n",(end-start)/CPU_tick_count_per_second);
   Number++;
   printf("Thread2 add one to Number.\n");
   printf("The Number is:%d.\n", Number);
  } 
 pthread_exit(0);
}

int main()
{
 pthread_t pt1, pt2;
 sem_init(&sem, 0, 1);
 int ret = 0;
 ret =  pthread_create(&pt1, NULL, (void *)pthread_fun_1, NULL);
 if(ret)
  {
   perror("Creat pthread 1 failed.\n");
   exit(-1);
  }
 ret = pthread_create(&pt2, NULL, (void *)pthread_fun_2, NULL);
 if(ret)
  {
   perror("Creat pthread 2 failed.\n");
   exit(-1);
  }
 pthread_join(pt1,NULL);
 pthread_join(pt2,NULL);
 printf("Main pthread exit.\n");
 return 0;
}

x下面是測試的時間結果,在us數量級

參考資料:

2. 深入理解linux核心

3. APUE


閱讀(414) | 評論(0) | 轉發(1) | 給主人留下些什麼吧!~~ 評論熱議