Jiffies カウンターは、サイズが 4 バイトの符号なし整数を返します。カウンターが最大値に達すると、再び 0 から再開します。期間を取得するために、古い値で最新の値を減算します。では、古い値が最大値で新しい値がゼロより大きい場合、間違った期間を取得するようにするにはどうすればよいですか?
2 に答える
何もする必要はありません。正しい期間があります(4バイトの符号なし整数を使用してすべての計算を行う限り)。これは、固定幅の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
...
単一の問題は、期間がカウンターの最大値と比較して小さくない場合、つまり、最大値の半分より大きくなる可能性がある場合に発生します。
このアドバイスは、2 つのイベント a と b、つまり a が実際には b の前に発生し、それらの間の距離が 2^(32-1) 未満である場合にのみ正しくなります。(ba) を計算すると、正しい答えが得られます。大きい unsigned 値から小さい unsigned 値を差し引くと (時間の順序を定義すると考えて)、必要な答えの負の値を取得できます。
そのため、新しい循環比較操作も考慮する必要があります (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 番目の値の間の実際の距離が常に 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 シーケンス番号と他のいくつかの同様のカウンターでそれを見てきました。したがって、2 秒の褒め言葉ですべてを処理できるわけではありません。