Linux で推奨される方法で 1 ミリ秒の解像度のタイマーを使用する

StackOverflow https://stackoverflow.com/questions/240058

  •  04-07-2019
  •  | 
  •  

質問

Linux下で1msの解像度のタイマーティックが必要です。これはタイマー値をインクリメントするために使用され、その後、さまざまなイベントをトリガーする必要があるかどうかを確認するために使用されます。glibc 要件のため、POSIX timerfd_create はオプションではありません。timer_create と timer_settimer を試しましたが、それらから得られる最高の解像度は 10 ミリ秒で、それより小さい値はデフォルトで 10 ミリ秒の解像度になるようです。マンページによると、Getittimer と setitimer の分解能は 10 ミリ秒です。

私が現在考えているこのタイマーを実行する唯一の方法は、メインループで CLOCK_MONOTONIC を指定して Clock_gettime を使用し、ミリ秒が経過したかどうかをテストし、経過した場合はカウンターを増やすことです (その後、さまざまなイベントが発生するかどうかを確認します)。

これを行うには、メインループで常にクエリを実行するより良い方法はありますか?これに対する推奨される解決策は何ですか?

私が使用している言語は単純な古い c です

アップデート
2.6.26 カーネルを使用しています。1kHz で割り込みができることはわかっていますし、POSIX timer_* 関数は最大 1ms までプログラムできるのですが、それは信頼性が低そうなので使いたくないです。一部のバージョンでは新しいカーネルが必要になる可能性があるためです。システム。一部の標準カーネルにはまだ 100Hz が設定されているようです。そしてそれを検出する必要があります。アプリケーションは私のシステム以外で実行されている可能性があります:)

対応しなければならないネットワーク イベントがある可能性があるため、1 ミリ秒も眠ることができません。

どのように解決したかそれほど重要ではないので、グローバル タイマーの分解能が 100 ミリ秒であるとだけ宣言しました。独自のタイマーを使用するすべてのイベントでは、タイマーの有効期限を少なくとも 100 ミリ秒に設定する必要があります。もっと良い方法があるのではないかと多かれ少なかれ思っていたので、質問させていただきました。

なぜその答えを受け入れたのかfreespace からの回答が、リアルタイム Linux システムなしでは実際には不可能である理由を最もよく説明していると思います。

役に立ちましたか?

解決

メインループのポーリングも答えではありません。プロセスがCPU時間をあまり取得しない可能性があるため、コードが実行される前に10ミリ秒以上が経過し、意味がなくなります。

10msは、ほとんどのリアルタイムオペレーティングシステム(RTOS )。しかし、非RTOSでは意味がありません-スケジューラーとディスパッチャーの動作は、タイマーの期限切れにどれだけ早く応答できるかに大きく影響します。たとえば、10ミリ秒未満の解像度のタイマーがある場合でも、コードが実行されていない場合、タイマーの期限切れに応答することはできません。コードがいつ実行されるかを予測できないため、タイマーの期限切れに正確に対応することはできません。

もちろんリアルタイムのLinuxカーネルがあります。 http://www.linuxdevices.com/articlesをご覧ください。 /AT8073314981.html をご覧ください。 RTOSには、コードがいつ実行されるかについてソフトまたはハードの保証を取得できる機能があります。これは、タイマーの期限切れなどに確実かつ正確に対応する唯一の方法です。

他のヒント

1msの解像度タイマーを取得するには、 libevent が行うことを行います。

タイマーを min-heap に整理します。つまり、ヒープの最上部は最も早い有効期限(絶対)時間(rbツリーも機能しますが、オーバーヘッドが大きくなります)。メインイベントループで select()または epoll()を呼び出す前に、最も早いタイマーの有効期限から現在までのデルタをミリ秒単位で計算します。このデルタを select()のタイムアウトとして使用します。 select()および epoll()タイムアウトの解像度は1msです。

上記で説明したメカニズムを使用するタイマー解決テストがあります(libeventではありません)。このテストでは、目的のタイマーの有効期限と実際の1ms、5ms、10msの有効期限の差を測定します。

1000 deviation samples of  1msec timer: min=  -246115nsec max=  1143471nsec median=   -70775nsec avg=      901nsec stddev=    45570nsec
1000 deviation samples of  5msec timer: min=  -265280nsec max=   256260nsec median=  -252363nsec avg=     -195nsec stddev=    30933nsec
1000 deviation samples of 10msec timer: min=  -273119nsec max=   274045nsec median=   103471nsec avg=     -179nsec stddev=    31228nsec
1000 deviation samples of  1msec timer: min=  -144930nsec max=  1052379nsec median=  -109322nsec avg=     1000nsec stddev=    43545nsec
1000 deviation samples of  5msec timer: min= -1229446nsec max=  1230399nsec median=  1222761nsec avg=      724nsec stddev=   254466nsec
1000 deviation samples of 10msec timer: min= -1227580nsec max=  1227734nsec median=    47328nsec avg=      745nsec stddev=   173834nsec
1000 deviation samples of  1msec timer: min=  -222672nsec max=   228907nsec median=    63635nsec avg=       22nsec stddev=    29410nsec
1000 deviation samples of  5msec timer: min= -1302808nsec max=  1270006nsec median=  1251949nsec avg=     -222nsec stddev=   345944nsec
1000 deviation samples of 10msec timer: min= -1297724nsec max=  1298269nsec median=  1254351nsec avg=     -225nsec stddev=   374717nsec

テストはFedora 13カーネル2.6.34でリアルタイムプロセスとして実行され、1msタイマーの最高達成精度はavg = 22nsec stddev = 29410nsecでした。

それが最善の解決策かどうかはわかりませんが、カーネルの高解像度タイマーを使用してタイミングをとる小さなカーネルモジュールを作成することを検討してください。基本的に、読み取りが1ミリ秒間隔でのみ返されるデバイスファイルを作成します。

このタイプのアプローチの例は、ztdummyモジュールを介してAsterisk PBXで使用されます。 ztdummyを検索すると、これを行うコードを見つけることができます。

カーネルはアプリケーションが常にCPUを取得することを保証しないため、メインループでの一定のクエリでも標準のLinuxで1ミリ秒の精度を達成するのは難しいと思います。たとえば、プリエンプティブマルチタスクのために数十ミリ秒スリープすることができますが、それに対してできることはほとんどありません。

Real-Time Linux をご覧ください。

x86プラットフォームをターゲットにしている場合は、HPETタイマーを確認する必要があります。これは非常に正確なハードウェアタイマーです。それはあなたの母によってサポートされなければなりません(今はそれらのすべてがそれをサポートしています)そしてあなたのカーネルも同様にそれのためのドライバを含むべきです。問題なくそれを数回使用しましたが、1msよりもはるかに優れた解像度を達成することができました。

ここにいくつかのドキュメントと例があります:

gettimeofday / usleepベースのポーリングでokの結果を取得したことを思い出すようです-毎秒1000個のタイマーは必要ありませんでしたが、必要なティックのタイミングについては十分な精度が必要でした-私のアプリはMIDIドラムマシンコントローラー。非常に悪いドラマー(特にMIDIの組み込みレイテンシをカウントする)のように鳴らしたくない場合は、ドラムマシンに必要なサブミリ秒の精度を得たことを覚えているようです。 (2005年だったため、記憶が少し曖昧になりました)usleepを使用すると、ターゲット時間の200マイクロ秒以内になりました。

しかし、私はシステムで他の多くを実行していませんでした。制御された環境がある場合、そのようなソリューションで逃げることができるかもしれません。システムでさらに処理が行われている場合(cronがupdatedbを起動するのを見るなど)、物事はばらばらになる可能性があります。

Linux 2.4カーネルで実行していますか?

VMware KB記事#1420( http://kb.vmware.com/kb/1420 )。

  

LinuxゲストOS   タイマー割り込みのカウントによる時間。   パッチ未適用の2.4以前のカーネル   仮想システムタイマーをプログラムする   100Hzでのクロック割り込み要求(100   1秒あたりの割り込み)。 2.6カーネル、   一方、割り込みを要求する   1000Hz-10倍の頻度で。一部   ディストリビューションベンダーによって2.6の機能を含むように変更された2.4カーネル   1000Hzの割り込みを要求する、または   ケース、他のレートでの割り込みなど   512Hzとして。

まず、カーネル ソースを取得し、調整された HZ パラメーターを使用してコンパイルします。

  • もし HZ=1000, 、タイマーは 1 秒あたり 1000 回中断します。使用しても大丈夫です HZ=1000 i386 マシンの場合。
  • 組み込みマシンでは、HZ が 100 または 200 に制限される場合があります。

正常に動作するには、PREEMPT_KERNEL オプションをオンにする必要があります。があります このオプションを適切にサポートしていないカーネル。あなたはそれらをチェックアウトすることができます 探訪。

最近のカーネル、つまり2.6.35.10は、NO_HZオプションをサポートし、 動的ティックで。これは、アイドル状態のときにタイマーティックがないことを意味します。 ただし、タイマーティックは指定された瞬間に生成されます。

カーネルには RT パッチがありますが、ハードウェアのサポートは非​​常に限定されています。

一般的にRTAIはあなたの問題に対するすべてのキラーソリューションですが、その ハードウェアのサポートは非常に限られています。ただし、次のような優れたCNCコントローラー emc2では、クロッキングにRTAIを使用しますが、おそらく5000 Hzですが、 それをインストールするためのハードワーク。

可能であれば、ハードウェアを追加してパルスを生成できます。そうすると、 あらゆるOSバージョンに適応できるシステム。

少なくとも1ミリ秒スリープするループでnanosleepを使用できますか?それともglibcのものですか?

更新:気にしないで、manページから「プロセスが再び実行可能になるまで指定よりも最大10ミリ秒かかることがあります」

単純なリアルタイムアプリケーションにRTOSは必要ありません。最新のプロセッサにはすべて、汎用タイマーがあります。作業中のターゲットCPUのデータシートを入手します。カーネルソースを見ると、archディレクトリの下に、これらのタイマーの処理方法を示すプロセッサ固有のソースがあります。

これには次の2つのアプローチがあります。

1)アプリケーションは、ステートマシンのみを実行し、他は何も実行していません。 Linuxは単に「ブートローダー」です。キャラクターデバイスをインストールするカーネルオブジェクトを作成します。カーネルに挿入したら、GPタイマーを連続して実行するように設定します。あなたはそれが動作している周波数を知っています。次に、カーネルで、ウォッチドッグを明示的に無効にします。割り込みを無効にします(ハードウェアとソフトウェア)シングルCPU Linuxカーネルでは、spin_lock()を呼び出すことでこれを実現します(決して手放しません)。CPUはあなたのものです。ビジーループ。必要なティック数が経過するまでGPTの値をチェックし、経過したら次のタイムアウトの値を設定して処理ループに入ります。コードのバースト時間が1ミリ秒未満であることを確認してください

2)2番目のオプション。これは、プリエンプティブLinuxカーネルを実行していることを前提としています。実行中のOSの横に未使用のGPTをセットアップします。ここで、1msのタイムアウトが発生する前に、構成可能なマージンを発生するように割り込みを設定します(たとえば50-75 uSec)。待機OUTで割り込みを有効にします。これは、割り込みを無効にするカーネルの他の要素と協力しているという事実を説明しています。これは、長時間(100us以上)割り込みをロックアウトする他のカーネルアクティビティがないことを前提としています。これで、発火イベントの精度を測定し、ニーズを満たすまでウィンドウを大きくすることができます。

代わりに、RTOSの仕組みを学習しようとしている場合、または複数のリアルタイムの責任を伴う制御問題を解決しようとしている場合は、RTOSを使用します。

" / dev / rtc0"の使用について(または" / dev / rtc")デバイスとそれに関連するioctl()インターフェース?正確なタイマーカウンターを提供すると思います。レートを1 msだけに設定することはできませんが、近い値または1/1024秒(1024Hz)、または8192Hzなどのより高い周波数に設定することはできません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top