首先感谢如下两篇的blog,让我走出了很大的一个误区:
http://www.cppblog.com/kongque/archive/2011/01/18/138765.aspx
http://blog.csdn.net/zjwoody/article/details/7882240
在我的一个项目中,因为需要与串口通信,每次读写都需要延时usleep(1000)=1ms,但是通信量非常大,每一次工作这样的通信大概有300次左右,这样算下耗时应该是300ms左右。
但是通过strace打印出系统函数调用发现实际接近900ms,仔细观察strace日志才发现,每次usleep(1000000)其实都延时了2ms,之后上网搜索才发现usleep是不精确的。
1.sleep的精度是秒
2.usleep的精度是微妙,不精确
3.select的精度是微妙,精确
struct timeval delay;
delay.tv_sec = 0;
delay.tv_usec = 20 * 1000; // 20 ms
select(0, NULL, NULL, NULL, &delay);
4.nanosleep的精度是纳秒,不精确
unix、linux系统尽量不要使用usleep和sleep而应该使用nanosleep,使用nanosleep应注意判断返回值和错误代码,否则容易造成cpu占用率100%。
这是第一篇blog中提到的,然后第二篇blog中提供的测试代码,本人做了少量改动(原作者没有打印出usleep(0)时的信息),代码如下:
/*
make: gcc -o test_sleep test_sleep.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>
#define PRINT_USEAGE { \
fprintf(stderr,"\n Usage: %s usec ",argv[0]); \
fprintf(stderr,"\n\n");\
}
int main (int argc, char **argv)
{
unsigned int nTimeTestSec = 0; /* sec */
unsigned int nTimeTest = 0; /* usec */
struct timeval tvBegin;
struct timeval tvNow;
int ret = 0;
unsigned int nDelay = 0; /* usec */
fd_set rfds;
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; /* 误差 */
#if 0
if (argc < 2)
{
PRINT_USEAGE;
exit (1);
}
nDelay = atoi (argv[1]);
#endif
fprintf (stderr, "%18s%12s%12s%12s\n", "function", "time(usec)", "realTime",
"reduce");
fprintf (stderr,
"-------------------------------------------------------------------\n");
for (i = 0; i < 11; i++)
{
if (delay[i] < 0)
break;
nDelay = delay[i];
/* test usleep */
gettimeofday (&tvBegin, NULL);
ret = usleep (nDelay);
if (-1 == ret)
{
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 */
gettimeofday (&tvBegin, NULL);
req.tv_sec = nDelay / 1000000;
req.tv_nsec = (nDelay % 1000000) * 1000;
ret = nanosleep (&req, NULL);
if (-1 == ret)
{
fprintf (stderr, "/t nanosleep %8u not support\n", nDelay);
}
else
{
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 */
gettimeofday (&tvBegin, NULL);
FD_ZERO (&rfds);
FD_SET (fd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = nDelay;
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);
}
return 0;
}
程序显示如下:
[root@localhost test]# ./sleep_com
function time(usec) realTime reduce
-------------------------------------------------------------------
/t usleep 500000 501575 1575
/t nanosleep 500000 501861 1861
/t select 500000 499893 -107
/t usleep 100000 101933 1933
/t nanosleep 100000 101957 1957
/t select 100000 99946 -54
/t usleep 50000 51954 1954
/t nanosleep 50000 51962 1962
/t select 50000 49991 -9
/t usleep 10000 11941 1941
/t nanosleep 10000 11973 1973
/t select 10000 9974 -26
/t usleep 1000 2976 1976
/t nanosleep 1000 2974 1974
/t select 1000 993 -7
/t usleep 900 1968 1068
/t nanosleep 900 1978 1078
/t select 900 966 66
/t usleep 500 1971 1471
/t nanosleep 500 1973 1473
/t select 500 992 492
/t usleep 100 1970 1870
/t nanosleep 100 1979 1879
/t select 100 968 868
/t usleep 10 1972 1962
/t nanosleep 10 1974 1964
/t select 10 993 983
/t usleep 1 1969 1968
/t nanosleep 1 1983 1982
/t select 1 960 959
/t usleep 0 988 988
/t nanosleep 0 961 961
/t select 0 5 5
/t usleep 0 971 971
通过上表可以看出usleep(1000)实际 延时将近3ms。