机器时钟实现高精度定时

本文深入探讨了Linux系统中usleep()及nanosleep()函数的精度限制,特别是在x86架构下,受制于内核时钟频率,精度最高仅能达到10ms。通过示例代码展示了如何利用RDTSC指令进行更精确的时间测量。

Linux系统使用usleep()以及nanosleep(),受内核时钟频率的影响,如x86默认是100Hz,这样精度只能到0.01s,也就是10ms。

timer_use.c

/* According to POSIX.1-2001 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <stdio.h>


#define RTE_STD_C11
typedef unsigned long int uint64_t;
typedef unsigned int uint32_t;

static inline uint64_t
ipv4_rte_rdtsc(void)
{
    union {
        uint64_t tsc_64;
        RTE_STD_C11
        struct {
            uint32_t lo_32;
            uint32_t hi_32;
        };
    } tsc;

    asm volatile("rdtsc" :
             "=a" (tsc.lo_32),
             "=d" (tsc.hi_32));
    return tsc.tsc_64;
}


int main() {

        uint64_t ret, tsc_start, tsc_end, tsc_second, tsc_usecond, tsc_old;
        ret = ipv4_rte_rdtsc();
        printf("ret:%lu\n", ret);


        /* get tsc for second */
        tsc_start = ipv4_rte_rdtsc();
        sleep(1);
        tsc_end = ipv4_rte_rdtsc();

        /* get tsc for microsecond */
        tsc_second = tsc_end - tsc_start;
        tsc_usecond = tsc_second / 1000000; 

        printf("tsc_start:%lu, tsc_end:%lu, tsc_second:%lu, tsc_usecond:%lu\n", ret, tsc_start, tsc_end, tsc_second, tsc_usecond);

        uint64_t tsc_current;
        tsc_old = ipv4_rte_rdtsc();
        int i;
        for (i = 0; i < 5; ) {
                tsc_current = ipv4_rte_rdtsc();
                if (tsc_current - tsc_old > tsc_usecond) {
                        i++;
                        /* usecond timer */
                        tsc_old = tsc_current;
                        printf("handle something\n");
                } 
        }




}

测试

gcc timer_use.c -o timer_use
strace -tt ./timer_use 
03:53:46.526131 execve("./timer_use", ["./timer_use"], [/* 14 vars */]) = 0
03:53:46.526272 brk(NULL)               = 0x602000
03:53:46.526315 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff6000
03:53:46.526360 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
03:53:46.526404 open("tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526444 open("tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526479 open("x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526514 open("libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526549 open("/usr/local/postgres/pglib/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526588 stat("/usr/local/postgres/pglib/tls/x86_64", 0x7fffffffd220) = -1 ENOENT (No such file or directory)
03:53:46.526624 open("/usr/local/postgres/pglib/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526662 stat("/usr/local/postgres/pglib/tls", 0x7fffffffd220) = -1 ENOENT (No such file or directory)
03:53:46.526698 open("/usr/local/postgres/pglib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526736 stat("/usr/local/postgres/pglib/x86_64", 0x7fffffffd220) = -1 ENOENT (No such file or directory)
03:53:46.526772 open("/usr/local/postgres/pglib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526808 stat("/usr/local/postgres/pglib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
03:53:46.526849 open("/sdd/tmp/extlib/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526886 stat("/sdd/tmp/extlib/tls/x86_64", 0x7fffffffd220) = -1 ENOENT (No such file or directory)
03:53:46.526922 open("/sdd/tmp/extlib/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.526957 stat("/sdd/tmp/extlib/tls", 0x7fffffffd220) = -1 ENOENT (No such file or directory)
03:53:46.526992 open("/sdd/tmp/extlib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.527028 stat("/sdd/tmp/extlib/x86_64", 0x7fffffffd220) = -1 ENOENT (No such file or directory)
03:53:46.527063 open("/sdd/tmp/extlib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
03:53:46.527100 stat("/sdd/tmp/extlib", {st_mode=S_IFDIR|0755, st_size=8192, ...}) = 0
03:53:46.527136 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
03:53:46.527172 fstat(3, {st_mode=S_IFREG|0644, st_size=9993, ...}) = 0
03:53:46.527206 mmap(NULL, 9993, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7ff3000
03:53:46.527240 close(3)                = 0
03:53:46.527274 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
03:53:46.527310 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\35\2\0\0\0\0\0"..., 832) = 832
03:53:46.527347 fstat(3, {st_mode=S_IFREG|0755, st_size=2127336, ...}) = 0
03:53:46.527383 mmap(NULL, 3940800, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7a18000
03:53:46.527420 mprotect(0x7ffff7bd0000, 2097152, PROT_NONE) = 0
03:53:46.527455 mmap(0x7ffff7dd0000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b8000) = 0x7ffff7dd0000
03:53:46.527497 mmap(0x7ffff7dd6000, 16832, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7dd6000
03:53:46.527539 close(3)                = 0
03:53:46.527577 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff2000
03:53:46.527615 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff0000
03:53:46.527653 arch_prctl(ARCH_SET_FS, 0x7ffff7ff0740) = 0
03:53:46.527710 mprotect(0x7ffff7dd0000, 16384, PROT_READ) = 0
03:53:46.527747 mprotect(0x600000, 4096, PROT_READ) = 0
03:53:46.527782 mprotect(0x7ffff7ffc000, 4096, PROT_READ) = 0
03:53:46.527816 munmap(0x7ffff7ff3000, 9993) = 0
03:53:46.527868 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
03:53:46.527905 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff5000
03:53:46.527943 write(1, "ret:14282334634150\n", 19ret:14282334634150
) = 19
03:53:46.527981 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
03:53:46.528016 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
03:53:46.528050 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
03:53:46.528096 nanosleep({1, 0}, 0x7fffffffe870) = 0
03:53:47.528230 write(1, "tsc_start:14282334634150, tsc_en"..., 100tsc_start:14282334634150, tsc_end:14282335028144, tsc_second:14285743869122, tsc_usecond:3408840978
) = 100
03:53:47.528279 write(1, "handle something\n", 17handle something
) = 17
03:53:47.528307 write(1, "handle something\n", 17handle something
) = 17
03:53:47.528333 write(1, "handle something\n", 17handle something
) = 17
03:53:47.528367 write(1, "handle something\n", 17handle something
) = 17
03:53:47.528393 write(1, "handle something\n", 17handle something
) = 17
03:53:47.528419 exit_group(17)          = ?
03:53:47.528468 +++ exited with 17 +++

JAVA精确定时器,利用系统时间,使长期工作的误差稳定。 功能: ·可定时启动任务或直接启动任务 ·重复启动任务(时间间隔可在任务线程中改变,范围大于100ms,否则精度降低) 引用列表: ·import psn.razerpen.thread.AccuracyTimer; ·import psn.razerpen.thread.AccuracyTimerMission; ·import psn.razerpen.time.TimeStruct; 使用方法: //1·继承AccuracyTimerMission接口,创建一个类。 class MyTimer implements AccuracyTimerMission { //2·指定一个周期 int nDelay=1000; //3·重写run方法(如不需要使用新线程执行任务,也可留空) /** * 任务线程,本函数继承自Runnable */ @Override public void run() { System.out.println(new TimeStruct()); } //4·重写RunInCurrentThread(long nCurrentMilliSecond)方法。该方法接收当前时间,并返回下一次执行的时间。如果返回值不大于nCurrentMilliSecond则中止计时器。该方法必须重写。 /** * 接收当前时间的毫秒值,并返回下一次执行的毫秒值。如果返回的下一个时间早于当前时间,则退出 */ @Override public long RunInCurrentThread(long nCurrentMilliSecond) { return nCurrentMilliSecond+=nDelay; } } //5·创建主线程代码 public class TestTimer { public static void main(String[] args) throws InterruptedException { //6·创建一个AccuracyTimer对象,并指定一个任务。 AccuracyTimer at=new AccuracyTimer(new MyTimer()); //7·(可选)如果不需要在新线程中启动任务,则写 // at.SetNewThreadEnabled(false); //否则不写或者写 // at.SetNewThreadEnabled(true); //8·(可选)设定第一次启动的时间点SetNextMissionTime/SetNextMissionMilliSecond或延迟时间SetNextMissionMilliSecondFromNow //设置为当前这一分钟的第59秒后启动(不写此行则表示直接启动) at.SetNextMissionTime(Integer.MIN_VALUE, -1, -1, -1, -1, 59, 0); //9·启动定时器 at.Start(); //10·主线程继续 for(int i=0;i<60;++i){ Thread.sleep(1000); } //11·结束定时器 at.End(); } } 详见sample.razerpen.thread包中TestTimer及各代码文件中注释
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值