加载中…
个人资料
tribanana
tribanana
  • 博客等级:
  • 博客积分:0
  • 博客访问:4,211
  • 关注人气:0
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

linux环境下的精确时延的获取

(2017-10-29 15:51:23)
标签:

linux

精确延时

分类: programming
项目中有人要用精确到2us的时延,我觉得很奇怪,觉得是设计中有问题才会有这种要求,不过不妨碍探讨一番,网上各种翻阅,做个小实验,大概总结如下。

用户空间Linux下延时的方式不外乎调用各种延时函数,或者使用定时器,或者使用select,网络摘抄如下

1. unsigned int sleep(unsigned int seconds);
2. int usleep(useconds_t usec);
3. int nanosleep(const struct timespec *req, struct timespec *rem);
4. select()
 
useconds_t在linux中的定义就是unsigned int
struct timespec {
        time_t  tv_sec;        
        long    tv_nsec;        
};
 
1、 sleep函数可能提前返回,即延时时间不足seconds秒
原因分析:sleep函数可能被信号中断而提前返回
规避措施:判断sleep的返回值,只有返回0时才表示完成预期的延时,否则返回剩余的秒数。所以请检查一下您的代码中是否判断了sleep的返回值?
 
2、 usleep/nansleep函数成功返回时其延时时间可能大于入参指定的时间
原因分析:usleep函数手册中明确标示usleep调用至少挂起进程usec微秒,导致进程挂起时间变长的原因是:
1、  函数调用的开销
2、  系统调度开销
3、  系统定时器的精度
规避措施:
1、  一定要判定usleep的返回值,仅当返回0表示成功
2、  在时间精度要求比较高的场合不建议使用usleep函数
 
3、 usleep函数不能用于秒级以上的延时
原因分析: 在某些系统中usleep接受的入参最大值必须小于1000000(即1秒)
规避措施:
1、  用sleep函数替代
2、  通过多次usleep完成
3、  其他
说明:在Suse9/10上测试是没有问题的,但出于可移植性考虑,不要使用usleep作秒级以上的延时
 
4、nanosleep函数延时存在多种情况,需要根据返回值/errno/输出变量综合判断
情况1延时时间大于或是等于预期时间(绝大部分情况下),返回0
情况2延时时间小于预期时间,返回-1
情况2.1 errno为EINTR表示函数提前返回,此时可读取第2个参数的获取剩余时间
情况2.2 errno为EINVAL表示第一个入参req.tv_nsec取值超出范围[0, 999,999,999]

5. 、select的精度是微妙,精确

struct timevaldelay;

delay.tv_sec =0;

delay.tv_usec =20 * 1000; // 20 ms

select(0, NULL,NULL, NULL, &delay);#include

6. 直接 循环 延时, for (i =0; i < 1000; i++){}


=========================实验============

#include
#include
#include
#include
#include

void seconds_sleep(unsigned seconds){
    struct timeval tv;
    tv.tv_sec=seconds;
    tv.tv_usec=0;
    int err;
    do{
       err=select(0,NULL,NULL,NULL,&tv);
    }while(err<0 && errno==EINTR);
}

void milliseconds_sleep(unsigned long mSec){
    struct timeval tv;
    tv.tv_sec=mSec/1000;
    tv.tv_usec=(mSec00)*1000;
    int err;
    do{
       err=select(0,NULL,NULL,NULL,&tv);
    }while(err<0 && errno==EINTR);
}


void microseconds_sleep(unsigned long uSec){
    struct timeval tv;
    tv.tv_sec=uSec/1000000;
    tv.tv_usec=uSec00000;
    int err;
    do{
        err=select(0,NULL,NULL,NULL,&tv);
    }while(err<0 && errno==EINTR);
}

void nanodelay_sleep(unsigned long nSec){
    struct timespec ts;

    ts.tv_sec=nSec/1000000000;
    ts.tv_nsec=nSec00000000;
    struct timeval tv;
    if ( nanosleep(&ts, NULL) == -1 )
    {
        printf("error!\n");
        return;
    }
}

void countdelay(unsigned long cnt)
{
    int j;

    for (j = 0; j < cnt*100; j++)
    {
    }
}


int main()
{
    int i,j;
    struct  timeval start;
    struct  timeval end;
    unsigned  long diff;
    struct sched_param param;
    int maxpri;
    int pri;

    maxpri = sched_get_priority_max(SCHED_FIFO);
    if(maxpri == -1)
    {
        perror("sched_get_priority_max() failed");
 //       exit(1);
    }
    
    pri = sched_getscheduler(getpid());
    param.sched_priority = 10; //maxpri;  
    if (sched_setscheduler(getpid(), SCHED_FIFO, &param) == -1) //设置优先级
    {
        perror("sched_setscheduler() failed");
    }

    for(i=0;i<50;++i){
#define MAXLP 1000
        printf("%d  ",i);
        gettimeofday(&start,NULL);
        //seconds_sleep(1);
        //milliseconds_sleep(1);
        //microseconds_sleep(1);
        //sleep(1);
        //usleep(1);
        //nanodelay_sleep(1000*1);
        //for (j = 0; j < MAXLP; j++) {}
        countdelay(8);
        gettimeofday(&end,NULL);
        diff = 1000000 * (end.tv_sec-start.tv_sec)+ end.tv_usec-start.tv_usec;
        printf("thedifference is %ld\n",diff);
    }
    printf("%s %d, old-sched %d, new sched %d, pri %d\n", __FUNCTION__,__LINE__,pri, sched_getscheduler(getpid()),maxpri);
    param.sched_priority = maxpri;
    sched_setscheduler(getpid(), pri, &param);
}

===================================

1. sleep只能延时秒级, 在秒级的水平是没有问题的

thedifference is 1000517 us
thedifference is 1000024 us
thedifference is 1000141 us
thedifference is 1000034 us
thedifference is 1000028 us

2. usleep 微妙级时延,效果不佳,最短时延也要几十微妙

thedifference is 81 us
thedifference is 90 us
thedifference is 59 us
thedifference is 56 us
thedifference is 56 us
main 102, old-sched 0, new sched 1, pri 99

3. nanosleep 纳秒时延,结果也不好

thedifference is 82 us
thedifference is 101 us
thedifference is 125 us
thedifference is 78 us
thedifference is 69 us
main 102, old-sched 0, new sched 1, pri 99

4. select的精度也不好,

thedifference is 88 us
thedifference is 79 us
thedifference is 69 us
thedifference is 63 us
thedifference is 61 us
main 102, old-sched 0, new sched 1, pri 99

5. for loop, 精度最好

thedifference is 3 us
thedifference is 3 us
thedifference is 2 us
thedifference is 3 us
thedifference is 2 us
main 102, old-sched 0, new sched 1, pri 99

thedifference is 3 us
thedifference is 3 us
thedifference is 11 us
thedifference is 2 us
thedifference is 10 us
main 102, old-sched 0, new sched 1, pri 99

***********************结论******************

微秒级时延最好的方式是:在使用sched_setscheduler改变进程调度方式为FIFO,并提升进程优先后,直接使用for循环来达到目的



0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有