13

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

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

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

私が書き込んでいるデバイスは、合理的な究極のスループットを持っていると思います.15MBのファイルをメモリfsからコピーし、同期が完了するのを待つ(そしてUSBスティックのライトが点滅を止める)と、約2.7MBytes /秒の書き込み速度が得られました。

私は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 の質問は? :)

4

10 に答える 10

10

記録として、最も極端なケースを除いて、すべての問題を解決したと思われる 2 つの主な側面があることが判明しました。このシステムはまだ開発中であり、完全な拷問テストはまだ行われていませんが、かなりうまく機能しています (木に触れてください)。

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

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

この問題に対処するために他に何かする必要がある場合は、この回答をポップバックして更新することを忘れないようにします。他の回答者に感謝します。

于 2008-10-18T20:47:20.753 に答える
5

私はいくつかの提案を投げます、アドバイスは安いです。

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

いくつか考えてみてください。これがお役に立てば幸いです。

于 2008-09-16T23:18:06.057 に答える
3

書き込み負荷の高い操作用にpdflushを調整する方法について説明します。

于 2008-09-17T13:51:29.123 に答える
2

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

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

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

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

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

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

于 2008-09-16T23:17:40.333 に答える
1

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

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

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

于 2008-09-18T14:30:12.987 に答える
1

デバッグ支援機能があり、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)

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

于 2008-09-17T15:00:22.143 に答える
1

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

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

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

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

安価で低速なフラッシュ チップと高価で高速なフラッシュ チップとのトレードオフは、USB フラッシュ ドライブの結果が大きく異なることに関係していると思われます。

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

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

于 2010-11-18T04:04:22.987 に答える
1

あなたの特定の状況について詳しく知ることができないので、次の推測しかできません。

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

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

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

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

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

于 2008-09-16T23:10:32.393 に答える
0

独自の flush()ing を実行するのは、私には正しいように思えます。一般的なバッファー レイヤーの気まぐれに任せるのではなく、コントロールしたいのです。

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

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

于 2008-09-17T16:59:57.800 に答える
0

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

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


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

SYNC(2) Linux プログラマーズ マニュアル SYNC(2)

名前
       sync - バッファ キャッシュをディスクにコミットする

あらすじ
       #include <unistd.h>

       ボイド同期(ボイド);

   glibc の機能テスト マクロ要件 (feature_test_macros(7) を参照):

       sync(): _BSD_SOURCE || _XOPEN_SOURCE >= 500

説明
       sync() は、最初に inode をバッファにコミットし、次にバッファをディスクにコミットします。

エラー
       この関数は常に成功します。
于 2008-09-16T23:03:57.947 に答える