0

Unices システムで非同期 I/O アラートに使用できる機能 (Linux の epoll、BSD システムの kqueue、Solaris の /dev/poll または I/O ポートなど) はすべて、ユーザーがポインタを指定して、ユーザーが I/O アラートを受け取りたいファイル記述子。

通常、このポインターでは、ユーザーはファイル記述子を抽象化する構造 (「ストリーム」構造など) へのポインターを指定し、ユーザーは新しいファイル記述子が開かれるたびに新しい構造を割り当てます。

例えばstruct stream { int fd; int flags; callback_t on_read_fn; /* ... */ };

さて、私の質問は次のとおりです。ユーザーがマルチスレッド環境で割り当てたこの構造を安全に解放するにはどうすればよいですか?

epoll/kqueue/etc の性質上、これについて質問します。通常、カーネルからイベントのベクトルを「ダウンロード」するスレッドがあり、I/O の準備ができているファイル記述子と、それに関連付けられたユーザー ポインターが含まれています。ファイル記述子。

ここで、2 つのスレッドがあるとします。これらのイベントをダウンロードして処理 (呼び出しstream->on_read_fn();など) する T1 と、ユーザー コード、ユーザー イベントなどを単純に実行する T2 です。

T2 がファイル記述子を閉じたい場合は、単純に閉じてclose(stream->fd);、T1 はその fd の I/O アラートを受信しなくなるため、streamそこで構造体の割り当てを解除しても安全です。

しかし、T1 スレッドが、現在処理しているイベントのベクトルに同じファイル記述子を既にダウンロードしており、そのファイル記述子をまだ処理していない場合はどうでしょうか?

T1 が T2 の前にスケジュールされている場合は問題ありませんが、T2 が T1 の前にスケジュールされている場合は、ファイル記述子を閉じてstream構造体の割り当てを解除するため、スレッド T1 は、そのファイル記述子を処理するときに、ユーザーに関連付けられたポインターを持ちます。 、既に割り当て解除された構造を指しています! もちろん、これはひどくクラッシュします。

要点は、スレッド T1 がその特定のファイル記述子の I/O アラートをダウンロードしたかどうかを T2 が知ることはなく、T2 も T1 が I/O アラートをダウンロードするかどうかを予測できないことです。

これは非常にトリッキーで、頭がくらくらします。何かご意見は?このシナリオでユーザー指定のポインターを安全に解放できるのはいつですか?

注: 私の友人は、ファイル記述子を呼び出す前に、epoll/kqueue キューからファイル記述子を削除することを提案しclose(2)ました。これは正しく、これは私が今行っていることですが、T2 は epoll/kqueue キューからファイル記述子を削除できるため、問題は解決しませんが、そのファイルの I/O イベントは保証されません。記述子はまだカーネルから「ダウンロード」されておらず、すぐにスレッド T1 によって処理されます。

4

3 に答える 3

1

私はまったく同様の問題を抱えていました。そのため、新しいLinuxカーネルの提案で、誰か(名前を思い出せません)がFDにDISABLEDステータスを実装することを提案したため、別のスレッドによって割り当てが解除された場合に処理をスキップできます。

個人的には、マルチスレッドの epool 呼び出しから、FD で epool() を実行する単一のスレッドに移行し、イベントを複数のスレッドにスケジュールしました。内部のオブジェクト自体は参照カウントされ、後でガベージ コレクターによって収集されます。マルチスレッドのepoolソリューションに対して目立った劣化なしに、正直に非常にうまく機能します...

*編集済み*

また、FD を閉じる必要がある限り、ミューテックスによって保護され、消費者スレッドによって埋められる std::set を作成することによって、epool を処理するよりも同じスレッドから FD を閉じる別の方法を調査しました。これもかなりうまくいきました。

于 2015-02-27T12:57:42.480 に答える
1

構造体を解放せず、代わりに「デッド」としてマークし、後で再利用できるようにリストに追加することで、プログラムでこの問題を解決しました。そうすれば、ポインタは再利用された可能性がありますが、常に有効なままです。

于 2020-05-23T02:11:51.040 に答える