jiffies回绕问题的分析&linux下的时间表示

本文深入探讨了Linux系统中jiffies变量的回绕问题,由于jiffies通常定义为无符号32位,当HZ为1000时,回绕周期约为50天。文章详细分析了time_after等比较时间先后的宏的工作原理,并指出在进行比较时,两个值的逻辑差值必须小于有符号整型的最大值。此外,还介绍了Linux常用的时间结构、获取系统时间的方法以及时间转换和延迟休眠的相关知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原理:为什么负数的表示方法是补码

正数 :原码
负数 :补码(反码+1)
因为需要 相同的正数和负数和需要为0 。

20 + -20 = 0
0001 0100
+1110 1011    
1111 1111  + 1 = 0
定义 jiffies /Tick为 无符号32bits , 如何判断回绕

如果HZ为1000,那么回绕时间将只有50天左 右。
使用这几个宏进行判断

  #define time_after(unknown,known) ((long)(known) - (long)(unknown)<0)
  #define time_before(unkonwn,known) ((long)(unknown) - (long)(known)<0)
  #define time_after_eq(unknown,known) ((long)(unknown) - (long)(known)>=0)
  #define time_before_eq(unknown,known) ((long)(known) -(long)(unknown)>=0)
//当前时间 jiffies , 记录timeout的时间
unsigned long timeout = jiffies + HZ/2; /* timeout in 0.5s */  
//一段时间以后
if (time_before(jiffies, timeout)) {  
} else{
}
time_after等比较时间先后的宏背后的原理
#define uc_after(a, b) ((char)(b) - (char)(a) < 0)

假定当接近回绕的时候,b为临近8位无符号整型最大值附近的一个固定值254

ab(char)(b) - (char)(a)
254 (1111 1110)254(1111 1110)0
255 (1111 1111)- 1
0 (0)- 2
1 (1)- 3
124-126
125-127
126 ( 0111 1110)-128
127 (0111 1111)127
128 (1000 0000)126
2522
2531

可以看出从 127 以后开始不对了。
使用这个宏必须满足一个条件。

两个值之间相差从逻辑值来讲应小于有符号整型的最大值。

如果HZ为1000,两个值之间相差小于25天左 右。

linux 常用时间结构
struct timespec 
{
    time_t tv_sec;       
    long   tv_nsec;        
};

typedef __kernel_time_t       time_t;  //long

struct timeval
{
__kernel_time_t tv_sec;        //从Epoch1970-01-01 00:00:00 UTC开始的秒数 ,long
__suseconds_t tv_usec;  /* Microseconds. */

struct tm {  
        int tm_sec;     /* seconds after the minute - [0,59] */  
        int tm_min;     /* minutes after the hour - [0,59] */  
        int tm_hour;    /* hours since midnight - [0,23] */  
        int tm_mday;    /* day of the month - [1,31] */  
        int tm_mon;     /* months since January - [0,11] */  
        int tm_year;    /* years since 1900 */  
        int tm_wday;    /* days since Sunday - [0,6] */  
        int tm_yday;    /* days since January 1 - [0,365] */  
        int tm_isdst;   /* daylight savings time flag */  
        };  
};
获取系统时间
//获取当前时间time_t(秒)  从Epoch
time_t t=time(NULL); 
//timeval 从Epoch
int gettimeofday(struct timeval *tv, struct timezone *tz);

时间转换和时间差
//转换time_t ==> struct tm,0时区的标准时间
struct tm *gmtime(const time_t *timep); 
//转换time_t ==> struct tm
struct tm *localtime(const time_t *timep);
//线程安全,传入返回结果的指针
struct tm *localtime_r(const time_t *timep, struct tm *result);  
//转换struct tm==> time_t
time_t mktime(struct tm *tm);
//double 和timespec转换
// struct timeval to double
double tv2double(const struct timeval *val)
{
    double rv;

    rv = val->tv_sec;
    rv += (double) val->tv_usec / uSecsPerSec;

    return rv;
}

// struct timespec to double
double ts2double(const struct timespec *val)
{
    double rv;

    rv = val->tv_sec;
    rv += (double) val->tv_nsec / nSecsPerSec;

    return rv;
}

// double to struct timespec
struct timespec double2ts(double amt)
{
    struct timespec rv;

    rv.tv_sec = floor(amt);    //floor ceil
    rv.tv_nsec = (amt - rv.tv_sec) * nSecsPerSec;
    // TODO: Handle cases where amt is negative
    while ((unsigned) rv.tv_nsec >= nSecsPerSec) {
        rv.tv_nsec -= nSecsPerSec;
        rv.tv_sec++;
    }

    return rv;
}

//时间差timespec
struct timespec tsDelta(const struct timespec *first,
                        const struct timespec *second)
{
    struct timespec rv;

    assert(first != NULL);
    assert(second != NULL);
    assert(first->tv_nsec >= 0 && first->tv_nsec < nSecsPerSec);
    assert(second->tv_nsec >= 0 && second->tv_nsec < nSecsPerSec);
    rv.tv_sec = second->tv_sec - first->tv_sec;
    if (second->tv_nsec >= first->tv_nsec) {
        rv.tv_nsec = second->tv_nsec - first->tv_nsec;
    } else {
        rv.tv_nsec = (second->tv_nsec + nSecsPerSec) - first->tv_nsec;
        rv.tv_sec--;
    }

    return rv;
}
// Delta (difference) between two struct timeval.
// It is expected that the time given by the structure pointed to by
// second, is later than the time pointed to by first.
struct timeval tvDelta(const struct timeval *first,
                       const struct timeval *second)
{
    struct timeval rv;

    assert(first != NULL);
    assert(second != NULL);
    assert(first->tv_usec >= 0 && first->tv_usec < uSecsPerSec);
    assert(second->tv_usec >= 0 && second->tv_usec < uSecsPerSec);
    rv.tv_sec = second->tv_sec - first->tv_sec;
    if (second->tv_usec >= first->tv_usec) {
        rv.tv_usec = second->tv_usec - first->tv_usec;
    } else {
        rv.tv_usec = (second->tv_usec + uSecsPerSec) - first->tv_usec;
        rv.tv_sec--;
    }

    return rv;
}
休眠延迟方法
//如果rmtp参数为NULL,不返回的剩余时间
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);

void usleep(int micro_seconds);

```c
//延迟, 输入浮点数时间
void DelayNonint(float amt)
{
    struct timespec   start, current, delta;
    struct timespec   remaining;

    // Get the time at which we started
    clock_gettime(CLOCK_MONOTONIC, &start);

    do {
        // Get current time
        clock_gettime(CLOCK_MONOTONIC, &current);

        // How much time is left
        delta = tsDelta(&start, &current);
        if (ts2double(&delta) > amt) { break; }

        // Request to sleep for the remaining time
        remaining = double2ts(amt - ts2double(&delta));
        (void) nanosleep(&remaining, NULL);
    } while (true);
}

//延迟, 输入浮点数时间
void DelayNonintSpin(float amt)
{
    struct timespec   start, current, delta;

    // Get the time at which we started
    clock_gettime(CLOCK_MONOTONIC, &start);

    do {
        // Get current time
        clock_gettime(CLOCK_MONOTONIC, &current);

        // How much time is left
        delta = tsDelta(&start, &current);
        if (ts2double(&delta) > amt) { break; }
    } while (true);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值