最近在使用nanosleep的時(shí)候又踩坑了。于是整理下linux短延遲的用法。
用法
回顧下秒的換算:ms(毫秒),μs(微秒),ns(納秒),ps(皮秒)
1s = 1000ms = 1000 * 1000us = 1000 * 1000 * 1000ns = 1000 * 1000 * 1000* 1000ps
sleep()-------以秒為單位
#include<unistd.h>
unsigned int sleep(unsigned int seconds);
return:若進(jìn)程暫停到參數(shù)seconds 所指定的時(shí)間,成功則返回0,若有信號(hào)中斷則返回剩余秒數(shù)。
在linux中,sleep是通過nanosleep實(shí)現(xiàn)的。在一些其他系統(tǒng)中(例如POSIX.1),它是通過alarm()來實(shí)現(xiàn)的。
usleep()----以微秒為單位
#include<unistd.h>
unsigned int usleep(unsigned int useconds);
return:若進(jìn)程暫停到參數(shù)seconds 所指定的時(shí)間,成功則返回0,若有信號(hào)中斷則返回剩余微秒數(shù)。
nanosleep( )---------以納秒為單位
#include<time.h>
struct timespec
{
time_t tv_sec; /* 秒seconds */
long tv_nsec; /* 納秒nanoseconds */
};
int nanosleep(const struct timespec *req, struct timespec *rem);
return: 若進(jìn)程暫停到參數(shù)*req所指定的時(shí)間,成功則返回0,若有信號(hào)中斷則返回-1,并且將剩余微秒數(shù)記錄在*rem中。
req->tv_sec是以秒為單位,而tv_nsec以毫微秒為單位(10的-9次方秒)。
由于調(diào)用nanosleep是是進(jìn)程進(jìn)入TASK_INTERRUPTIBLE,這種狀態(tài)是會(huì)相應(yīng)信號(hào)而進(jìn)入TASK_RUNNING狀態(tài)的。
函數(shù)的精確度與時(shí)鐘的頻率有關(guān)系:
我們假設(shè)時(shí)鐘中斷是10納秒一次,如果tv_sec = 0, tv_nsec = 2,那么時(shí)鐘中斷一定是在10納秒后來喚醒這個(gè)進(jìn)程的,這里我們看到任務(wù)的重新調(diào)度最少是在10納秒之上,因此此函數(shù)的精確程度與時(shí)鐘頻率有關(guān)系。
cpu的速度決定了時(shí)鐘周期; 如, 一個(gè) 50 MHz 的CPU, 一個(gè)時(shí)鐘周期的時(shí)間是 1/50000000 s(200 nsec)。
注意
使用這些函數(shù)時(shí)一定要注意判斷返回值。有時(shí)候會(huì)出現(xiàn)sleep函數(shù)被系統(tǒng)中斷的情況,導(dǎo)致結(jié)果不符合預(yù)期。
NANOSLEEP(2) BUGS
// POSIX nanosleep may be interrupted by signals.
while (nanosleep(&ts, &ts) == -1 && errno == EINTR) {}
精確度對(duì)比
低精度情況(100000us及以上):usleep和nanosleep表現(xiàn)差不多。select和pselect表現(xiàn)較差。
高精度情況(100000us及以上):四者表現(xiàn)差不多。
fuction time(usec) realtime reduce
----------------------------------------------------
usleep 500000 500091 91
nanosleep 500000 500089 89
select 500000 500540 540
pselect 500000 500549 549
--------------------------------
usleep 100000 100078 78
nanosleep 100000 100110 110
select 100000 100157 157
pselect 100000 100149 149
--------------------------------
usleep 50000 50091 91
nanosleep 50000 50107 107
select 50000 50111 111
pselect 50000 50084 84
--------------------------------
usleep 10000 10086 86
nanosleep 10000 10091 91
select 10000 10089 89
pselect 10000 10088 88
--------------------------------
usleep 1000 1089 89
nanosleep 1000 1065 65
select 1000 1065 65
pselect 1000 1066 66
--------------------------------
usleep 900 969 69
nanosleep 900 974 74
select 900 970 70
pselect 900 980 80
--------------------------------
usleep 500 569 69
nanosleep 500 565 65
select 500 569 69
pselect 500 569 69
--------------------------------
usleep 100 166 66
nanosleep 100 165 65
select 100 163 63
pselect 100 163 63
--------------------------------
usleep 10 73 63
nanosleep 10 76 66
select 10 73 63
pselect 10 78 68
--------------------------------
usleep 1 64 63
nanosleep 1 66 65
select 1 65 64
pselect 1 63 62
--------------------------------
測(cè)試代碼:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<sys/time.h>
#include<errno.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/select.h>
int main(int argc, char **argv)
{
unsigned int nTimeTestSec = 0;
unsigned int nTimeTest = 0;
struct timeval tvBegin;
struct timeval tvNow;
int ret = 0;
unsigned int nDelay = 0;
struct timeval tv;
int fd = 1;
int i = 0;
struct timespec req;
unsigned int delay[20] =
{500000, 100000, 50000, 10000, 1000, 900, 500, 100, 10, 1, 0};
int nReduce = 0; //誤差
fprintf(stderr, "%19s%12s%12s%12s\n", "fuction", "time(usec)", "realtime", "reduce");
fprintf(stderr, "----------------------------------------------------\n");
for (i = 0; i < 20; i++)
{
if (delay[i] <= 0)
break;
nDelay = delay[i];
//test sleep
gettimeofday(&tvBegin, NULL);
ret = usleep(nDelay);
if(ret == -1)
{
fprintf(stderr, "usleep error, errno=%d [%s]\n", errno, strerror(errno));
}
gettimeofday(&tvNow, NULL);
nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t usleep %8u %8u %8d\n", nDelay, nTimeTest,nReduce);
//test nanosleep
req.tv_sec = nDelay/1000000;
req.tv_nsec = (nDelay%1000000) * 1000;
gettimeofday(&tvBegin, NULL);
ret = nanosleep(&req, NULL);
if (-1 == ret)
{
fprintf (stderr, "\t nanousleep %8u not support\n", nDelay);
}
gettimeofday(&tvNow, NULL);
nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t nanosleep %8u %8u %8d\n", nDelay, nTimeTest,nReduce);
//test select
tv.tv_sec = 0;
tv.tv_usec = nDelay;
gettimeofday(&tvBegin, NULL);
ret = select(0, NULL, NULL, NULL, &tv);
if (-1 == ret)
{
fprintf(stderr, "select error. errno = %d [%s]\n", errno, strerror(errno));
}
gettimeofday(&tvNow, NULL);
nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t select %8u %8u %8d\n", nDelay, nTimeTest,nReduce);
//pselcet
req.tv_sec = nDelay/1000000;
req.tv_nsec = (nDelay%1000000) * 1000;
gettimeofday(&tvBegin, NULL);
ret = pselect(0, NULL, NULL, NULL, &req, NULL);
if (-1 == ret)
{
fprintf(stderr, "select error. errno = %d [%s]\n", errno, strerror(errno));
}
gettimeofday(&tvNow, NULL);
nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t pselect %8u %8u %8d\n", nDelay, nTimeTest,nReduce);
fprintf (stderr, "--------------------------------\n");
}
return 0;
}