0

Named pipe(たとえば、同じ共有メモリを使用している 2 つの独立した無関係なプロセス)を実装しようとしている間、私は使用pthread_atforkしたい と を読み続けますatexit

私はミューテックスとセマフォの使用に完全に同意します。これらを使用して、いつ読み取り/書き込みを行うか、process Aいつ読み取り/書き込みを行うかを決定できprocess Bます。

pthread_atforkしかし、どのような理由でスレッドを使用したいのでしょうか?

編集:

セマフォを使用しないと非常にコストがかかる例:

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <assert.h>

 // Simple busy-wait loop to throw off our timing.
 void busywait(void)
 {
     clock_t t1 = times(NULL);
     while (times(NULL) - t1 < 2);
 }


 int main(int argc, char *argv[])
 {
     const char *message = "Hello World\n";
     int n = strlen(message) / 2;

     pid_t pid = fork();
     int i0 = (pid == 0) ? 0 : n;
     int i;

     for (i = 0; i < n; i++) {
         write(1, message + i0 + i, 1);
         busywait();
     }
 }
4

2 に答える 2

1

名前付きパイプを実装するためにスレッドを使用する必要があるとは誰も言っていません。ただし、ライブラリ コードはスレッド化されたプロジェクトで使用できるため、多くの特殊なケースを処理する必要があります。ご存じのとおり、共有メモリ セグメントなどの SysV IPC オブジェクト0は、破棄のマークが付けられていない限り、使用回数が に減少しても自動的には削除されません。これは、プログラムがコードを使用してパイプを作成し、何らかの理由でクラッシュした場合、パイプ実装の IPC オブジェクトが残り、IPC 名前空間を汚染し、貴重なシステム リソースを消費する可能性が高いことを意味します。

あなたが言及した2つの関数は、pthread_atfork()特定atexit()のことが起こるたびに実行されるコールバックを登録するために使用されます。atexit()プロセスが通常の方法で終了するたびに実行されるコードを登録します (たとえば、 を呼び出すexit(3)か、から戻ることによりmain())。これにより、パイプが明示的に閉じられていないケースを見つけて、必要なクリーンアップを行うことができます。

パイプを閉じずにプロセスを終了するだけでなく、プログラム自体が fork することもあります。これは、それに応じて処理する必要がある特殊なケースでもあります。pthread_atfork()フォークを実行するときにさまざまな時点で呼び出される 3 つのコールバックを登録することになっています。

また、適切なクリーンアップが実行される前にプログラムが終了する可能性がある、キャッチされない可能性のある特定の OS シグナルを処理する必要があります。

ご覧のとおり、ライブラリを作成することは、プログラムを作成することよりもはるかに複雑です。プログラムを作成しているときは、(ほとんど) すべてのユース ケースを制御します。ライブラリを作成するとき、それはさまざまなシナリオで使用される可能性があり、それらすべてを考慮して、それらすべてに備える必要があります。正しい使い方と間違った使い方を考える必要があります。クリーンアップや、ライブラリが正しく使用されていない場合に残る可能性のあるシステム リソースについて考える必要があります。などなど…。

于 2012-07-19T10:56:34.980 に答える
0

大まかに言うと、パイプに似た構造体は共有メモリ (たとえば、 によって取得shm_open) に配置され、次のメンバーが含まれている必要があります。

  • 構造体へのすべてのアクセスを制御する 1 つのミューテックス。
  • 待機中のピアに信号を送るための 1 つの条件変数。
  • 固定サイズのバッファ。
  • 現在の読み取りおよび書き込み位置のバッファーへの 2 つのオフセット。

読み取り関数は、基本的にミューテックスを取得してから、読み取り可能なデータがあるかどうかを確認し、そうでない場合は条件変数を待って、データの再確認をループします。データが見つかったら、待機中の可能性のあるライターをウェイクアップするために、読み取りによってバッファーにさらにデータを収めることが可能になった場合は、条件変数を通知する必要があります。読み取ったデータを呼び出し元が提供するバッファーにコピーしてから、ミューテックスのロックを解除します。

書き込み関数は基本的にミューテックスを取得し、バッファに書き込み用のスペースが残っているかどうかを確認する必要があります。そうでない場合は、条件変数を待機し、書き込みに使用できるスペースをループ再チェックする必要があります。スペースが見つかると、データをバッファーにコピーし、データを待機している可能性のあるリーダーをウェイクアップするように条件変数に通知し、ミューテックスのロックを解除する必要があります。

ミューテックスと条件変数の両方をプロセス共有属性で作成する必要があります。これは、プロセス間の通信に「パイプ」を使用することになります (同じプロセス内のスレッドだけではありません)。また、複数のリーダー/ライターをサポートするかどうか、および条件変数に単一シグナルまたはブロードキャスト シグナルのセマンティクスが必要かどうかについても検討する必要があります。動作を最適化する方法はたくさんありますが、上記の概要は一般的な出発点となるはずです。

于 2012-07-18T13:42:48.790 に答える