在編寫程式時,我們經常會用到定時器。首先看看select函數原型如下:
複製代碼 代碼如下:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
參數說明:
slect的第一個參數nfds為fdset集合中最大描述符值加1,fdset是一個位元組,其大小限制為__FD_SETSIZE(1024),位元組的每一位代表其對應的描述符是否需要被檢查。
select的第二三四個參數表示需要關注讀、寫、錯誤事件的檔案描述符位元組,這些參數既是輸入參數也是輸出參數,可能會被核心修改用於標示哪些描述符上發生了關注的事件。所以每次調用select前都需重新初始化fdset。
timeout參數為逾時時間,該結構會被核心修改,其值為逾時剩餘的時間。
利用select實現定時器,需要利用其timeout參數,注意到:
1)select函數使用了一個結構體timeval作為其參數。
2)select函數會更新timeval的值,timeval保持的值為剩餘時間。
如果我們指定了參數timeval的值,而將其他參數都置為0或者NULL,那麼在時間耗盡後,select函數便返回,基於這一點,我們可以利用select實現精確定時。
timeval的結構如下:
複製代碼 代碼如下:
struct timeval{
long tv_sec;/*secons*
long tv_usec;/*microseconds*/
}
我們可以看出其精確到microseconds也即微妙。
一、秒級定時器
複製代碼 代碼如下:
void seconds_sleep(unsigned seconds){
struct timeval tv;
tv.tv_sec=seconds;
tv.tv_usec=0;
int err;
do{
err=select(0,NULL,NULL,NULL,&tv);
}while(err<0 && errno==EINTR);
}
二、毫秒層級定時器
複製代碼 代碼如下:
void milliseconds_sleep(unsigned long mSec){
struct timeval tv;
tv.tv_sec=mSec/1000;
tv.tv_usec=(mSec%1000)*1000;
int err;
do{
err=select(0,NULL,NULL,NULL,&tv);
}while(err<0 && errno==EINTR);
}
三、微妙層級定時器
複製代碼 代碼如下:
void microseconds_sleep(unsigned long uSec){
struct timeval tv;
tv.tv_sec=uSec/1000000;
tv.tv_usec=uSec%1000000;
int err;
do{
err=select(0,NULL,NULL,NULL,&tv);
}while(err<0 && errno==EINTR);
}
現在我們來編寫幾行代碼看看定時效果吧。
複製代碼 代碼如下:
#include <stdio.h>
#include <sys/time.h>
#include <errno.h>
int main()
{
int i;
for(i=0;i<5;++i){
printf("%d\n",i);
//seconds_sleep(1);
//milliseconds_sleep(1500);
microseconds_sleep(1900000);
}
}
註:timeval結構體中雖然指定了一個微妙層級的解析度,但核心支援的分別率往往沒有這麼高,很多unix核心將逾時值向上舍入成10ms的倍數。此外,加上核心調度延時現象,即定時器時間到後,核心還需要花一定時間調度相應進程的運行。因此,定時器的精度,最終還是由核心支援的分別率決定。
分類: Linux