天天看點

時鐘程式設計: alarm和setitimer

alarm

我們知道,sleep(n)函數可将程序挂起n秒,而其在實作上主要依靠alarm系統調用,sleep的工作原理有3部分組成:

1、為SIGALRM設定一個處理函數; —->SIGALRM為alarm計時n秒後發出的信号

2、調用alarm(n); —->設定一個計時器

3、調用pause. —->程序一直阻塞在pause,而pause函數隻有在收到信号時才傳回。

在程序中alarm設定計時器到n秒後激發信号。當設定的時間過去之後,核心發送SIGALRM到程序。如果在調用alarm時計時器已經被設定,則alarm傳回剩餘秒數。如果調用alarm(0)意味着關掉鬧鐘。

pause函數挂起程序直到收到信号,如果接收到的信号是将這個程序中止,則程序中止,pause無法傳回。如果信号處理是忽略,則程序繼續挂起,而pause不傳回。如果信号處理是捕捉,則在調用信号處理函數之後傳回-1。永遠都是無法正常傳回的,因為pause的目的是無限挂起。

由以上可以看出,linux的函數在設計時是多麼的精妙。簡簡單單的一個sleep函數,拆解出來就是三部分,1、無限挂起2、計時器3、實作異步處理的信号。這三個部分都是十分子產品化的部分,通過組合拼裝可以形成其他功能,而其各自由于功能單一可以不斷深化。子產品化的思維展現地淋漓盡緻。

setitimer

由于alarm的精度機關是秒,難以滿足許多要求,于是引入了setitimer間隔計時器,以毫秒為機關,同時還支援間隔計時,不必循環。

result = setitimer(int which, const struct itimerval *newval, struct itimerval *oldval)

其中which代表計時器的編碼。計算機中有三種計時器(ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF)。其中後兩種與cpu的使用者态和核心态有關,暫不考慮。ITIMER_REAL則是真實時間。

newval是函數特有的struct itimeval,it_value儲存初始間隔,it_interval儲存重複間隔,定義如下:

struct itimerval {
    struct timeval it_interval;
    struct timeval it_value;
};
struct timeval {
    time_t tv_sec;
    suseconds_t tv_usec;
};
           

其中timeval結構體是提高精度展現,suseconds_t儲存毫秒值。setitimer同alarm的作用類似,同樣時通過計時向程序發送信号,隻是前者具有循環間隔發送的功能。

時鐘

在上面我曾說過sleep通過簡化可拆解成計時器等部分。如果我們深入探究,計時器又是如何實作的呢?系統内有無數個程序需要計時,不可能為每一個程序都設定一個計時器,那麼又是如何簡化下去的呢?

計算機内有一個硬體時鐘,能夠每隔固定的時間片段産生脈沖,是以時間就可以分為n個時間片段。當程序設定好計數的時間之後,作業系統會将其按照自身每秒的跳數計數。假設系統時針每秒100跳,程序計時5s,則作業系統設定計數器為500。

每當核心收到系統時間的脈沖,它就周遊所有的計時器,将其計數器減去一個時鐘機關,當計數器達到0時,則發送信号SIGALRM給程序。

到了硬體層面一切都是那麼地簡潔和必然。