2007-09-27

nanosleep精度问题分析

最近有个同学在做流量模拟,发包时需要精确控制发包间隔,即要用到高精度的延时。

首先想到的便是nanosleep系统调用,毕竟从接口上看,它可以达到纳秒级的精度(1*10^-9)。

然而实际上nanosleep的解析度远没有达到预期,把时间间隔设地再小,精度也始终只有一微秒左右。

WHY?

READ THE FUCKING MANUAM

原来,2.6内核中nanosleep实现基于内核中的普通定时器机制,最大解析度也只有1/HZs,所以nanpsleep暂停的最小单位即是1ms,考虑到进程的切换与函数调用消耗的时间,实际的最小单位可能会在10ms左右甚至更长。

如果是这样,那为何内核还要提供这样一个名不副实的函数呢?
原来在2.4内核中,当把进程调度策略设置成实时级别的,诸如SCHED_FIFO或SCHED_RR,则对于较小的延迟时间,内核将采用忙等的方式进行计时处理。由此提供的解析精度可以达到2ms以下。

尝试了一下,实际解析度的确升高了,效果不错。

#include   <stdio.h>
#include   <time.h>
#include   <sys/time.h>
#include   <sys/types.h>
#include   <unistd.h>
#include   <sched.h>

#define BILLION  1000000L

void do_sleep(int looptimes,int sleeptimes){
    
    int i,j;
    struct timespec tv={0,1};
    struct timeval start, stop;
    long accum;

    for(i=0;i<looptimes;i++){
        gettimeofday(&start,NULL);

        for(j=0; j<sleeptimes; j++){
            nanosleep(&tv,NULL);
        }

        gettimeofday(&stop,NULL);
        accum = ( stop.tv_sec - start.tv_sec )*BILLION + ( stop.tv_usec - start.tv_usec );

        fprintf(stderr,"%ld\t", accum/sleeptimes );
    }
    
    fprintf(stderr,"\n");
}

int main( int argc, char** argv )
{
    struct sched_param sp;
        
    sp.sched_priority=0;
    sched_setscheduler(0,SCHED_FIFO,&sp);

    fprintf(stderr,"Schedule priority: %d\n",sp.sched_priority);
        
    do_sleep(10,100);

    sp.sched_priority=99;
    sched_setscheduler(0,SCHED_FIFO,&sp);
    
    fprintf(stderr,"Schedule priority: %d\n",sp.sched_priority);
    
    do_sleep(10,100);
    
    return 0;
}

实验环境:
kernel version: Linux version 2.4.20-8smp (bhcompile@stripples.devel.redhat.com) (gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)) #1 SMP Thu Mar 13 16:43:01 EST 2003

–EOF–