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

在内核代码中,我们经常会看到这样的头文件使用#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 的调用使进程进入一个不可中断的睡眠给定的秒数.

(笔记)Linux下的准确延时,#include <linux/delay.h>调用出错的更多相关文章

  1. Linux下ps命令详解 Linux下ps命令的详细使用方法

    http://www.jb51.net/LINUXjishu/56578.html Linux下的ps命令比较常用 Linux下ps命令详解Linux上进程有5种状态:1. 运行(正在运行或在运行队列 ...

  2. linux下错误的捕获:errno(errno.h)和strerror(string.h)的使用

    参考:http://blog.csdn.net/starstar1992/article/details/52756387 linux下错误的捕获:errno和strerror的使用 经常在调用lin ...

  3. Linux下修改默认字符集--->解决Linux下Java程序种中文文件夹file.isDirectory()判断失败的问题

    一.问题描述: 一个项目中为了生成树状目录,调用了file.listFiles()方法,然后利用file.isDirectory()方法判断是否为目录,该程序在windows下运行无问题,在Linux ...

  4. 《linux 内核全然剖析》 include/asm/io.h

    include/asm/io.h #define outb(value,port) \ __asm__ ("outb %%al,%%dx"::"a" (valu ...

  5. LINUX下SYN FLOOD攻击及LINUX下SYN攻防简述

    LINUX下SYN攻防战如下 (一)SYN攻击原理 SYN攻击属于DOS攻击的一种,它利用TCP协议缺陷,通过发送大量的半连接请求,耗费服务器CPU和内存资源.SYN攻击聊了能影响主机外,还可以危害路 ...

  6. linux下添加自动启动项,linux 开机自动启动脚本方法

    #service servicename status是当前状态#chkconfig --list servicename是查看启动状态,也就是是否开机自动启动 首先写好脚本,如 mysql,把它放到 ...

  7. linux下显示完整路径,linux下显示绝对路径

    linux下,命令行显示路径仅最后一个文件名,非常不方便,想显示完整路径.环境背景:linux,无root权限,可sudo(为了服务器安全,一般只给管理员root账号和密码,普通账号仅sudo权限)方 ...

  8. Linux下使用select延时

    在LINUX用户态的情况下,如果想要延时的话,可以使用用sleep函数,但是在一些情况下,需要更小单位的延时,ms/us 也是要的.用循环获取到的延时是不精确的. sleep是不准确,这个函数是可以中 ...

  9. Linux下同步模式、异步模式、阻塞调用、非阻塞调用总结

    转自:http://www.360doc.com/content/13/0117/12/5073814_260691714.shtml 同步和异步:与消息的通知机制有关. 本质区别 现实例子 同步模式 ...

随机推荐

  1. android - 调用系统分享功能分享图片

    step1: 编写分享代码, 将Uri的生成方式改为由FileProvider提供的临时授权路径,并且在intent中添加flag 注意:在Android7.0之后,调用系统分享,传入URI的时候可能 ...

  2. [AWS vs Azure] 云计算里AWS和Azure的探究(1)

    转自:http://www.cnblogs.com/hotcan/archive/2013/01/31/2886794.html 云计算里AWS和Azure的探究(1) 全球领先的云的计算平台主要有两 ...

  3. Vue.js使用-组件(下篇)

    上一节,我们定义了组件的基本使用,下面我们看看组件其他的一些特性. 1.组件作用域 同时在Vue对象和组件中定义一个属性,显示结果是怎样的呢? <!DOCTYPE html> <ht ...

  4. TensorFlow学习路径【转】

    作者:黄璞链接:https://www.zhihu.com/question/41667903/answer/109611087来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

  5. js弹出层的插件

    1.jquery.fancybox.pack.js 2.artdialog 3.

  6. django后台显示图片 而不是图片地址

    修改admin代码 class Ad_CampaingAdmin(admin.ModelAdmin): list_display = ("content","previe ...

  7. DIV+CSS兼容解决DIV最大宽度和最小宽度问题

    在制作网页中,我们经常会碰到min/max-width,min/max-height在IE6底下是无效的,这也是web设计师最头疼的问题之一,以下的方法可以解决这些难题,并且比较简约.当然,如果你还有 ...

  8. java 获取 path

    (1).request.getRealPath("/");//不推荐使用获取工程的根路径 (2).request.getRealPath(request.getRequestURI ...

  9. spring mvc之启动过程源码分析

    简介 这两个星期都在看spring mvc源码,看来看去还是还是很多细节没了解清楚,在这里把看明白的记录下,欢迎在评论中一起讨论. 一.铺垫 spring mvc是基于servlet的,在正式分析之前 ...

  10. git pull出现There is no tracking information for the current branch

    使用git pull 或者 git push 的时候报错 gitThere is no tracking information for the current branch. Please spec ...