zoukankan      html  css  js  c++  java
  • (笔记)Linux下的准确延时,#include <linux/delay.h>调用出错

    在编写应用层程序时,有时需要延时一下,这个时候该怎么办呢?

    在内核代码中,我们经常会看到这样的头文件使用#include <linux/delay.h>,心想着直接调用这个就可以了吧!可是在编译时发现,压根通不过,

    提示错误如下:error: No such file or directory.

    是不是觉得很奇怪,明明文件是存在的,怎么就不能调用了,而且内核很多文件调用得很欢。这是为什么呢?

    因为内核程序跟应用程序是有区别的,有些特殊的内核头文件编译器不允许被应用程序调用。故编译应用程序使用内核的头文件,报错是难免的

    但是,这个时候该怎么呢?

    哈哈!#include <unistd.h>头文件出现了!功能与#include <linux/delay.h>一致,但是可以在应用层随便调用。不错的东西吧!以下是其详细介绍:

    应用层:
       #include <unistd.h>
     
       1、unsigned int sleep(unsigned int seconds); 秒级
       2、int usleep(useconds_t usec);              微秒级:1/10^-6

     
       #define _POSIX_C_SOURCE 199309
       #include <time.h>
       3、int nanosleep(const struct timespec *req, struct timespec *rem);
           struct timespec {
                      time_t tv_sec;        /* seconds */
                      long   tv_nsec;       /* nanoseconds */
                  };
           // The value of the nanoseconds field must be in the range 0 to 999999999.
     
     内核层:
       include <linux/delay.h>
       1、void ndelay(unsigned long nsecs);         纳秒级:1/10^-10
       2、void udelay(unsigned long usecs);         微秒级: 1/10^-6
       3、void mdelay(unsigned long msecs);         毫秒级:1/10^-3 

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

       首先, 我会说不保证你在使用者模式 (user-mode) 中执行的行程 (process) 能够精确地控制时序因为 Linux 是个多工的作业环境. 你在执行中的行程 (process) 随时会因为各种原因被暂停大约 10 毫秒到数秒 (在系统负荷非常高的时候). 然而, 对於大多数使用 I/O 埠的应用而言, 这个延迟时间实际上算不了什麽. 要缩短延迟时间, 你得使用函式 nice 将你在执行中的行程 (process ) 设定成高优先权(请参考nice(2)使用说明文件) 或使用即时排程法 (real-time scheduling) (请看下面).

      如果你想获得比在一般使用者模式 (user-mode) 中执行的行程 (process) 还要精确的时序, 有一些方法可以让你在使用者模式 (user-mode) 中做到 `即时' 排程的支援. Linux 2.x 版本的核心中有软体方式的即时排程支援; 详细的说明请参考 sched_setscheduler(2) 
    使用说明文件. 有一个特殊的核心支援硬体的即时排程; 详细的资讯请参考网页 luz.cs.nmt.edu/~rtlinux/

    休息中 (Sleeping) :

    sleep() 与 usleep()

      现在, 让我们开始较简单的时序函式呼叫. 想要延迟数秒的时间, 最佳的方法大概 是使用函式 sleep(). 想要延迟至少数十毫秒的时间 (10 ms 似乎已是最短的 延迟时间了), 函式 usleep()应该可以使用. 
      这些函式是让出 CPU 的使用权 给其他想要执行的行程 (processes) (“自己休息去了''), 所以没有浪费掉 CPU 的时间. 细节请参考: sleep(3) 与 usleep(3) 的说明文件.

      如果让出 CPU 的使用权因而使得时间延迟了大约 50 毫秒 (这取决於处理器与机器的速度, 以及系统的负荷), 就浪费掉 CPU 太多的时间, 因为 Linux 的排程器 (scheduler) (单就 x86 架构而言) 在将控制权发还给你的行程 (process) 之前通常至少要花费 10-30 毫秒的时间. 因此, 短时间的延迟, 使用函式 usleep(3) 所得到的延迟结果通常会大於你在参数所指定的值, 大约至少有 10 ms.

    nanosleep()

      在 Linux 2.0.x 一系列的核心发行版本中, 有一个新的系统呼叫 (system call),nanosleep() (请参考 nanosleep(2)的说明文件), 他让你能够休息或延迟一个短的时间 (数微秒或更多).

      如果延迟的时间 <= 2 ms, 若(且唯若)你执行中的行程 (process) 设定了软体的即时 排程 (就是使用函式 tt/sched_setscheduler()/), 呼叫函式 nanosleep()  时不是使用一个忙碌回圈来延迟时间; 就是会像函式 usleep() 一样让出 CPU 的使用权休息去了.

      这个忙碌回圈使用函式 udelay() (一个驱动程式常会用到的核心内部的函式) 来达成, 并且使用 BogoMips 值 (BogoMips 可以准确量测这类忙碌回圈的速度) 来计算回圈延迟的时间长度. 其如何动作的细节(请参考/usr/include/asm/delay.h).

    使用 I/O 埠来延迟时间

      另一个延迟数微秒的方法是使用 I/O 埠. 就是从埠位址 0x80 输入或输出任何 byte 的资料 (请参考前面) 等待的时间应该几乎只要 1 微秒这要看你的处理器的型别与速度. 如果要延迟数微秒的时间你可以将这个动作多做几次. 在任何标准的机器上输出资料到该 埠位址应该不会有不良的後果□对 (而且有些核心的设备驱动程式也在使用他).

    {in|out}[bw]_p()等函式就是使用这个方法来产生时间延迟的 (请参考档案asm/io.h).

      实际上, 一个使用到埠位址范围为 0-0x3ff 的 I/O 埠指令几乎只要 1 微秒的时间, 所以如果你要如此做, 例如, 直接使用并列埠, 只要加上几个inb()函式从该埠位址□围读入 byte 的资料即可.

    使用组合语言来延迟时间

      如果你知道执行程式所在机器的处理器型别与时钟速度, 你可以执行某些组合语言指令以便获得较短的延迟时间 (但是记住, 你在执行中的行程 (process) 随时会被暂停, 所以有时延迟的时间会比实际长). 如下面的表格所示, 内部处理器的速度决定了所要使用的时钟周期数; 如, 一个 50 MHz 的处理器 (486DX-50 或 486DX2-50), 一个时钟周期要花费 1/50000000 秒 (=200 奈秒).

        指令 i386 时钟周期数 i486 时钟周期数

        nop 3 1

        xchg %ax,%ax 3 3

        or %ax,%ax 2 1

        mov %ax,%ax 2 1

        add %ax,0 2 1

    (对不起, 我不知道 Pentiums 的资料, 或许与 i486 接近吧. 我无法在 i386 的资料上找到只花费一个时钟周期的指令. 如果能够就请使用花费一个时钟周期的指令, 要不然就使用管线技术的新式处理器也是可以缩短时间的.)

      上面的表格中指令 nop 与 xchg 应该不会有不良的後果. 指令最後可能会改变旗号暂存器的内容, 但是这没关系因为 gcc 会处理. 指令 nop 是个好的选择.

      想要在你的程式中使用到这些指令, 你得使用 asm("instruction"). 指令的语法就如同上面表格的用法; 如果你想要在单一的asm()叙述中使用多个指令, 可以使用分号将他们隔开. 
    例如,
      asm("nop ; nop ; nop ; nop")

    会执行四个 nop 指令, 在 i486 或 Pentium 处理器中会延迟四个时钟周期 (或是 i386 会延迟 12 个时钟周期).

    gcc 会将 asm() 翻译成单行组合语言程式码, 所以不会有呼叫函式的负荷.

    在 Intel x86 架构中不可能有比一个时钟周期还短的时间延迟.

    在 Pentiums 处理器上使用函式 rdtsc

    对於 Pentiums 处理器而言, 你可以使用下面的 C 语言程式码来取得自从上次重新开机 到现在经过了多少个时钟周期:

    extern __inline__ unsigned long long int rdtsc()

    {

    unsigned long long int x;

    __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));

    return x;

    }


    你可以询问参考此值以便延迟你想要的时钟周期数.

    想要时间精确到一秒钟, 使用函式 time() 或许是最简单的方法. 想要时间更精确, 函式 gettimeofday() 大约可以精确到微秒 (但是如前所述会受到 CPU 排程的影响). 至於 Pentiums 处理器, 使用上面的程式码片断就可以精确到一个时钟周期.

    如果你要你执行中的行程 (process) 在一段时间到了之後能够被通知 (get a signal), 你得使用函式 setitimer() 或 alarm(). 细节请参考函式的使用说明文件.

    应用程序:

    #include <syswait.h>

    usleep(n) //n微秒

    Sleep(n)//n毫秒

    sleep(n)//n秒

    驱动程序:

    #include <linux/delay.h>

    mdelay(n) //milliseconds 其实现

    #ifdef notdef

    #define mdelay(n) (

    {unsigned long msec=(n); while (msec--) udelay(1000);})

    #else

    #define mdelay(n) (

    (__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) :

    ({unsigned long msec=(n); while (msec--) udelay(1000);}))

    #endif

    调用 asm/delay.h的udelay,udelay应该是纳秒级的延时

     

    Dos:

    sleep(1); //停留1秒

    delay(100); //停留100毫秒   

    Windows:

    Sleep(100); //停留100毫秒

    Linux:

    sleep(1); //停留1秒

    usleep(1000); //停留1毫秒

    每一个平台不太一样, 最好自己定义一套跨平台的宏进行控制秒还是微秒?

    关于延时函数sleep()

        因为要写一段代码,需要用到sleep()函数,在我印象中,sleep(10)好像是休眠10微秒,结果却是休眠了10秒(在Linux下)。觉得很奇怪,因为头儿也记得好像是微秒为单位的。所以就查了一下。

    原来linux下的sleep函数原型为:

            unsigned int sleep(unsigned int seconds);

    而MFC中的 Sleep函数原型为:

            void Sleep(DWORD dwMilliseconds);

      也就是说,Linux下(使用的gcc的库),sleep()函数是以秒为单位的,sleep(1);就是休眠1秒。而MFC下的sleep()函数是以微秒为单位的,sleep(1000); 才是休眠1秒。原来如此啊。而如果在Linux下也用微妙为单位休眠,可以使用线程休眠函数:void usleep(unsigned long usec);当然,使用的时候别忘记#include <system.h>哦。

        另外值得一提的是,linux下还有个delay()函数,原型为extern void delay(unsigned int msec);它可以延时msec*4毫秒,也就是如果想延时一秒钟的话,可以这么用 delay(250);


    当一个设备驱动需要处理它的硬件的反应时间, 涉及到的延时常常是最多几个毫秒. 

    内核函数 ndelay, udelay, 以及 mdelay 对于短延时好用, 分别延后执行指定的纳秒数, 微秒数或者毫秒数. 它们的原型是:

    #include <linux/delay.h>
    void ndelay(unsigned long nsecs);
    void udelay(unsigned long usecs);
    void mdelay(unsigned long msecs); 
    有另一个方法获得毫秒(和更长)延时而不用涉及到忙等待. 文件 <linux/delay.h> 声明这些函数:

    void msleep(unsigned int millisecs);
    unsigned long msleep_interruptible(unsigned int millisecs);
    void ssleep(unsigned int seconds) 
    前 2 个函数使调用进程进入睡眠给定的毫秒数. 一个对 msleep 的调用是不可中断的; 你能确保进程睡眠至少给定的毫秒数. 如果你的驱动位于一个等待队列并且你想唤醒来打断睡眠, 使用 msleep_interruptible. 从 msleep_interruptible 的返回值正常地是 0; 如果, 但是, 这个进程被提早唤醒, 返回值是在初始请求睡眠周期中剩余的毫秒数. 对 ssleep 的调用使进程进入一个不可中断的睡眠给定的秒数.

  • 相关阅读:
    debian安装jdk,batik
    Debian 安装 VMware
    [转贴]UNIX\LINUX系统开发-gcc参数详解
    Free mail server
    mysql 权限控制笔记
    Debian 4.0 编译内核 2.6.27.6
    backbone 学习之sync
    backbone 学习之extend以及总结
    backbone 学习之全部源码
    backbone 学习之Collection
  • 原文地址:https://www.cnblogs.com/tdyizhen1314/p/4140643.html
Copyright © 2011-2022 走看看