Jiffies 计数器返回一个大小为四个字节的无符号整数。当计数器达到最大值时,它会再次从 0 重新开始。我将用旧值减去最新值以获得持续时间。那么我应该如何考虑这样一种情况,即当旧值是最大值而新值大于零时,我会得到错误的持续时间?
2 回答
您无需执行任何操作,您将拥有正确的持续时间(只要您使用四个字节的无符号整数进行所有计算)。这就是以固定宽度二进制算术实现的整数值的魔力。
这是一个带有 8 位无符号整数的示例。您实际上可以看到,即使存在溢出,差异仍然有效。
236 - 221 = 11101100 - 11011101 = 11101100 + 00100011 = 00001111 = 15
251 - 236 = 11111011 - 11101100 = 11111011 + 00010100 = 00001111 = 15
10 - 251 = 00001010 - 11111011 = 00001010 + 00000101 = 00001111 = 15
25 - 10 = 00011001 - 00001010 = 00011001 + 11110110 = 00001111 = 15
...
当持续时间与计数器的最大值相比不小时,您的唯一问题就出现了,即当它可以大于最大值的一半时。
仅当您的两个事件 a 和 b,a 实际发生在 b 之前,并且它们之间的距离小于 2^(32-1) 时,此建议才正确。如果你计算 (ba) 那么你会得到正确的答案。如果您从较大的无符号数中减去较小的无符号数(认为定义了它们的时间顺序),那么您可以获得您想要的答案的负数。
因此,您还需要考虑新的循环比较操作(请参阅 Linux time_after、time_before 并使用它们等)。
有符号和无符号比较都是错误的,因为环绕的计数器与有符号或无符号数字并不完全相同。考虑 8 位情况:
a=250, b=20 #8-bit sequence number a was created before b
a=120, b=130 #8-bit sequence number a was created before b
具有相同位数的有符号值和无符号值之间的主要区别在于比较运算符的实现。由于决定用 1 或 0 对负值进行符号扩展,因此在分配给具有更多位的值时,有符号与无符号非常重要。
考虑一个新的小于为环绕数字设计的定义:
LT_CIRCULAR_32(250,5) == True //like signed?
LT_CIRCULAR_32(0,11) == True
LT_CIRCULAR_32(127,138) == True //like unsigned?
只要第一个值和第二个值之间的实际距离始终小于 2^(32-1),这种比较就有效。
想象一个有 256 个位置的圆圈,计数器 a 和 b 顺时针前进。如果 a 可以以小于 2^(8-1) 的增量达到 b,则“a < b”。这是一个考虑换行的比较,它既没有签名也没有签名:
#define LT_CIRCULAR_LONG(a,b) ((((long)(b)-(long)(a)) < 0)==0)
这就是为什么 time_after 看起来是这样的原因。b 和 a 的强制转换分别确保符号扩展是一致的。使值带符号并检查是否小于零是测试高位的技巧。
我正在观察一位开发人员现在用 jiffies 处理这个问题(即:-300 jiffies),并且已经看到它使用 TCP 序列号和其他一些类似的计数器。所以,2s恭维并不能解决所有问题。