序文 Linux カーネルでは、元のコードとの互換性を保つため、または特定の仕様に準拠するため、また現在高まる精度の要件を満たすために、さまざまな時間関連のデータ構造がさまざまな目的で実装されています。 1) jiffies and jiffies_64 カーネルは jiffies_64 グローバル変数を使用して、システムの起動後に経過したティック数を記録します。宣言は次のとおりです (コードは kernel/time/timer.c にあります)。 __visible u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; エクスポートシンボル(jiffies_64); jiffies_64 は 64 ビットの符号なし整数として定義されていることがわかります。ただし、歴史的な理由により、カーネル ソース コードには jiffies と呼ばれる別の変数も含まれています。 jiffies への参照 (コードは include/linux/jiffies.h にあります) は次のように宣言されます。 外部 u64 __cacheline_aligned_in_smp jiffies_64; extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies; したがって、jiffies 変数は unsigned long 型のグローバル変数であり、32 ビット プロセッサの場合は 4 バイト (32 ビット) のみの長さになります。ただし、プロセッサの長さが 64 ビットの場合、グローバル変数 jiffies と jiffies_64 は同等になります。 しかし、すべてのコードを検索しても、グローバル変数 jiffies の定義は見つかりません。最終的に、カーネル リンク スクリプトで次の行が見つかりました (Arm64 アーキテクチャの場合、スクリプトは arch/arm64/kernel/vmlinux.lds.S にあります)。 ジフィー = jiffies_64; 秘密はここにあります。シンボル jiffies と jiffies_64 は、リンク時に同じアドレスを指すように指定されていることがわかります。つまり、32 ビット マシンでは、jiffies と jiffies_64 の下位 4 バイトは同じです。 一般に、32 ビット マシンでも 64 ビット マシンでも jiffies グローバル変数に直接アクセスできますが、jiffies_64 グローバル変数を取得する場合は、get_jiffies_64 関数を呼び出す必要があります。 64 ビット システムの場合、これら 2 つは同じであり、jiffies は揮発性でキャッシュ アラインメントとして宣言されているため、jiffies を直接返すだけです。 静的インライン u64 get_jiffies_64(void) { (u64)jiffies を返します。 } 32 ビット システムの場合、64 ビットの読み取りと書き込みはアトミックではないため、jiffies_lock 読み取り順序ロックも必要です。 u64 get_jiffies_64(void) { 符号なし整数シーケンス; u64 戻り; する { seq = read_seqbegin(&jiffies_lock); ret = jiffies_64; } while (read_seqretry(&jiffies_lock, seq)); ret を返します。 } 基本的に、jiffies はティックが到着するたびに 1 ずつ増加し、ティック期間 HZ はカーネルのコンパイル オプションによって設定されます。 32 ビット システムでは、HZ が 250 に設定され、各ティックの期間が 4 ミリ秒であると想定し、カウンターは 200 日以内に最大値に達した後にオーバーフローします。 HZ を高く設定すると、オーバーフロー時間が短くなります。もちろん、64 ビット システムで実行している場合は、この問題についてまったく心配する必要はありません。したがって、時間の比較に jiffies を使用する場合は、システムによって定義されているいくつかのマクロを使用する必要があります。 時間後(a,b) 時間前(a,b) 時間後の等式(a,b) 時間前等価(a,b) 範囲オープン時間(a,b,c) 時間_is_before_jiffies(a) 時間経過後(a) 時間_is_before_eq_jiffies(a) 時間_is_after_eq_jiffies(a) 安全のため、カーネルは対応する 64 ビット バージョンも提供します。これらのマクロはラッピング問題を効果的に解決できますが、無制限ではありません。具体的にはどのように行うのでしょうか? time_after マクロを見てみましょう: #time_after(a,b) を定義します \ (typecheck(符号なしlong, a) && \ 型チェック(符号なしlong、b) && \ ((長い)((b) - (a)) < 0)) まず、2 つの変数の型チェックを行います。両方とも unsigned long 型である必要があります。最も重要なことは、まず 2 つの符号なし長整数を減算し、次にそれらを符号付き長整数に変換し、それが負の数であるかどうか、つまり 32 ビットの最上位ビットが 1 であるかどうかを判断することです。 なぜこれによって、いわゆるラップアラウンド問題が部分的に解決できるのでしょうか?簡単にするために、8 ビットの符号なし整数を例に挙げます。その値の範囲は 0 ~ 255 (0xFF) です。現在の時刻が 250 であるとすると、5 ティック後には 255 になり、表現できる最大値に達します。このとき、さらにもう1ティック経過すると、つまり6ティック経過すると、オーバーフローして0になります。この時点で、単純に 2 つの値を比較してどちらの時間が遅いかを判断すると、6 ティック後の時間は 0 となり、現在の時間よりも小さくなるため、明らかに間違いになります。この問題は、いわゆるラップアラウンドです。ただし、最初に 2 つの数値、つまり 0 ~ 250 (0 ~ 0xFA) を減算すると、オーバーフローも発生し、最終的な数値はちょうど 6 になります。ただし、これにも制限があり、比較する 2 つの時間の差は最大表現範囲の半分を超えることはできません。現在の時刻がまだ 250 であると仮定すると、128 ティック後に時刻の値は 122 になります。2 つを減算すると、結果は 122-250 (0x86-0xFA) となり、減算された数値は 128 になります。このとき、符号付きの数値に変換すると負の数になり、結果が間違っています。 さらに、jiffies は Tick ごとに更新され、Tick 期間はコンパイル時に定義されるため、jiffies の値を経過した特定の時間に変換することができ、その逆も可能です。そのため、カーネルは次の変換関数を提供します。 符号なし整数 jiffies_to_msecs(const 符号なし long j); 符号なし整数 jiffies_to_usecs(const 符号なし long j); 符号なしlong msecs_to_jiffies(const unsigned int m); unsigned long usecs_to_jiffies(const unsigned int u); 2) timespec と timespec64 Timespec は秒とナノ秒で構成され、次のように定義されます (コードは include/uapi/linux/time.h にあります)。 構造体タイムスペック{ カーネル時間tv_sec; 長いtv_nsec; }; tv_sec: 1970 年 1 月 1 日 00:00 (UTC 時間) から経過した秒数を格納します。 __kernel_time_t は最終的には long 型として定義されます。つまり、32 ビット システムでは 32 ビット長、64 ビット システムでは 64 ビット長になります。 tv_nsec: 最後の 1 秒から経過したナノ秒数 (ns) を格納します。 Timespec には 64 ビットの拡張構造体もあり、次のように定義されています (コードは include/linux/time64.h にあります)。 typedef __s64 time64_t; ...... 構造体timespec64 { time64_t テレビ秒; 長いtv_nsec; }; この構造体の変数定義は、tv_sec の型が 64 ビットの符号なし数値である必要があることを除いて、timespec と同じです。したがって、64 ビット システムでは、timespec と timespec64 の構造はまったく同じです。 3) ktime_t Linux の時間サブシステムでは、時間を表すために ktime_t が一般的に使用され、次のように定義されます (コードは include/linux/ktime.h にあります)。 s64 ktime_t をタイプ定義します。 これは、時間単位をナノ秒単位で表す非常に単純な 64 ビットの符号付き整数です。 4) タイムバル gettimeofday 関数と settimeofday 関数は、時間単位として timeval を使用します。 構造体timeval{ カーネル時間tv_sec; __kernel_suseconds_t tv_usec; }; tv_sec: 1970 年 1 月 1 日 00:00 (UTC 時間) から経過した秒数を格納します。 __kernel_time_t は最終的には long 型として定義されます。つまり、32 ビット システムでは 32 ビット長、64 ビット システムでは 64 ビット長になります。 tv_usec:__kernel_suseconds_t は実際には long 型として定義されており、前の秒から経過したマイクロ秒数 (us) を格納します。 したがって、この構造体は実際には timespec 構造体と非常によく似ています。tv_sec に格納される値は同じであり、timeval の tv_usec を取得するには、timespec の tv_nsec を 1000 で割るだけで済みます。 要約する Linux 時間サブシステムの時間表現に関するこの記事はこれで終わりです。Linux 時間表現の詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
>>: echartsマップカルーセルハイライトを解決するための記事
この記事の例では、参考までに時間セレクターを実装するためのjQueryの具体的なコードを共有していま...
この記事のガイド: テーブル内のデータを削除するには、削除と切り捨ての 2 つの方法があります。TR...
VScode リモートデバッグ Linux プログラムの問題について見てみましょう。具体的な内容は以...
のようにLIKE ではデータ全体が一致する必要がありますが、REGEXP では部分的な一致のみが必要...
前回の記事では、Docker Desktop をインストールし、Kubernetes を有効にしまし...
システムバージョン [root@ ~]# cat /etc/redhat-release CentO...
現在、ますます多くのフロントエンド開発者が、元のテーブル レイアウトを xHTML + CSS に置...
目次1. 縦方向のスライス1.1 垂直データベース1.2 垂直テーブル分割2. 水平(横断)セグメン...
目次1. 属性を追加する2. 複数のオブジェクトを結合する3. オブジェクトのプロパティを削除する4...
目次1.まずネットワークカードの設定ディレクトリに入る2. ifcfg-ens33ネットワークカード...
この記事は、@C7210 によって翻訳されたブログ「Usability Counts」からの翻訳です...
Tomcat の上位バージョンでは、デフォルト モードは NIO モードを使用することになります。...
目次CentOS 8にDockerをインストールする1. yumを更新する2. containerd...
mysql5.7 でリモート アクセスを設定することは、ユーザーを作成して権限を付与するだけでアクセ...
SUSE Linuxでルートパスワードを忘れた場合の解決方法SUSE (Linux オペレーティング...