オペレーティング システム 3: プロセスの同期と古典的なプロセス同期の問題

目次

1. プロセスの同期

1. プロセス同期の基本概念

(1) 2種類の制約

(2) 重要なリソース

(3) クリティカルセクション

(4) 同期機構が守るべきルール

2. ハードウェア同期機構

(1) シャットダウン割り込み

(2) Test-and-Set コマンドを使用して相互排他を実現します

(3) Swap命令を使用してプロセス相互排他を実現

3. セマフォ機構

(1) 整数セマフォ

(2) レコード型セマフォ

(3) AND型セマフォ

(4)信号量集

4. セマフォの応用

(1) セマフォを利用してプロセス相互排他を実現する

(2) セマフォを利用して先行関係を実現する

5. モニター機構

(1) モニターとは何ですか?

(2) 条件変数

2. 古典的なプロセス同期の問題

1. 生産者と消費者の問題

(1) レコード型セマフォを使用してプロデューサー・コンシューマー問題を解決する

(2) AND セマフォを使用して生産者と消費者の問題を解決する

(3) モニターを活用して生産者と消費者の問題を解決する

2. 食事の哲学者の問題

(1) 記録セマフォを使用して哲学者の食事の問題を解決する

(2) AND セマフォ機構を使用して哲学者の食事の問題を解決する

3. リーダーライター問題

(1) レコードセマフォを使用してリーダーライター問題を解決する

(2) セマフォセット機構を利用してリーダライタ問題を解決する


1. プロセスの同期

        複数のプロセスを確実に規則正しい方法で実行できるようにするには、マルチプログラミング システムにプロセス同期メカニズムを導入する必要があります。ユニプロセッサ システムのプロセス同期メカニズムには、プログラム実行の再現性を保証できるハードウェア同期メカニズム、セマフォ メカニズム、モニター メカニズムなどが含まれます。// プログラムの実行結果の決定性を確保する

1. プロセス同期の基本概念

        プロセス同期メカニズムの主なタスクは、同時に実行されるプロセスが特定のルールに従ってシステム リソースを共有し、相互に適切に連携できるように、複数の関連するプロセスの実行順序を調整することです。これにより、プログラムの実行が再現可能になります

(1) 2種類の制約

        マルチプログラミング環境では、同じシステム内の複数のプロセスがシステム内のリソースを共有したり、互いに協力して特定のタスクを完了したりするため、プロセス間に次の 2 つの形式の制約が存在する可能性があります。

        1 - 間接的な相互制約

        複数のプログラムが同時に実行されると、CPU、I/O デバイスなどのシステム リソースの共有により、これらの同時に実行されるプログラム間に相互制限関係が形成されます。プリンタやテープドライブなどの重要なリソースの場合、複数のプロセスがそれらに相互に排他的にのみアクセスできることを保証する必要があり、そのため、そのようなリソースの共有に起因する、これらのプロセス間にいわゆる間接的な相互制限関係が形成されます。これらのプロセスが正常に実行されることを保証するために、システム内のリソースはシステムによって均一に割り当てられる必要があります。つまり、ユーザーは使用する前にリソースを申請する必要があり、ユーザー プロセスがリソースを直接使用することはできません。// スレッド間に協力関係はありません

        2 - 直接的な相互制約

        一部のアプリケーションでは、タスクを完了するために 2 つ以上のプロセスを作成します。これらのプロセスは相互に連携して同じタスクを実行します。プロセス間の直接的な制約関係は、相互の協力から導き出されます。たとえば、入力プロセス A と計算プロセス B という 2 つの連携プロセスがあり、それらの間でバッファを共有しているとします。プロセス A は、バッファリングを通じてプロセス B にデータを提供します。プロセス B はバッファからデータをフェッチし、データを処理します。ただし、バッファが空の場合、必要なデータを取得できないため、計算プロセスがブロックされます。プロセス A がデータをバッファに入力すると、プロセス B が起動されます。逆に、バッファがいっぱいになると、プロセス A はバッファにデータを入れることができなくなるためブロックされ、プロセス B はバッファ データを取り出した後に A を起動できます。// スレッド間で連携する

(2) 重要なリソース

        プリンタ、テープ ドライブなどの多くのハードウェア リソースは重要なリソースであり、プロセスはそのようなリソースの共有を実現するために相互排他的な方法を採用する必要があります。// 共有リソースまたは共有変数

(3) クリティカルセクション

        ハードウェアにクリティカルなリソースであるか、ソフトウェアにクリティカルなリソースであるかに関係なく、複数のプロセスが相互に排他的にアクセスする必要があります。各プロセスの重要なリソースにアクセスするコードのセクションをクリティカル セクションと呼びます。

        明らかに、すべてのプロセスがそれぞれのクリティカル セクションに排他的に入ることが保証できれば、すべてのプロセスによるクリティカル リソースへの相互排他的アクセスが実現できます。このため、各プロセスはクリティカル セクションに入る前に、まずアクセス対象のクリティカル リソースがアクセスされているかどうかをチェックする必要があります。この時点でクリティカル リソースにアクセスしていない場合、プロセスはクリティカル セクションに入ってリソースにアクセスし、アクセス中であることを示すフラグを設定できます。クリティカル リソースが現在プロセスによってアクセスされている場合、プロセスはクリティカル セクションに入ることができません。クリティカルセクション。

        したがって、クリティカルセクションの前に上記のチェックを行うコードを追加する必要があり、このコードをエントリーセクションと呼びます。これに応じて、終了セクションと呼ばれるコードをクリティカル セクションの後に追加する必要があります。これは、クリティカル セクションでアクセスされているフラグをアクセスされていないフラグに戻すために使用されます。上記のエントリーエリア、クリティカルエリア、エグジットエリアを除いた他の部分のコードをここでは残差エリアと呼びます。このように、重要なリソースにアクセスするための循環プロセスは次のように説明できます。

    while(TURE)
    {
        进入区
        临界区
        退出区
        剩余区
    }

(4) 同期機構が守るべきルール

        プロセスがそれぞれの重要な領域に相互に排他的に入ることを実現するには、ソフトウェア手法を使用できます。多くの場合、システムに特別な同期メカニズムが設定され、各プロセスの動作を調整します。すべての同期メカニズムは、次の 4 つのガイドラインに従う必要があります。// ガイドラインと重要な標準

  • 出入り自由です。クリティカル セクションにプロセスがない場合は、クリティカル リソースがアイドル状態であることを示しており、クリティカル セクションへの入室を要求するプロセスは、クリティカル リソースを効果的に利用するために、直ちに自身のクリティカル セクションへの入室を許可される必要があります。
  • 忙しい場合はお待ちください。既存のプロセスがクリティカル エリアに入ると、クリティカル リソースにアクセス中であることを示すため、クリティカル エリアに入ろうとする他のプロセスは、クリティカル リソースへの相互排他的アクセスを確保するために待機する必要があります。
  • 待ち時間は限られています。クリティカルなリソースへのアクセスを必要とするプロセスの場合、「デッドウェイト」状態に陥らないように、限られた時間内にプロセス自体のクリティカル領域に入ることが保証される必要があります。
  • 右は待たせてください。プロセスがそれ自体のクリティカル セクションに入ることができない場合、プロセスが「ビジー待機」状態に陥るのを防ぐために、プロセッサを直ちに解放する必要があります。

// 実際には、アイドル状態のときに許可する、ビジー状態のときに待機する、制限付きで待機するという 3 つの条件があります。

2. ハードウェア同期機構

        多くのコンピュータは、単語の内容を確認および修正したり、2 つの単語の内容を交換したりできる特別なハードウェア命令を提供しています。これらの特別な命令は、クリティカル セクションの問題を解決するために使用できます。// ハードウェア命令を使用する

        実際には、クリティカルセクションを管理する場合、フラグをロックとみなすことができ、「ロックオープン」が入り、「ロックオフ」が待機し、最初にロックが開かれます。クリティカル セクションに入ろうとする各プロセスは、最初にロックをテストする必要があり、ロックが開かれていない場合は、ロックが開かれるまで待機する必要があります。逆に、ロックが開いている場合は、他のプロセスがクリティカル セクションに入ることを防ぐために、すぐにロックする必要があります。明らかに、複数のプロセスがロックが開いているかどうかを同時にテストすることを防ぐために、テストとクローズの操作は連続的に行う必要があり、個別の操作は許可されません

(1) シャットダウン割り込み

        割り込みを無効にすることは、相互排他を実装する最も簡単な方法の 1 つです。ロックテストに入る前に割り込みをオフにし、ロックテストが完了してロックがかかるまで割り込みをオンにすることはできませんこのようにして、クリティカル セクションでのプロセスの実行中、コンピュータ システムは割り込みに応答しないため、スケジューリングはトリガーされず、プロセスやスレッドの切り替えは発生しません。したがって、ロックのテストとロック操作の継続性と完全性が保証され、相互排除が効果的に保証されます。ただし、割り込みをオフにする方法には多くの欠点があります。// 非常に限定的

  • シャットダウン電源を誤用すると、重大な結果が生じる可能性があります。
  • 割り込み時間が長すぎると、システム効率に影響し、プロセッサがプログラムをクロス方式で実行する能力が制限されます。
  • 1 つのプロセッサで割り込みを無効にしても、プロセスが他のプロセッサで同じクリティカル セクション コードを実行することは妨げられないため、割り込みを無効にする方法はマルチ CPU システムにも適していません。

(2) Test-and-Set コマンドを使用して相互排他を実現します

        相互排他は、ハードウェア命令、「テスト アンド セット」命令 TS (Test-and-Set) によって実現されます。このような命令は多くのコンピュータで提供されています。TS 命令の一般的な説明は次のとおりです。

boolean TS(boolean lock){
    Boolean old;
    old = lock;
    lock = TRUE;
    return old;
}

        この命令は関数処理とみなすことができ、その実行処理は分離不可能、すなわちプリミティブである。このうち、lock には 2 つの状態があり、lock = FALSE の場合はリソースがアイドル状態であることを意味し、lock = TRUE の場合はリソースが使用中であることを意味します。

        TS命令でクリティカルセクションを管理する場合、クリティカルリソースごとにブール型の変数ロックを設定しますが、変数ロックはリソースの状態を表すためロックとみなすことができます。lock の初期値は FALSE で、重要なリソースがアイドル状態であることを示します。プロセスがクリティカル セクションに入る前に、最初に TS 命令を使用してロックをテストします。値が FALSE の場合、クリティカル セクションに入ることができるプロセスがないことを意味し、ロックには TRUE の値が割り当てられます。これは、クリティカル リソースを閉じて、プロセスがクリティカル セクションに入ることができないようにすることと同じです。そうでない場合、TS が TRUE になるまでテストをループする必要があります。// 実際には、ロックの検出と値の設定をプリミティブにするためです

        TS ポインタを使用して反発を実現するループ処理構造は次のように説明できます。

do(
    ...
    while TS(&lock);   // 进入
    critical section;  // 临界区
    lock = FALSE;      // 退出
    remainder section; // 剩余区
while(TRUE);

(3) Swap命令を使用してプロセス相互排他を実現

        この命令はスワップ命令と呼ばれ、Intel 80x86 では XCHG 命令とも呼ばれ、2 つのワードの内容を交換するために使用されます。その処理プロセスは次のように説明されます。

void swap(boolean a, boolean b){
    boolean temp;
    temp = a;
    a = b;
    b = temp;
}

        相互排他は、スワップ命令を使用することで簡単かつ効果的に実現でき、その方法は、クリティカルなリソースごとに初期値が false であるグローバルなブール変数ロックを設定し、各プロセスでローカルのブール変数キーを使用することです。Swap 命令を使用してプロセスの相互排他を実現する循環プロセスは次のように記述できます。 //交換プロセスはアトミックであるため、キーの値が異なるプロセスで同時に異なる値を持つことはありません。

do {
    key = TRUE;
    do {
        swap(&lock, &key);
    } while (key != FALSE);
    临界区操作;
    lock = FALSE;
    ...
} while(TRUE);

        上記のハードウェア命令を使用すると、プロセスの相互排他を効果的に実現できますが、重要なリソースがビジー状態の場合、アクセスしている他のプロセスは「ビジー待機」状態でテストを続行する必要があり、「待機する権限を与える」という原則に準拠しません。プロセッサーの原因となるのは時間の無駄であり、複雑なプロセス同期の問題を解決するためにそれらを使用することは困難です。// ビジー待機を避けることはできない

3. セマフォ機構

        セマフォ メカニズムは、ユニプロセッサおよびマルチプロセッサ システムおよびコンピュータ ネットワークで広く使用されています。

(1) 整数セマフォ

        整数セマフォは、リソースの数を表すために使用される整数 S です。一般的な整数とは異なります。初期化に加えて、2 つの標準的なアトミック操作 (Atomic Operation) wait(S) と signal(S) のみを渡すことができます。にアクセスします(つまり、P、V 操作)。wait および signal 操作は次のように記述できます。// wait および wake up

wait(S){
    while(S<=0); // 循环等待
    S--;
}
signal(S){
    S++;
}

        wait(S) と signal(S) は、実行が中断不可能な 2 つのアトミック操作です

(2) レコード型セマフォ

        整数セマフォ メカニズムの待機操作では、セマフォ S <= 0 である限り、テストが続行されます。したがって、このメカニズムは「正しく待つ」という原則に従いませんが、プロセスを「ビジー待機」状態に保ちます。

        レコード型セマフォ機構は、「ビジー待ち」現象のないプロセス同期機構です。ただし、「待機する権利を放棄する」戦略を採用した後は、複数のプロセスが同じ重要なリソースへのアクセスを待機する状況が発生します。このため、セマフォ機構では、リソースの数を表すための整変数値に加えて、上記のすべての待機プロセスをリンクするためのプロセスリンクリストポインタリストを追加する必要があります。レコード型セマフォは、レコード型のデータ構造を使用するため、この名前が付けられています。// セマフォ + ブロッキングキュー

        これに含まれる上記 2 つのデータ項目は次のように説明できます。

typedef struct {
    int value;                             // 信号量
    struct process_control_block list;     // 阻塞队列
} semaphore;

        同様に、wait(S) 操作と signal(S) 操作は次のように記述できます。

wait(semaphore S){
    S->value--;
    if(S->value < 0) block(S->list); // 进入阻塞队列
}
signal(semaphore S){
    S->value++;
    if(S->value<=0) wakeup(S->list); // 从阻塞队列中唤醒
}

        レコード型セマフォメカニズムでは、S -> value の初期値がシステム内の特定の種類のリソースの番号を示すため、リソース セマフォとも呼ばれます。その各待機操作は、プロセスがユニットを要求することを意味します。このタイプの resource は、システム内で割り当て可能なリソースの数を 1 つ減らすため、 S -> vaue--; // セマフォを差し引きます。

        S.value <0 の場合、このタイプのリソースが割り当てられていることを意味するため、プロセスはブロック プリミティブを呼び出して自身をブロックし、プロセッサを放棄し、セマフォ リスト S -> リストに挿入する必要があります。このメカニズムは「電源を放棄して待つ」という原則に従っていることがわかります。このとき、S -> value の絶対値がセマフォ連結リスト内のブロックされたプロセスの数を表します。// リソースが不足しているため、プロセスが待機中です

        セマフォ上の各シグナル操作は、実行プロセスが単位リソースを解放することを意味します。これにより、システム内で割り当て可能なリソースの数が 1 つ増加します。そのため、S -> value++ 操作は、リソースの数が 1 つ増加することを意味します。1 を加算した後も S -> vaue <= 0 である場合は、セマフォのリンクされたリストでブロックされたリソースを待っているプロセスがまだあることを意味するため、ウェイクアップ プリミティブも呼び出す必要があり、S -> の最初の項目も呼び出す必要があります。 > list linked list 待機中のプロセスが起動します。// プロセスを起動します

        S -> value の初期値が 1 の場合は、クリティカルリソースへのアクセスを 1 つのプロセスのみに許可することを意味し、このときのセマフォはプロセスを相互排他する相互排他セマフォに変換されます。

(3) AND型セマフォ

        AND 同期メカニズムの基本的な考え方は、プロセスの実行中にプロセスが必要とするすべてのリソースを一度にプロセスに割り当て、プロセスが使い果たされた後にそれらをまとめて解放することです。プロセスに割り当てられないリソースがまだ存在する限り、そのプロセスに割り当てられる他のリソースもすべて割り当てられません。つまり、いくつかの重要なリソースの割り当てにはアトミック操作が採用されます。アトミック操作によって要求されたすべてのリソースがプロセスに割り当てられるか、まったく割り当てられないかのどちらかです待機動作には「AND」条件を追加する必要があるため、AND 同期と呼ばれます。// デッドロックを避けるために複数のセマフォを同時に割り当てます

(4)信号量集

        以前のレコードタイプのセマフォ メカニズムでは、wait(S) または signal(S) 操作はセマフォに 1 を加算または 1 を減算することしかできませんでした。つまり、特定の種類のクリティカル リソースに対して一度に適用できるユニットは 1 つだけです。またはリリースします。一度に N 個のユニットが必要な場合、N 回の wait(S) 操作が必要になりますが、これは明らかに非効率であり、デッドロックの可能性さえ増加する可能性があります。また、システムのセキュリティを確保するために、適用されるリソースの量が一定の下限値を下回る場合には、リソースを割り当てずに制御する必要がある場合もあります。したがって、プロセスが特定の種類のクリティカル リソースを適用する場合、各割り当ての前にリソースの量をテストして、割り当ての下限を超えているかどうかを確認し、割り当てるかどうかを決定する必要があります。//(1) 複数のリソースを同時に申請または解放する (2) 割り当てる前にリソースの数を確認する

        上記の 2 つの点に基づいて、AND セマフォ メカニズムを拡張して、プロセスによって適用されるすべてのリソースおよび各タイプのリソースのさまざまなリソース要件に対する 1 つの P および V プリミティブ操作でアプリケーションまたはリリースを完了することができますセマフォ上のプロセスのテスト値 S(i) は 1 ではなくなり、リソース割り当ての下限値 T(i) になります。つまり、S(i) >= T(i) である必要があります。それ以外の場合は割り当てられません。割り当てが許可されると、リソースに対するプロセスの要求値は D(i) になります。これはリソース占有を意味し、単純に S(i) の代わりに演算 S(i) = S(i) - D(i) が実行されます。 ) = S (i)-1。したがって、一般化された「セマフォ セット」メカニズムが形成されます。

        対応する S_wait および S_signal 形式は次のとおりです。// これは主に背後にある疑似コードを確認するためのものです。

S_wait(S1, T1, D1, ..., Sn, Tn, Dn);
S_signal(S1, D1, ..., Sn, Dn);

        一般的な「セマフォ セット」には、次の特殊なケースもあります。

  • S_wait(S,T,D): 現時点では、セマフォ セットにはセマフォ S が 1 つだけありますが、毎回 D リソースを申請できます。既存のリソース数が T 未満の場合は、申請できません。割り当てられます。(T >= D)
  • S_wait(S,1,1): このとき設定されたセマフォは、一般記録セマフォ(S>1の場合)または相互排他セマフォ(S=1の場合)に縮退しています。
  • S_wait(S,1,0): これは非常に特殊で便利なセマフォ操作です。S > 1 の場合、複数のプロセスが特定の領域に入ることが許可され、S が 0 になると、どのプロセスも特定の領域に入ることができなくなります。つまり、制御可能なスイッチに相当します。// 読み書きロックの実装

4. セマフォの応用

(1) セマフォを利用してプロセス相互排他を実現する

        複数のプロセスがクリティカル リソースに相互に排他的にアクセスできるようにするには、リソースに相互排他セマフォミューテックスを設定し、その初期値を 1 に設定してから、各プロセスがリソースにアクセスするようにクリティカル セクション CS を設定するだけです。待機(ミューテックスおよびシグナル(ミューテックス)操作のためのリソース。

        実装原則:クリティカルなリソースにアクセスする各プロセスは、クリティカル セクションに入る前にミューテックス上で待機操作を実行する必要があります。その時点でリソースにアクセスしていない場合、今度は待機操作が成功する必要があり、プロセスはその状態に入ることができます。この時点で、他のプロセスが独自のクリティカル セクションに入りたい場合、ミューテックスでの待機操作は失敗するため、プロセスはこの時点でブロックされ、クリティカル リソースに排他的にアクセスできることが保証されます。クリティカル リソースにアクセスするプロセスがクリティカル セクションを終了すると、クリティカル リソースを解放するためにミューテックス上でシグナル操作を実行する必要があります。// 重要なリソースの取得ロジック

        セマフォを使用して 2 つのプロセス間の相互排他を実現する方法の説明は次のとおりです。

        1 - ミューテックスを相互排他セマフォとして設定します。その初期値は 1 で、値の範囲は (-1, 0, 1) です。mutex=1 の場合、2 つのプロセスが相互排他を必要とするクリティカル エリアに入っていないことを意味します。mutex=0 の場合、一方のプロセスがクリティカル エリアに入って実行され、もう一方のプロセスは待機してブロッキング キューにハングする必要があることを意味します。 ; mutex=-1 の場合、プロセスがクリティカル セクションで実行中で、別のプロセスが待機によりセマフォ キューでブロックされており、クリティカル セクションで現在実行中のプロセスが終了するときに起動する必要があることを示します。

        2 -コードの説明:

semaphore mutex=1;
// 进程1
PA(){
    while(1){
        wait(mutex);
        临界区;
        signal(mutex);
        剩余区;
    }
}
// 进程2
PB(){
    while(1){
        wait(mutex);
        临界区;
        signal(mutex);
        剩余区;
    }
}

セマフォ メカニズムを使用してプロセス相互排他を実装する場合、 wait(mutex) と signal(mutex) はペアで指定する必要がある        ことに注意してくださいwait(mutex) が不足するとシステムの混乱が発生し、重要なリソースへの相互排他的アクセスが保証されなくなります。また、signal(mutex) が不足すると重要なリソースが解放されなくなり、リソースの待機によってブロックされたプロセスが実行できなくなります。目が覚める。

(2) セマフォを利用して先行関係を実現する

セマフォは、プログラムまたはステートメント間の先行関係を記述する        ために使用することもできます

        2 つのプロセス P1 と P2 が同時に実行されているとします。P1 にはステートメント S1 があり、P2 にはステートメント S2 があります。S1の後にS2を実行したいと考えています。この先行関係を実現するには、プロセス P1 と P2 に共通のセマフォ S を共有させ、それに初期値 0 を与え、signal(S) 操作をステートメント S1 の後ろに置き、wait を挿入するだけで済みます。 ( S) 操作、つまり

在进程 P1 中,用:
    S1; signal(S);  // S1 之后唤醒

在进程 P2 中,用: 
    wait(S); S2;    // S2 之前等待

        S は 0 に初期化されるため、P2 が最初に実行される場合は、ブロックする必要があります。プロセス P1 が S1; signal(S); の実行を終了し、S を 1 に増加した後でのみ、P2 プロセスはステートメント S2 を正常に実行できます。// S1 -> S2 の実行順序を制御

5. モニター機構

        セマフォ メカニズムは便利で効果的なプロセス同期メカニズムですが、重要なリソースにアクセスしたい各プロセスには、独自の同期操作 (waitS) と signal(S) が必要です。これにより、多数の同期操作がプロセス全体に分散されます。これは、システムの管理に問題を引き起こすだけでなく、同期操作の不適切な使用によるシステムのデッドロックの原因にもなります。このようにして、上記の問題を解決する過程で、新しいプロセス同期ツールであるモニターが作成されます// 多数のセマフォが分散していて制御が難しい問題を解決する

(1) モニターとは何ですか?

        システム内のあらゆる種類のハードウェア リソースとソフトウェア リソースは、データ構造を使用してリソースの特性を抽象的に記述することができます。つまり、内部構造や実装の詳細を無視して、リソースを少量の情報とリソース上で実行される操作で表現します。// リソースは抽象化できる

        したがって、共有データ構造を使用してシステム内の共有リソースを抽象的に表すことができ、共有データ構造上で実装される特定の操作を一連のプロシージャとして定義できます共有リソースに対するプロセスのアプリケーション、リリース、およびその他の操作は、このプロセスのグループを通過し、共有データ構造に対する操作を間接的に実装する必要があります。共有リソースへのアクセスを要求する多くの同時プロセスについては、リソースの状態に応じて受け入れまたはブロックすることができ、一度に 1 つのプロセスだけがモニターに入り、このプロセス グループを実行し、共有リソースを使用して、リソースの統合管理を実現します。共有リソースへのすべてのアクセスを効果的に行い、プロセスの相互排他を実現します。// 同じ方法でリソースを管理するプロセスを定義します

共有リソース        を表すデータ構造と、共有データ構造を操作するための一連の手順で構成されるリソース管理プログラムは、オペレーティング システムのリソース管理モジュールを構成し、これをモニターと呼びます。モニターは、リソースを要求および解放するプロセスによって呼び出されます。// モニターはリソース管理プログラムです: データ構造 + 操作プロセス

        上記の定義からわかるように、モニターは 4 つの部分で構成されます。

  • モニターの名前
  • モニターの共有データ構造
  • データ構造を操作する一連のプロシージャ
  • モニターの共有データの初期値を設定するステートメント

        次の図はチューブの概略図です。

        モニターの構文は次のように説明されます。

Monitor monitor_name{                 /*管程名*/
    share variable declarations;      /*共享变量说明*/
    cond declarations;                /*条件变量说明*/
    public:                           /*能被进程调用的过程*/
        void P1(......)               /*对数据结构操作的过程*/
        {......}
        void P2(......)               
        {......}
        ...... 
        void PN(......)              
        {......}
        ...... 
    {                                 /*管程主体*/
        initialization code;          /*初始化代码*/
        ......
    }
}

        モニターには、共有リソースのデータ構造と、同期メカニズムを含むデータ構造を操作するための一連のプロセスを表すオブジェクト指向の考え方が含まれており、これらはすべてオブジェクトに集中してカプセル化され、オブジェクトを隠します。実装の詳細。モニター内にカプセル化されたデータ構造には、モニター内にカプセル化されたプロセスのみがアクセスでき、モニター外部のプロセスはアクセスできません。逆に、モニター内にカプセル化されたプロセスは、モニター構造内のデータにのみアクセスできます。すべてのプロセスが重要なリソースにアクセスしたい場合、モニターを介して間接的にアクセスすることしかできず、モニターは毎回 1 つのプロセスのみがモニターに入り、モニター内でプロセスを実行することを許可することで、プロセスの相互排他を実現します。// モニターの動作原理、モニターはリソース同期操作をさらに抽象化したものです

        モニターには主に次の特徴があります: // モニターはパッケージ化されたプログラムです

  • モジュール性。つまり、モニターは個別にコンパイルできる基本的なプログラム単位です。
  • 抽象データ型とは、モニター内にデータがあるだけでなく、データに対する操作も存在することを意味します。
  • 情報マスキングとは、モニター内のデータ構造にモニター内のプロセスのみがアクセスできることを意味します。これらのプロセスもモニター内で定義されます。モニター外部のプロセスによって呼び出された場合、モニター内のデータ構造とその特定の詳細は、プロセス 実装は外部からは見えません

        モニターとプロセスの違い

  • どちらもデータ構造を定義しますが、プロセスはプライベート データ構造 PCB を定義し、モニターはメッセージ キューなどのパブリック データ構造を定義します。
  • どちらもそれぞれのデータ構造に対する操作を持っていますが、プロセスは逐次プログラムによって実行され、モニターは主に同期操作と初期化操作を実行します。
  • プロセスの設定の目的はシステムの同時実行性を実現することであり、モニターの設定は共有リソースの相互排他的使用の問題を解決することです。
  • プロセスは、通常のサブルーチンのように呼び出されるモニター内のプロシージャを呼び出すことによって共有データ構造上で動作します。そのため、モニターは受動的に動作し、プロセスは能動的に動作します。
  • プロセスは同時に実行できますが、モニターは呼び出し元と同時に実行できません。
  • プロセスは動的であり、「作成」によって生成され、「元に戻す」によって消滅します。モニターは、プロセスが呼び出すためのオペレーティング システム内のリソース管理モジュールです

// 両者にはまだ大きな違いがあります。どちらもプログラムであり、独自のデータ構造を定義していますが、本質的には同じものではありません

// オペレーティング システムは、ハードウェア命令を操作し、リソース管理を実現するために使用されるプログラムのセットです。たとえば、Linux は C で書かれています

(2) 条件変数

        モニターを使用してプロセス同期を実現する場合、2 つの同期操作プリミティブ wait および signal などの同期ツールを設定する必要があります。プロセスがモニターを通じて重要なリソースを要求したが、それが満たされなかった場合、モニターは待機プリミティブを呼び出してプロセスを待機させ、待機キューに入れます。別のプロセスがアクセスを終了してリソースを解放した後でのみ、モニターはシグナル プリミティブを再度呼び出して、待機キュー内のリーダー プロセスを起動します。

        ただし、上記の同期ツールがあるだけでは十分ではなく、プロセスがモニターを呼び出すと、ブロックまたは一時停止の原因が除去されるまでモニター内でブロックまたは一時停止される状況を考えてみましょう。プロセス モニターが解放されていない場合、他のプロセスはモニターに入ることができず、長時間待機することになります。この問題を解決するために、条件変数condition が導入されます。通常、プロセスがブロックまたは一時停止される条件は複数あるため、モニターには複数の条件変数が設定され、これらの条件変数へのアクセスはモニター内でのみ実行できます。// 条件変数を使用する理由、モニターを割り当てる条件です

        モニター内の各条件変数は次の形式で説明する必要があります: 条件x, y; 条件変数に対する操作は待機とシグナルのみであるため、条件変数も抽象データ型であり、各条件変数はリンクされたリストを保存します。は条件変数によってブロックされたすべてのプロセスを記録するために使用され、同時に提供される 2 つの操作は x.wait と x.signal として表現でき、その意味は次のとおりです。

  • x.wait: モニターを呼び出すプロセスは、x 条件によりブロックまたは一時停止する必要があります。その後、x.wait を呼び出して自分自身を x 条件の待機キューに挿入し、x 条件が変わるまでモニターを解放します。この時点で、他のプロセスがモニターを使用できるようになります。
  • x.signal: モニターを呼び出しているプロセスは、条件 x が変更されたことを検出し、x.signal を呼び出して、条件 x によりブロックまたは一時停止されているプロセスを再起動します。そのようなプロセスが複数ある場合は、そのうちの 1 つを選択します。そうでない場合は、結果を生み出さずに元のプロセスを続行します。これは、セマフォ機構における信号の動作とは異なります。後者は常に s=s+1 演算を実行するため、セマフォの状態は常に変更されます。

// 他のプロセスによるモニターの使用をブロックしないように、条件ごとに異なるブロッキング キューを設定します。

2. 古典的なプロセス同期の問題

1. 生産者と消費者の問題

        プロデューサーとコンシューマーの問題は、相互に連携するプロセス間の関係を抽象化したものです。たとえば、入力では、入力プロセスがプロデューサーであり、コンピューティング プロセスがコンシューマーであり、出力では、コンピューティング プロセスがプロデューサーであり、印刷プロセスは消費者であるため、この問題には大きな代表性と実用的価値があります。// プロセス連携問題の抽象化

(1) レコード型セマフォを使用してプロデューサー・コンシューマー問題を解決する

        プロデューサーとコンシューマー間のパブリック バッファ プールにn 個のバッファがあると仮定すると、この時点で、相互排他セマフォミューテックスを使用して、バッファ プール上のプロセスの相互排他を実現できます。セマフォはフルが使用されます。バッファプールをそれぞれ表す空のバッファと満杯のバッファの数。また、これらのプロデューサーとコンシューマーは互いに同等であると想定されており、バッファ プールがいっぱいでない限り、プロデューサーはバッファ プールにメッセージを送信でき、バッファ プールが空でない限り、コンシューマはメッセージを受け取ることができます。バッファプールからのメッセージ。

        生産者と消費者の問題は次のように説明できます。

//缓冲区存数据指针和取数据指针
int in=0, out=0
//缓冲区
item buffer[n];
//信号量
semaphore mutex=1,empty=n,full=0;

//生产者
void proceducer(){
    do {
        producer an item nextp; //生产元素
        ...
        wait(empty); //获取一个缓冲块: empty-1
        wait(mutex); //获取信号量: mutex-1
        buffer[in] = nextp;
        in = (in+1) % n; // 调整指针
        signal(mutex); //释放信号量: mutex+1
        signal(full);  //通知消费+1: full+1
    }while(TRUE);
}

//消费者
void consumer() {
    do {
        wait(full); //等待full信号量: full-1
        wait(mutex); //获取信号量: mutex-1
        nextc = buffer[out];
        out =(out+1)% n;
        signal(mutex);
        signal(empty);
        consumer the item in nextc; //消费数据
    } while(TRUE);
}

//主函数
void main(){
    cobegin
        proceducer();consumer();
    coend
}

        プロデューサーとコンシューマーの問題では、まず、相互排他を実現するために使用される wait(mutex) と signal(mutex) が各プログラム内でペアで出現する必要があること、次に、リソース セマフォが空および満杯になるまでの待機、および信号操作もペアで指定する必要がありますが、それらは異なるプログラム内にあります。たとえば、wait(empty) は計算プロセスにあり、signal(empty) は印刷プロセスにありますが、wait(empty) の実行によって計算プロセスがブロックされた場合、将来の印刷プロセスによってウェイクアップされます。最後に、各プログラムで複数の待機操作の順序を逆にすることはできませんリソース セマフォに対する待機操作を最初に実行し、次にミューテックス セマフォに対する待機操作を実行する必要があります。そうしないと、プロセスのデッドロックが発生する可能性があります。// セマフォの待機と通知はペアで表示される必要があります

(2) AND セマフォを使用して生産者と消費者の問題を解決する

        プロデューサーとコンシューマーの問題については、AND セマフォを使用したり、wait(empty) と wait(mutex) の代わりに Swait(empty, mutex) を使用したり、signal(mutex) の代わりに Ssignal(mutex, full) を使用したりすることによっても解決できます。 signal(full): wait(full) と wait(mutex) を Swait(full, mutex) に置き換え、Signal(mutex) と Signal(empty) を Ssignal(mutex, empty) に置き換えます。// 分散したセマフォ操作をまとめます

        AND セマフォを使用してプロデューサーとコンシューマーの問題を解決するアルゴリズムにおけるプロデューサーとコンシューマーは、次のように説明できます。

//缓冲区存数据指针和取数据指针
int in=0, out=0
//缓冲区
item buffer[n];
//信号量
semaphore mutex=1,empty=n,full=0;

//生产者
void proceducer(){
    do {
        producer an item nextp; //生产元素
        ...
        Swait(empty,mutex); //获取信号量-1
        buffer[in] = nextp;
        in = (in+1) % n; // 调整指针
        Ssignal(mutex,full); //释放信号量+1
    }while(TRUE);
}

//消费者
void consumer() {
    do {
        Swait(full,mutex); //获取信号量-1
        nextc = buffer[out];
        out =(out+1)% n;
        Ssignal(mutex,empty); //释放信号量+1
        consumer the item in nextc; //消费数据
    } while(TRUE);
}

(3) モニターを活用して生産者と消費者の問題を解決する

        モニター メソッドを使用してプロデューサーとコンシューマーの問題を解決する場合は、最初にモニターを作成し、procducer_consumer (略して PC) という名前を付ける必要があります。これには 2 つのプロセスが含まれます。

  • put(x)プロセス。プロデューサは、このプロセスを使用して自分の製品をバッファ プールに入れ、整数変数 count を使用してバッファ プール内の製品の数を示します。count >= N の場合、バッファ プールがいっぱいで、プロデューサは待機する必要があることを意味します。// データを入れる
  • get(x)プロシージャ。消費者はこのプロセスを使用してバッファ プールから製品を取り出します。count ≤ 0 の場合、バッファ プールに利用可能な製品がないことを意味し、消費者は待つ必要があります。// データを取得する

        条件変数 not_full および not_empty については、それらを操作する 2 つのプロシージャ c_wait および c_signal があります。

  • c_wait(condition)プロセス: モニターがプロセスによって占有されている場合、他のプロセスはこのプロセスを呼び出すときにブロックされ、条件キューでハングします。// 条件キュー内のブロック
  • c_signal(condition)プロセス: c_wait の実行後に条件キューでブロックされているプロセスをウェイクアップします。そのようなプロセスが複数ある場合は、そのうちの 1 つを選択してウェイクアップ操作を実装します。キューが空の場合は、操作せずに戻ります。 。// 条件キューから目覚める

        PC チューブのプロセスは次のように説明できます。

//定义一个管程
Monitor procducer_consumer {
    //缓冲区
    item buffer[N];  
    //缓冲区指针
    int in, out;
    //条件
    condition not_full, not_empty;
    //计数
    int count;
    //控制过程
    public:
        void put(item x) {
            if(count >= N) c_wait(not_full); //如果缓冲区满,需等待,信号-1
            buffer[in] = x; 
            in = (in+1) % N; 
            count++                          //缓冲区存放数据数量+1
            c_signal(not_empty);             //释放缓冲区部位空信号,信号+1
        }

        void get(item x) {
            if (count <= 0) c_wait(not_empty); //如果缓冲区为空,需等待,信号-1
            x = buffer[out];
            out =(out+1) % N;
            count--;
            c_signal(not_full);                //释放信号,信号+1
        }
    //初始化指针的值
    {in=0;out=0;count=0;}
}JPC;

        モニターを使用してプロデューサーとコンシューマーの問題を解決する場合、プロデューサーとコンシューマーは次のように説明できます。

//生产者
void producer() {
    item x;
    while(TRUE) {
        ...
        produce an item in nextp;
        PC.put(x);     //使用管程放数据,放数据过程中buffer资源互斥
    }
}

//消费者
void consumer) {
    item x;
    while(TRUE) {
        PC.get(x);    //使用管程取数据,取数据过程中buffer资源互斥
        consume the item in nextc;
        ...
    }
}

//主函数
void main(){
    co_begin
        proceducer(); consumer();
    co_end
}

2. 食事の哲学者の問題

        食事の哲学者の問題。問題は、5 人の哲学者が円卓を共有し、その周りに 5 つの椅子に座り、円卓の上に 5 つの茶碗と 5 つの箸を置き、交互に考え、食べるという生き方をしていると記述します。通常、哲学者は考え事をし、お腹が空いたときは左右の一番近い箸を使おうとし、箸が2本ないと食事ができない食べ終わったら箸を置き、思考を続けます。// マルチプロセスとマルチリソースの同期の問題

(1) 記録セマフォを使用して哲学者の食事の問題を解決する

        分析の結果、テーブルの上に置かれた箸は重要な資源であり、一定期間、一人の哲学者だけがそれを使用することを許可されていることがわかります。箸の相互排他的使用を実現するために、箸を表すためにセマフォを使用することができ、これら 5 つのセマフォがセマフォ配列を形成します。

        説明は次のとおりです。// 共有リソースの数をセマフォとして抽象化します。

semaphore chopstick[5] = {1,1,1,1,1};

        すべてのセマフォは 1 に初期化され、i 番目の哲学者の活動は次のように説明できます。

do {
    wait(chopstick[i]);            //获取左边筷子,chopstick-1
    wait(chopstick[(i+1)%5]);      //获取右边筷子,chopstick-1
    ...
    //eat
    ...
    signal(chopstick[il);          //释放左边筷子,chopstick+1
    signal(chopstick[(i+1)%5]);    //获取右边筷子,chopstick+1
    ...
    //think
    ...
} while(TRUE);

        上記の説明では、哲学者はお腹が空いているとき、常に最初に左側の箸を取りに行きます、つまり wait(chopstick[i]) を実行し、成功した後、右側の箸を取りに行きます、つまり、実行 wait(chopstick[(i +1)%5]); 次の成功後に食事をすることができます。食べ終わったら、まず左側の箸を置き、次に右側の箸を置きます。//箸を順番に取ります

        上記の解決策では、隣接する 2 人の哲学者が同時に食事をしないことを保証できますが、デッドロックが発生する可能性があります。5 人の哲学者が同時にお腹が空いて左側の箸を取ると、5 本の手腕箸がすべて 0 になります、お待ちください。//デッドロックの問題を引き起こす

        このようなデッドロックの問題に対しては、次の解決策が考えられます。

  • 左側の箸を同時に取れるのは最大 4 人の哲学者です。最終的に、少なくとも 1 人の哲学者が食事をすることが保証され、使い終わったら使用していた 2 本の箸を放すことができ、より多くの哲学者が食事をすることができます。食べる、食事。// プロセス数を制限する
  • 哲学者は左右の箸が揃っている場合にのみ、箸を使って食事をすることが許されます。//リソース取得は中断できません
  • 奇数番目の哲学者は最初に左側の箸を取り、次に右側の箸に進み、偶数番目の哲学者はその逆を行うと規定されています。このルールによれば、哲学者 No. 1 と No. 2 は箸 No. 1 を奪い合い、哲学者 No. 3 と No. 4 は箸 No. 3 を奪い合うことになります。つまり、5人の哲学者全員が最初に奇数の箸を奪い合い、偶数の箸を手に入れた後は奪い合い、最終的には必ず1人の哲学者が2本の箸を手に入れて食事をすることができる。

(2) AND セマフォ機構を使用して哲学者の食事の問題を解決する

        哲学者の食事の問題では、各哲学者は食事の前に 2 つの重要なリソース (箸) を取得する必要があります。これは本質的に AND 同期問題であるため、AND セマフォ メカニズムを使用することが最も簡潔な解決策です。// プロセスは複数の重要なリソースを使用する必要があります

//定义临界资源
semaphore chopstick chopstick[5]= {1,1,1,1,1};

do {
    ...
    //think
    ...
    Sswait(chopstick[(+1)%5], chopstick[i]);  //同时锁定两个信号量+2
    ...
    //eat
    ...
    Ssignal(chopstick[(i+1)%5], chopstick[i]); //同时释放两个信号量-2
} while(TRUE);

3. リーダーライター問題

        データファイルは複数のプロセスで共有することができ、ファイルを読み込むだけのプロセスを「リーダープロセス」と呼び、それ以外のプロセスを「ライタープロセス」と呼びます読み取り操作によってデータ ファイルが乱雑にならないため、複数のプロセスが同時に共有オブジェクトを読み取ることができます。ただし、Writer プロセスと他の Reader プロセスまたは Writer プロセスが同時に共有オブジェクトにアクセスすることはできません。そのようなアクセスは混乱を引き起こす可能性があるためです。// 読み取り/書き込みロック、つまり共有ロックと排他ロック

        いわゆる「Reader-Writer 問題」とは、Writer プロセスが他のプロセスと排他的に共有オブジェクトにアクセスする必要があることを保証する同期問題を指します。

(1) レコードセマフォを使用してリーダーライター問題を解決する

相互排他セマフォw_mutex は、        読み取りまたは書き込み時に Reader プロセスと Writer プロセス間の相互排他を実現するために設定されますさらに、読み取られるプロセスの数を示す整数変数 Read_count を設定します。Reader プロセスが読み取りを行っている限り、Writer プロセスは書き込みを許可されません。したがって、read_count = 0 (Reader プロセスの読み取りがないことを示す) の場合にのみ、Reader プロセスは wait(w_mutex) オペレーションを実行する必要があります。wait(w_mutex) 操作が成功すると、Reader プロセスは読み取りを行うことができ、それに応じてread_count +1 操作を実行できます。// 書き込みと読み取りの相互排他、読み取りと読み取りの共有

        同様に、read_count を1減算してReaderプロセスの値が0になった場合のみ、signal(w_mutex)オペレーションを実行してWriterプロセスに書き込みを許可する必要があります。また、read_count は 複数の Reader プロセスからアクセスできる重要なリソースであるため、ミューテックス セマフォr_mutexも設定する必要があります。

       リーダーライター問題は次のように説明できます。

//定义读信号量,写信号量
semaphore r_mutex=1,w_mutex=l;
//读进程统计
int read_count = 0;

//读操作
void reader() {
    do {
        wait(r_mutex);                     //读信号量 -1
        if(read_count==0) wait(w_mutex);   //读操作进入时,阻塞写操作
        readcount++;                       //临界资源,读进程数+1
        signal(r_mutex);                   //释放读信号量+1
        ...
        perform read operation;            //此时读操作中仍然持有写信号量
        ...
        wait(r_mutex);                     
        readcount--;
        if(read_count==0) signal(w_mutex); //读操作退出时,如果读进程数量为0,释放写信号量
        signal(r_mutex);
    } while(TRUE)
}

//写操作
void writer() {
    do {
        wait(w_mutex);             //写操作,写信号量-1
        perform write operation;
        signal(w_mutex);           //退出写操作,写信号量+1
    } while(TRUE);
}

//主函数
void main(){
    cobegin
        writer();reader();
    coend
}

(2) セマフォセット機構を利用してリーダライタ問題を解決する

        ここでのリーダー/ライターの問題は前の問題とは少し異なり、RN リーダーのみが同時に読み取りを許可されるという制限が追加されます。このために、セマフォ Lを導入し、その初期値を RN として与えます。リーダの数は、wait(L,1,1) オペレーションを実行することで制御します。リーダが入るたびに、wait( を実行する必要があります) L,1,1) L の値を 1 減らす演算。RN リーダーが読み取りに入ると、L は 0 に減らされ、RN+1 番目のリーダーが読み取りに入ろうとすると、wait(L,1,1) 操作の失敗により必然的にブロックされます。

// 形式説明: wait (セマフォ、下限、要求)、セマフォ < 下限の場合、セマフォの割り当ては許可されず、ブロック

// シグナル(セマフォ、リリース量)

        セマフォ セットを使用してリーダー/ライターの問題を解決する方法は次のとおりです。

//定义读者数量
int RN;
//定义信号量L、信号量mx
semaphore L=RN, mx=1;

//读操作
void reader() {
    do {
        Swait(L, 1, 1);             // 信号量L,下限值1,当 L>=1 时,分配1个信号量
        Swait(mx, 1, 0);            // 信号量mx,下限值1,当 L>=1 时,分配0个信号量,也就是不扣减 mx 信号量
        perform read operation;
        ...
        Ssignal(L, 1);              // 释放一个L信号量,L+1
    } while(TRUE);
}

//写操作
void writer() {
    do {
        Swait(mx, 1, 1, L, RN, 0);  // 同时满足,mx>=1,扣减1个mx && L=RN,不扣减L数量
        perform write operation;
        Ssignal(mx, 1);             // 释放mx
    } while(TRUE);
}

//主函数
void main() {
    cobegin
        reader(); writer();
    coend
}

        このうち、Swait(mx,1,0)ステートメントはスイッチとして機能します。書き込みプロセスが書き込み操作に入らない限り (mx=1)、読み取りプロセスは読み取り操作に入ることができます。ただし、書き込みプロセスが書き込み操作に入ると、mx=0 である限り、読み取りプロセスは読み取り操作に入ることができません。Swait(mx, 1, 1, L, RN, 0) ステートメントは、書き込みプロセスが書き込み中でない場合 (mx =1)、読み取りプロセスが読み取り中でない場合 (L=RN) にのみ、書き込みプロセスがクリティカル セクションに入ることができることを意味します。 .書き込み操作。

// 書き込み時のみ、mx セマフォが差し引かれますが、同時に読み取り中のプロセスがないことを確認します。mx = 1 は 1 つのプロセスのみが書き込みを許可されることを意味します

// 読み取り操作では L の数が差し引かれますが、書き込みを行っているプロセスがないこと、つまり mx = 1、L=RN であることを確認する必要もあります。これは、最大 RN プロセスが一緒に読み取りを許可されることを意味します

// このメソッドは、読み取り操作のプロセス数を制限します。これは、レコード型セマフォメソッドと比較して多少制限的です。

おすすめ

転載: blog.csdn.net/swadian2008/article/details/131372602