ページ

2013年10月3日木曜日

OpenRTM-aist用のリアルタイム性を強化した実行コンテキスト

[追記] メーリスの方で何点か指摘をいただきましたので追記させていただきます。

OpenRTMのリアルタイム実行コンテキストについては、バージョン0.4の頃からPOSIX準拠のものが開発されており、2010年にSICEに論文投稿もされているそうです。
また、OpenRTM-aist 1.1.0 からはPREEMPT RTなLinux向けの実行コンテキストが標準で組み込まれています。やっている処理はほぼ同じなので、特別な理由がなければそちらを利用するほうが良いと思います。

--

OpenRTM-aist用のリアルタイム性を強化した実行コンテキスト、RealtimePeriodicExecutionContext.soを作りました。

→ Githubにアップしてあります。

基本的な仕組みはARTExecutionContextと同じで、PeriodicExecutionContextを継承しています。
実行コンテキストが呼ばれると、自身のスレッドのスケジューリングポリシーをリアルタイムに設定し、より低遅延で実行されるようにします。
※RTCが管理者権限で実行されている必要があります。

RT_PREEMPTなLinux環境下では20-50usの遅延での実行がなされます。
Ubuntuへのリアルタイムカーネルのインストールについてはこちらの記事に書いてあります。

使用法は基本的にはART Linux用の実行コンテキストと同じで、

*Comp.cppに実行コンテキストの共有ライブラリを動的ロードするコードを追加し、

manager->load("RealtimePeriodicExecutionContext.so", "RealtimePeriodicExecutionContextInit");

rtc.conf内に利用する実行コンテキストを指定する記述を行えば

exec_cxt.periodic.type: RealtimePeriodicExecutionContext
exec_cxt.periodic.rate: 1000

完了です。

Linuxカーネルのタスクスケジューリングについて


Linuxカーネルでスケジューリングされるタスクは、静的優先度(と動的優先度)によって順序付けされます。静的優先度は0-139の値をとり
0-98の静的優先度を持つタスクをリアルタイムタスク、100-139の静的優先度を持つタスクを非リアルタイムタスクと呼びます。(参考)

リアルタイムタスクは常に非リアルタイムタスクよりも優先的に実行され、自分より優先度の高いリアルタイムタスクが実行待ち状態になった時点で、そのタスクは実行を停止(プリエンプト)されます。

※psコマンドのlオプションで確認できます。PRIカラムに優先度が表示されるのですが、この値は0-139の値をオフセットしたもので、

のようになっています。多くのタスクは優先度80で実行されており、RTタスクとして実行されているのはIRQ割り込みハンドラが殆どを占めています。

※また、リアルタイムタスクの静的優先度はsetschedulerで指定する事ができるのですが、その際の値は正負が逆転しており、

のようになっています。(参考)

RT PREEMPTなLinuxカーネルでのスケジューリング


すべてのタスクは自分より優先度の高いリアルタイムタスクが実行待ちになった瞬間に実行を停止されるのですが、タスクのスイッチングが発生するタイミングはある特定のタイミングに限られています。

通常のLinuxカーネルでは割り込み処理中やクリティカルセッションではプリエンプションが発生しません。RT PREEMPTなLinuxカーネルでは、それらの処理中でもプリエンプションの発生を許可することで、より低遅延でのリアルタイムタスクの起床を実現しています。(参考)

これにより、通常のLinuxカーネルでは最大3ms程度発生していたタスク実行の遅延を50-60usまで抑えることを可能としています。

通常の実行コンテキストとの比較


OpenRTM-aistのcoil::sleepによって実効待ちをする通常のPeriodicExecutionContextと比較して、処理が呼ばれるタイミングがどのように変化するかを確かめてみました。
それぞれの実行コンテキストを利用し、1000Hzでコンポーネントが動作するように設定した状態で実行し、処理が呼ばれたタイミングでタイムスタンプを書き出します。それらのタイムスタンプのインターバルをプロットしたのが下の図になります。

RT PREEMPTカーネルとこのECを使うことで実行周期がかなり改善されているのがわかると思います。平均実行周期は0.0010133となったので、約13usの遅延でプリエンプションが発生しているようですね。ただ、冒頭に実行周期が5msとなっている箇所が1箇所あるのが見て取れます。これはページフォールトの発生によるものだと推測されます。[下の追記参照]

リアルタイムタスクの実装ではスワップを避けるためにメモリをロックし、またページフォールトを避けるために予めある程度のメモリを一時的に確保&開放します。今回ECをつくるにあたって、RTCの処理にどの程度メモリが必要か不明だったのですが、このECではプレフォールトのサイズを8KBとしてしまっています。そのため、これ以上のメモリが確保されたタイミングでページフォールトが発生して遅延が起きる可能性があります。この問題にはおいおい対策していく必要がありそうですね…(参考)

なお、テストはUbuntu 12.04 LTS amd64環境で行っています。
リアルタイムカーネルには 3.2.0-23-realtime #36~ppa1-Ubuntu SMP PREEMPT RT を用いました。

--

[追記]

5msの遅延は当初、ページフォールトのためかと思っていたのですが、これはSMI(System Management Interrupt)である可能性が高いとのご指摘を頂きました。この割り込みは古いハードウェアのエミュレーションなどの際に利用され、数msのCPU時間を消費する場合があるそうです。(参考)

BIOSでSMIを無効にするか、SMIを無効にするドライバモジュールを読み込むことで対処できるそうですが、熱管理など非常に多くの箇所で利用されているため、問題が発生する可能性があるそうです。(参考)

また、割り込み処理をが行われるコアとは別に、リアルタイムタスクに専用のコアを割り当てることで問題を解決できる可能性があるそうです。これはirqbalanceデーモンで特定のコアで割り込み処理を処理しないようにした上で、tasksetユーティリティで特定のコアのみであるプロセス処理を実行するようにします。(参考)






2 件のコメント:

  1. /etc/security/limits.confの値は変更していますか?

    返信削除
    返信
    1. コメントありがとうございます。
      とくに変更はしておらず、デフォルトのままで無制限となってます。
      ulimitで確認してもunlimitedと出るので、おそらく問題はなさそうなのですが…

      なにか設定を行わないといけないものなのでしょうか?

      削除