SD/USB へのバースト書き込みにより、組み込み Linux 上のタイム クリティカルなアプリが停止してしまう

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

質問

私は、ARM9 をハードウェア ビデオ エンコーダ チップに接続し、ビデオを SD カードまたは USB スティックに書き出す組み込み Linux プロジェクトに取り組んでいます。ソフトウェア アーキテクチャには、バッファのプールにデータを読み取るカーネル ドライバーと、マウントされたリムーバブル デバイス上のファイルにデータを書き込むユーザーランド アプリが含まれます。

特定のデータ速度 (約 750k バイト/秒) を超えると、ユーザーランドのビデオ書き込みアプリが約 5 秒ごとに 0.5 秒間停止し始めることがわかりました。これはカーネル ドライバーのバッファー不足を引き起こすのに十分です。また、バッファーの数を増やすことができたとしても、ビデオ データはリアルタイムで進行している他の処理と (理想的には 40 ミリ秒以内に) 同期する必要があります。これらの 5 秒間の「ラグ スパイク」の間に、書き込みは 40 ミリ秒以内に完了します (アプリに関する限り、OS によってバッファリングされていることを感謝します)。

この遅延のスパイクは、Linux がデータをディスクにフラッシュする方法に関係していると思います。pdflush は 5 秒ごとに起動するように設計されていることに注意してください。私の理解では、これが書き込みを行うものであると考えています。ストールが終わるとすぐに、ユーザーランド アプリはすぐにサービスを提供し、バッファーのバックログ (オーバーフローしなかったもの) を書き込むことができます。

私が書き込みを行っているデバイスには、妥当な最終的なスループットがあると思います。メモリ ファイルから 15 MB のファイルをコピーし、同期が完了する (USB スティックのライトの点滅が止まる) まで待機すると、書き込み速度は約 2.7 MBytes/秒になりました。

私は 2 種類の手がかりを探しています。

  1. バースト的な書き込みによってアプリが停止するのを防ぐには、どうすればよいでしょうか。プロセスの優先順位、リアルタイム パッチ、バースト的ではなく継続的に書き込むようにファイル システム コードを調整するなどです。

  2. カード/スティックへの書き込みバックログとスループットに関してファイルシステムで何が起こっているかをアプリに認識させるにはどうすればよいですか?ハードウェア コーデックのビデオ ビットレートをオンザフライで変更することができます。これは、フレームをドロップしたり、最大許容ビットレートに人為的な上限を課したりするよりもはるかに優れています。

さらに詳しい情報:これは、現在 Montavista 2.6.10 ベースのカーネルを実行している 200MHz ARM9 です。

更新情報:

  • ファイルシステム SYNC をマウントすると、スループットが非常に低下します。
  • リムーバブル メディアは FAT/FAT32 でフォーマットされており、メディアを Windows PC に接続して読み取ることができるように設計されている必要があります。
  • たとえば、定期的に sync() または fsync() を呼び出すと、毎秒定期的にストールが発生し、許容できないほどスループットが低下します。
  • 私は fopen() などではなく write() と open(O_WRONLY | O_CREAT | O_TRUNC) を使用しています。
  • 前述の「Linux リアルタイム ファイルシステム」については、オンラインですぐには見つかりません。リンク?

これが理にかなっているといいのですが。stackoverflow に関する最初の組み込み Linux の質問ですか?:)

役に立ちましたか?

解決

記録のために書いておきますが、最も極端な場合を除いて、問題を解決できると思われる 2 つの主要な側面があることが判明しました。このシステムはまだ開発中であり、まだ徹底的な拷問テストは行われていませんが、かなりうまく機能しています(タッチウッド)。

大きな成果は、userland Writer アプリをマルチスレッド化したことによってもたらされました。場合によっては、 write() の呼び出しがブロックされることがあります。他のプロセスとスレッドは引き続き実行されます。デバイス ドライバーを処理し、実行中の他のアプリと同期するためにフレーム カウントやその他のデータを更新するスレッドがある限り、期限を破ることなくデータをバッファリングして数秒後に書き出すことができます。最初に単純なピンポン ダブル バッファを試しましたが、それだけでは十分ではありませんでした。小さなバッファは圧倒され、大きなバッファはファイルシステムが書き込みを消化する間に大きな一時停止を引き起こすだけです。スレッド間でキューに入れられた 10 個の 1MB バッファーのプールは現在、正常に動作しています。

もう 1 つの側面は、物理メディアへの最終的な書き込みスループットを監視することです。このため、私はステータス Dirty に注目しています。/proc/meminfo によって報告されます。ダーティの場合にエンコーダーをスロットルするための大まかなコードがいくつかあります。特定のしきい値を超えると、漠然と機能しているように見えます。後でさらにテストと調整が必要になります。幸いなことに、私にはたくさんの RAM (128M) があるので、数秒以内にバックログが蓄積し、スムーズにスロットルダウンするのを確認できます。

この問題に対処するために他に何かする必要があることがわかった場合は、忘れずに戻ってこの回答を更新するように努めます。他の回答者様に感謝します。

他のヒント

いくつか提案をさせていただきますが、アドバイスは安いものです。

  • ディスクへの書き込みには下位レベルの API を使用していることを確認し、次のようなユーザーモードのキャッシュ関数を使用しないでください。 fopen, fread, fwrite 下位レベルの関数を使用する open, read, write.
  • を渡す O_SYNC ファイルを開くときにこのフラグを設定すると、ディスクに書き込まれるまで各書き込み操作がブロックされ、書き込みのバースト動作がなくなりますが、各書き込みの速度は遅くなります。
  • ビデオ データのチャンクを取得するためにデバイスから読み取り/ioctl を実行している場合は、アプリケーションとカーネルの間に共有メモリ領域を割り当てることを検討してください。そうしないと、大量のエラーが発生します。 copy_to_user ビデオ データ バッファをカーネル空間からユーザー空間に転送するときに呼び出します。
  • USB フラッシュ デバイスがデータを書き込むための持続的な転送を行うのに十分な速度であることを検証する必要がある場合があります。

ちょっと考えてみましたが、お役に立てば幸いです。

ここ これは、書き込み負荷の高い操作に対する pdflush のチューニングに関する情報です。

Linux リアルタイム ファイルシステムを探しているようですね。必ず Google などで検索してください。

XFS にはリアルタイム オプションがありますが、私はそれを試したことはありません。

hdparm を使用すると、キャッシュを完全にオフにできる場合があります。

ファイルシステム オプションを調整すると (余分な不要なファイル属性をすべてオフにする)、フラッシュする必要があるものが減り、フラッシュが高速化される可能性があります。しかし、それがあまり役立つとは思えません。

しかし、私の提案は、スティックをファイルシステムとして使用することをまったく避け、代わりにローデバイスとして使用することです。「dd」を使用するのと同じように、データを詰め込みます。次に、別の場所でその生データを読み取り、ベイク後に書き込みます。

もちろん、それがあなたにとって選択肢であるかどうかはわかりません。

デバッグ補助機能があり、strace を使用してどの操作に時間がかかっているかを確認できます。FAT/FAT32 には驚くべきことがいくつかあるかもしれません。

単一のファイルに書き込みますか、それとも複数のファイルに書き込みますか?

キューに書き込む準備ができているビデオ バッファのプールを維持する読み取りスレッドを作成できます。フレームが受信されると、それがキューに追加され、書き込みスレッドに通知されます。

共有データ

empty_buffer_queue
ready_buffer_queue
video_data_ready_semaphore

スレッドを読んでいる:

buf=get_buffer()
bufer_to_write = buf_dequeue(empty_buffer_queue)
memcpy(bufer_to_write, buf)
buf_enqueue(bufer_to_write, ready_buffer_queue)
sem_post(video_data_ready_semaphore)

書き込みスレッド

sem_wait(vido_data_ready_semaphore)
bufer_to_write = buf_dequeue(ready_buffer_queue)
write_buffer
buf_enqueue(bufer_to_write, empty_buffer_queue)

スレッド化された書き込みがカーネルの待機中にブロックされている場合、これは機能する可能性があります。ただし、カーネル空間内でブロックされている場合は、2.6.10 よりも新しいカーネルを探す以外にできることは何もありません。

お客様の特定の状況について詳しく知りませんので、次のような推測しかできません。

fsync()/sync() を使用して、カーネルにストレージ デバイスへのデータのフラッシュをより頻繁に強制してみてください。カーネルがすべての書き込みをバッファリングしてから、実際の書き込みを実行中にバスを拘束するか、システムを停止させているようです。fsync() を注意深く呼び出すと、よりきめ細かい方法でシステム バス経由の書き込みをスケジュールすることができます。

エンコード/キャプチャ (ビデオ キャプチャについては言及していないので、ここでは仮定を立てています。さらに情報を追加する必要があるかもしれません) タスクが独自のスレッドで実行されるようにアプリケーションを構造化することは理にかなっているかもしれません。出力をユーザーランドにバッファリングし、2 番目のスレッドがデバイスへの書き込みを処理できるようになります。これにより、エンコーダーがブロックすることなく常に書き込みを完了できるようにするスムージング バッファーが提供されます。

疑わしいと思われる点の 1 つは、この問題が特定のデータ レートでのみ発生することです。これが本当にバッファリングの問題である場合、より低いデータ レートでは問題の発生頻度が低くなることが予想されますが、それでもこの問題が発生することは予想されます。問題。

いずれにせよ、さらなる情報が役に立つかもしれません。システムのアーキテクチャは何ですか?(非常に一般的な言葉で。)

ご提供いただいた追加情報を考慮すると、小規模な書き込みや頻繁なフラッシュではデバイスのスループットがかなり低いように思えます。より大きな書き込みでも十分なスループットを得ることができると確信している場合 (実際にあるかどうかはわかりませんが、書き込みのたびに FAT を更新するなど、ファイル システムが愚かなことをしている可能性があります)、データをパイプするエンコード スレッドを用意します。ストールを避けるために、書き込みスレッドに十分なバッファリングを備えた書き込みスレッドに転送します。私は以前、この種のスキームを実装するために共有メモリのリング バッファを使用しましたが、バッファがいっぱいでない限り、ライターが停止することなく I/O プロセスに書き込むことができる IPC メカニズムであれば、どのような機能でも機能するはずです。

sync または fsync に代わる便利な Linux 関数は、sync_file_range です。これにより、カーネル内のバッファ システムがデータを取得するのを待たずに、データの書き込みをスケジュールできます。

長い一時停止を避けるために、IO キューを確認してください (例:/sys/block/hda/queue/nr_requests) は十分な大きさです。このキューは、データがメモリからフラッシュされてからディスクに到着するまでの間に入る場所です。

sync_file_range は移植性がなく、カーネル 2.6.17 以降でのみ使用できることに注意してください。

ホストがコマンドを送信した後、MMC と SD カードは「0 ~ 8 バイト以内に応答する必要がある」と言われています。

ただし、仕様により、これらのカードは操作が完了するまで「ビジー」と応答することができ、カードがビジーであると主張できる期間に制限はないようです(そのような制限がある場合は教えてください)。

M25P80 などの一部の低コスト フラッシュ チップでは、「単一セクターの最大消去時間」が 3 秒保証されていますが、通常は「わずか」0.6 秒しかかかりません。

この 0.6 秒は、「おそらく 0.5 秒間の失速」と疑わしいほど似ています。

私は、安価で遅いフラッシュ チップと高価で高速なフラッシュ チップの間のトレードオフが、USB フラッシュ ドライブの結果の大きなばらつきと関係があるのではないかと考えています。

フラッシュ セクタを消去して再プログラムするたびに、前回よりも少し時間がかかるという噂を聞いたことがあります。

したがって、タイムクリティカルなアプリケーションを使用している場合は、(a) SD カードと USB スティックをテストして、最小遅延や帯域幅などを満たしていることを確認する必要がある場合があります。(b) これらのメモリ デバイスを定期的に再テストするか、事前に交換します。

まず明らかですが、ファイルをフラッシュするように明示的に指示してみましたか?また、それを行うために使用できる ioctl がいくつかあるかもしれないとも思いますが、正直なところ、私は C/POSIX ファイルプログラミングをあまりやったことがありません。

Linux カーネルを使用していることを確認すると、カーネルを調整して、ニーズに合ったものに再構築できるはずです。永続ストレージへのフラッシュの頻度ははるかに高くなりますが、フラッシュの規模も小さくなります。


私のマニュアルページを簡単にチェックすると、次のことがわかります。

SYNC(2)                    Linux Programmer’s Manual                   SYNC(2)

NAME
       sync - commit buffer cache to disk

SYNOPSIS
       #include <unistd.h>

       void sync(void);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       sync(): _BSD_SOURCE || _XOPEN_SOURCE >= 500

DESCRIPTION
       sync() first commits inodes to buffers, and then buffers to disk.

ERRORS
       This function is always successful.

独自の flash() を実行することは、私にとっては正しいように思えます。汎用バッファ層の気まぐれに任せるのではなく、制御したいのです。

これは明らかかもしれませんが、write() を頻繁に呼び出しすぎないように注意してください。すべての write() に、システムコールのオーバーヘッドに見合った十分なデータが書き込まれていることを確認してください。また、逆に、あまり頻繁に呼び出さないでください。そうしないと、問題が発生するほど長時間ブロックされます。

再実装がより困難なトラックでは、非同期 I/O に切り替えてみたことはありますか?aio を使用すると、書き込みを開始して 1 つのバッファ セットを渡しながら、ビデオ データをもう 1 セットに取り込み、書き込みが終了したらバッファ セットを切り替えることができます。

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