关于各种延时
Linux,如果是应用层下的一些应用,我们可以:
1)调用unsigned int sleep(unsigned int second);函数去定时,这个时候它是秒级的;头文件为<unistd.h>
2)调用int usleep(useconds_t);函数去定时,这个时候它是微秒级的;头文件为<unistd.h>
3)调用高精度睡眠int nanosleep(const struct timespec * rep, struct timespec *rem);是一个相比标准UNIX sleep 调用具有更高高精度的版本。和普通的sleep 调用计算整秒数不同,nanosleep 接受一个指向一个struct timespec 对象的指针作为参数,它可以表示毫微秒(nanosecond,十亿分之一秒)的时间。然而,了解 Linux 内核的工作细节后可知,nanosleep 所提供的真正精确度是10毫秒—— sleep 提供的要精确。这个附加的精确度非常有用,比如说,可以根为反复进行的任务设置更短的间隔。
struct timespec 由两部分构成:tv_sec 表示整秒数部分;tv_nsec 则表示毫微秒。tv_nesc
的值必须小于109
nanosleep 相比sleep具有另一个优点。与sleep 相同,nanosleep 调用可以被信号中断,这是errno 将被设置为 EINTR 而调用将返回 -1。但是,nanosleep 的第二个参数,另一个指向struct timespec 对象的指针,如果不为 NULL 则在这种情况下它将被写入剩余的时间(这就是所请求的睡眠时间和实际睡眠时间的差)。这使重新开始睡眠变的很容易。 头文件 <time.h>
以下是内核中的:
1.udelay(); mdelay(); ndelay();实现的原理本质上都是忙等待,ndelaymdelay都是通过udelay衍生出来的,我们使用这些函数的实现往往会碰到编译器的 警告implicit declaration of function 'udelay',这往往是由于头文件的使用不当造成的。在include/asm-???/delay.h中定义了udelay(),而在 include/linux/delay.h中定义了mdelayndelay.udelay一般适用于一个比较小的delay,如果你填的数大于 2000,系统会认为你这个是一个错误的delay函数,因此如果需要2ms以上的delay需要使用mdelay函数。
2.由于这些delay函数本质上都是忙等待,对于长时间的忙等待意味这无谓的耗费着cpu的资源,因此对于毫秒级的延时,内核提供了msleepssleep等函数,这些函数将使得调用它的进程睡眠参数指定的时间。
那么,在Windows中呢
1)我们很快想到Sleep();头文件<windows.h>
然后再VC++中,到了一篇不错的文章,转自这里,内容如下:
方法一:VC中的WM_TIMER消息映射能进行简单的时间控制。首先调用函数SetTimer()设置定时 间隔,如SetTimer(0,200,NULL)即为设置200ms的时间间隔。然后在应用程序中增加定时响应函数 OnTimer(),并在该函数中添加响应的处理语句,用来完成到达定时时间的操作。这种定时方法非常 简单,可以实现一定的定时功能,但其定时功能如同Sleep()函数的延时功能一样,精度非常低,最小 计时精度仅为30msCPU占用低,且定时器消息在多任务操作系统中的优先级很低,不能得到及时响 应,往往不能满足实时控制环境下的应用。只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况。如示例工程中
Timer1
  方法 二:VC中使用sleep()函数实现延时,它的单位是ms,如延时2秒,用sleep(2000)。精度非常 低,最小计时精度仅为30ms,用sleep函数的不利处在于延时期间不能处理其他的消息,如果时间太 长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。如示例工程中的Timer2
  方法三:利用COleDateTime类和COleDateTimeSpan类结合WINDOWS的消息处理过程来实现秒级延时。如示例工程中的Timer3Timer3_1。以下是实现2秒的延时代码:
  COleDateTime  start_time = COleDateTime::GetCurrentTime();
  COleDateTimeSpan end_time= COleDateTime::GetCurrentTime()-start_time;
  while(end_time.GetTotalSeconds()< 2) //实现延时2
  {
      MSG msg;
      GetMessage(&msg,NULL,0,0);
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     
      //以上四行是实现在延时或定时期间能处理其他的消息,
      //虽然这样可以降低CPU的占有率,
      //但降低了延时或定时精度,实际应用中可以去掉。
      end_time = COleDateTime::GetCurrentTime()-start_time;
  }//这样在延时的时候我们也能够处理其他的消息。 
方法四:在精度要求较高的情况下,VC中可以利用GetTickCount()函数,该函数的返回值是 DWORD型,表示以ms为单位的计算机启动后 经历的时间间隔。精度比WM_TIMER消息映射高,在较 短的定时中其计时误差为15ms,在较长的定时中其计时误差较低,如果定时时间太长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序 中。如示例工程中的Timer4Timer4_1。下列代码可以实现50ms的精确定时:
  DWORD dwStart = GetTickCount();
  DWORD dwEnd = dwStart;
  do
  {
    dwEnd = GetTickCount()-dwStart;
  }while(dwEnd <50);
为使GetTickCount()函数在延时或定时期间能处理其他的消息,可以把代码改为:
  DWORD dwStart = GetTickCount();
  DWORD dwEnd = dwStart;
  do
  {
      MSG msg;
      GetMessage(&msg,NULL,0,0);
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      dwEnd = GetTickCount()-dwStart;
  }while(dwEnd <50);
虽然这样可以降低CPU的占有率,并在延时或定时期间也能处理其他的消息,但降低了延时或定时精度。
方法五:与GetTickCount()函数类似的多媒体定时器函数DWORD timeGetTime(void),该函数定时精 度为ms级,返回从Windows启动开始经过的毫秒数。微软公司在其多媒体Windows中提供了精确定时器的底 API持,利用多媒体定时器可以很精确地读出系统的当前时间,并且能在非常精确的时间间隔内完成一 个事件、函数或过程的调用。不同之处在于调用DWORD timeGetTime(void) 函数之前必须将 Winmm.lib Mmsystem.h 添加到工程中,否则在编译时提示DWORD timeGetTime(void)函数未定义。由于使用该 函数是通过查询的方式进行定时控制的,所以,应该建立定时循环来进行定时事件的控制。如示例工程中的Timer5Timer5_1
  方法六:使用多媒体定时器timeSetEvent()函数,该函数定时精度为ms级。利用该函数可以实现周期性的函数调用。如示例工程中的Timer6Timer6_1。函数的原型如下:
  MMRESULT timeSetEvent UINT uDelay,
              UINT uResolution,
              LPTIMECALLBACK lpTimeProc,
              WORD dwUser,
              UINT fuEvent
  该函数设置一个定时回调事件,此事件可以是一个一次性事件或周期性事件。事件一旦被激活,便调用指定的回调函数, 成功后返回事件的标识符代码,否则返回NULL。函数的参数说明如下:
  uDelay:以毫秒指定事件的周期。
  Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms
  LpTimeProc:指向一个回调函数。
  DwUser:存放用户提供的回调数据。
  FuEvent:指定定时器事件类型:
  TIME_ONESHOTuDelay毫秒后只产生一次事件
  TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。 
具体应用时,可以通过调用timeSetEvent()函数,将需要周期性执行的任务定义在LpTimeProc回调函数 (如:定时采样、控制等),从而完成所需处理的事件。需要注意的是,任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后, 应及时调用timeKillEvent()将之释放。
  方法七:对于精确度要求更高的定时操作,则应该使用 QueryPerformanceFrequency()
QueryPerformanceCounter()函数。这两个函数是VC提供的仅供Windows 95及其后续版本使用的精确时间函数,并要求计算机从硬件上支持精确定时器。如示例工程中的Timer7Timer7_1Timer7_2 Timer7_3
QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下:
  BOOL QueryPerformanceFrequency(LARGE_INTEGER lpFrequency);
  BOOL QueryPerformanceCounter(LARGE_INTEGER lpCount);
  数据类型ARGE_INTEGER既可以是一个8字节长的整型数,也可以是两个4字节长的整型数的联合结构, 其具体用法根据编译器是否支持64位而定。该类型的定义如下:
  typedef union _LARGE_INTEGER
  {
    struct
    {
      DWORD LowPart ;// 4字节整型数
      LONG HighPart;// 4字节整型数
    };
    LONGLONG QuadPart ;// 8字节整型数
   
    }LARGE_INTEGER ;
在进行定时之前,先调用QueryPerformanceFrequency()函数获得机器内部定时器的时钟频率, 然后在需要严格定时的事件发生之前和发生之后分别调用QueryPerformanceCounter()函数,利用两次获得的计数之差及时钟频率,计算 出事件经 历的精确时间。下列代码实现1ms的精确定时:
  LARGE_INTEGER litmp;
  LONGLONG QPart1,QPart2;
  double dfMinus, dfFreq, dfTim;
  QueryPerformanceFrequency(&litmp);
  dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
  QueryPerformanceCounter(&litmp);
  QPart1 = litmp.QuadPart;// 获得初始值
  do
  {
    QueryPerformanceCounter(&litmp);
    QPart2 = litmp.QuadPart;//获得中止值
    dfMinus = (double)(QPart2-QPart1);
    dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒
  }while(dfTim<0.001);
  其定时误差不超过1微秒,精度与CPU等机器配置有关。 下面的程序用来测试函数Sleep(100)的精确持续时间:
  LARGE_INTEGER litmp;
  LONGLONG QPart1,QPart2;
  double dfMinus, dfFreq, dfTim;
  QueryPerformanceFrequency(&litmp);
  dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
  QueryPerformanceCounter(&litmp);
  QPart1 = litmp.QuadPart;// 获得初始值
  Sleep(100);
  QueryPerformanceCounter(&litmp);
  QPart2 = litmp.QuadPart;//获得中止值
  dfMinus = (double)(QPart2-QPart1);
  dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒 
  由于Sleep()函数自身的误差,上述程序每次执行的结果都会有微小误差。下列代码实现1微秒的精确定时:
  LARGE_INTEGER litmp;
  LONGLONG QPart1,QPart2;
  double dfMinus, dfFreq, dfTim;
  QueryPerformanceFrequency(&litmp);
  dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
  QueryPerformanceCounter(&litmp);
  QPart1 = litmp.QuadPart;// 获得初始值
  do
  {
    QueryPerformanceCounter(&litmp);
    QPart2 = litmp.QuadPart;//获得中止值
    dfMinus = (double)(QPart2-QPart1);
    dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒
  }while(dfTim<0.000001);
其定时误差一般不超过0.5微秒,精度与CPU等机器配置有关。
关于短延迟 sleep usleep nanosleep select
Posted on 2008-08-21 19:19 Prayer 阅读(6904) 评论(0)  编辑 收藏 引用 所属分类: LINUX/UNIX/AIX
udelay(unsigned long usecs);
mdelay(unsigned long msecs);

前者用软件循环指定的微妙数,后者调用前者达到延迟毫秒级。udelay 函数只能用于获取
较短的时间延迟,因为loops_per_second值的精度只有8位,所以,当计算更长的延迟时会积累出相当大的误差。尽管最大能允 许的延迟将近1(因为更长的延迟就要溢出),推荐的 udelay 函数的参数的最大值是取1000微秒(1毫秒)。延迟大于 11 毫秒时可以使用函数 mdelay
要特别注意的是 udelay 是个忙等待函数(所以 mdelay 也是),在延迟的时间段内无法运行其他的任务,因此要十分小心,尤其是 mdelay,除非别无他法,要尽量避免使用。
mdelay Linux 2.0 中并不存在,头文件 sysdep.h 弥补了这一缺陷。

关于 usleep sleep 主要的差距在精确程度上,不过网友有关于这个方面的精辟论断:
同样我觉得select也是比较好的定时机制,不过大家可以看igmp-proxy的源代码。主函数里面用setitimerselect同时定时是一个相当好的想法。

>>>>>>>>>>>>>再论精确延时(usleep,nanosleep,select)
/*
        make: gcc -o test_sleep test_sleep.c
*/
/*        #include "comm_main.h" */
#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;linux下的sleep函数
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 < 20; 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);
        }

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。