セマフォは、コンピュータ システム内の複数のプロセスのアクティビティを調整するために使用される単なる通常の変数です。これらは、相互排他を強制し、競合状態を回避し、プロセス間の同期を実装するために使用されます。
セマフォを使用するプロセスでは、待機 (P) とシグナル (V) という 2 つの操作が提供されます。待機操作はセマフォの値を減少させ、信号操作はセマフォの値を増加させます。セマフォの値が 0 の場合、待機操作を実行するプロセスは、別のプロセスがシグナル操作を実行するまでブロックされます。
セマフォは、一度に 1 つのプロセスのみによって実行される必要があるコード領域であるクリティカル セクションを実装するために使用されます。セマフォを使用すると、プロセスは共有メモリや I/O デバイスなどの共有リソースへのアクセスを調整できます。
セマフォは、特定の同期プリミティブを通じてのみ使用できる特別な種類の同期データです。プロセスがセマフォに対して待機操作を実行する場合、操作ではセマフォの値が 0 より大きいかどうかがチェックされます。存在する場合、セマフォの値をデクリメントし、プロセスが実行を継続できるようにします。それ以外の場合は、セマフォ上のプロセスがブロックされます。セマフォに対するシグナル操作は、セマフォ上でブロックされているプロセスがあればそれを起動するか、セマフォの値を 1 つ増やします。これらのセマンティクスにより、セマフォはカウント セマフォとも呼ばれます。セマフォの初期値によって、待機操作を通過できるプロセスの数が決まります。
擬似コードJava
セマフォには 2 つのタイプがあります。
- バイナリセマフォ –
これはミューテックス ロックとも呼ばれます。値は 0 と 1 の 2 つだけです。値は 1 に初期化されます。これは、複数のプロセスでクリティカル セクションの問題の解決策を実装するために使用されます。 - カウンティングセマフォ –
その値は、無制限のドメインに及ぶ可能性があります。複数のインスタンスを持つリソースへのアクセスを制御するために使用されます。
では、それがどのように行われるかを見てみましょう。
まず、セマフォ変数の値にアクセスして変更するために使用できる 2 つの操作を見てみましょう。

P と V の操作に関するいくつかのポイント:
Javaでの文字列比較
- P 動作はウェイト、スリープ、またはダウン動作とも呼ばれ、V 動作はシグナル、ウェイクアップ、またはアップ動作とも呼ばれます。
- どちらの操作もアトミックであり、セマフォは常に 1 に初期化されます。ここでアトミックとは、読み取り、変更、更新がプリエンプションなしで同時に/瞬間に行われる変数を意味します。つまり、読み取り、変更、更新の間に、変数を変更する可能性のある他の操作は実行されません。
- クリティカル セクションは、プロセス同期を実装するための両方の操作で囲まれています。下の画像を参照してください。プロセス P のクリティカル セクションは、P 操作と V 操作の間にあります。

次に、相互排除がどのように実装されるかを見てみましょう。 2 つのプロセス P1 と P2 があり、セマフォ s が 1 に初期化されているとします。 ここで、P1 がクリティカル セクションに入ると仮定すると、セマフォ s の値は 0 になります。 ここで、P2 がクリティカル セクションに入りたい場合、P2 は s になるまで待機します。> 0 の場合、これは P1 がクリティカル セクションを終了し、セマフォで V 操作を呼び出した場合にのみ発生します。
このようにして相互排除が実現されます。詳細については、バイナリ セマフォの以下の画像を参照してください。

実装: バイナリセマフォ
struct semaphore { enum value(0, 1); // q contains all Process Control Blocks (PCBs) // corresponding to processes got blocked // while performing down operation. Queueq; }; P(セマフォ s) { if (s.value == 1) { s.value = 0; } else { // プロセスを待機キューに追加 q.push(P) sleep(); V(セマフォ s) { if (s.q が空) { s.value = 1; } else { // 待機キューからプロセスを選択 Process p = q.front(); // プロセスは CS に送信されたため、 // 待機状態から削除します。 q.pop(); ウェイクアップ(p); } } // このコードは Susobhan Akhuli によって変更されています>> C #include #include #include struct semaphore{ Queueq; int 値; }; void P(struct semaphore s) { if (s.value == 1) { s.value = 0; } else { s.q.push(P); 寝る(); } } void V(セマフォ s) { if (s.q が空) { s.value = 1; } else { // 待機キュープロセスからプロセスを取得 p = q.front(); // 待機中のプロセスを削除します q.pop(); ウェイクアップ(p); int main() { printf('これはヘミッシュです!!'); // このコードは、Himesh Singh Chauhan によって提供されました return 0; } // このコードは Susobhan Akhuli によって変更されています>>'ジャワPython3 using System.Collections.Generic; class Semaphore { public enum value { Zero, One } public Queueq = 新しいキュー(); public void P(セマフォ s, プロセス p) { if (s.value == value.One) { s.value = value.Zero; } else { // プロセスを待機キューに追加 q.Enqueue(p); p.Sleep(); public void V(Semaphore s) { if (s.q.Count == 0) { s.value = value.One; } } } else { // 待機キューからプロセスを選択 Process p = q.Peek(); // プロセスが CS に送信されたため、待機状態から削除します。 // q.Dequeue(); p.Wakeup(); } } }>>' JavaScript上記の説明は、0 と 1 の 2 つの値のみを取り、相互排他を保証するバイナリ セマフォに関するものです。 1 より大きい値を取ることができるカウンティング セマフォと呼ばれるもう 1 つのタイプのセマフォがあります。ラテックステキストサイズ
ここで、インスタンス数が 4 のリソースがあるとします。ここで S = 4 を初期化し、残りはバイナリ セマフォの場合と同じです。プロセスがそのリソースを必要とするときはいつでも、P を呼び出すか関数を待ち、それが完了すると V またはシグナル関数を呼び出します。 S の値がゼロになると、プロセスは S が正になるまで待機する必要があります。たとえば、4 つのプロセス P1、P2、P3、P4 があり、それらはすべて S (4 で初期化) の待機操作を呼び出すとします。別のプロセス P5 がリソースを必要とする場合、4 つのプロセスのうちの 1 つがシグナル関数を呼び出し、セマフォの値が正になるまで待機する必要があります。
制限事項:
- セマフォの最大の制限の 1 つは優先順位の反転です。
- デッドロック。プロセスがスリープ状態ではない別のプロセスをウェイクアップしようとしているとします。したがって、デッドロックにより無期限にブロックされる可能性があります。
- オペレーティング システムは、待機してセマフォに信号を送るすべての呼び出しを追跡する必要があります。
このセマフォ実装の問題:
セマフォの主な問題は、ビジー待機が必要なことです。プロセスがクリティカル セクションにある場合、クリティカル セクションに入ろうとする他のプロセスは、クリティカル セクションがプロセスによって占有されなくなるまで待機することになります。プロセスが待機するたびに、セマフォ値を継続的にチェックし (この行を while (s==0); in P 操作で確認してください)、CPU サイクルを無駄にします。
ロックを待っている間プロセスがスピンし続けるため、スピンロックが発生する可能性もあります。これを回避するために、別の実装を以下に示します。
Linuxアーキテクチャ
実装: カウンティングセマフォ
CPP struct Semaphore { int value; // q contains all Process Control Blocks(PCBs) // corresponding to processes got blocked // while performing down operation. Queueq; }; P(セマフォ s) { s.value = s.value - 1; if (s.value< 0) { // add process to queue // here p is a process which is currently executing q.push(p); block(); } else return; } V(Semaphore s) { s.value = s.value + 1; if (s.value <= 0) { // remove process p from queue Process p = q.pop(); wakeup(p); } else return; }> ジャワ import java.util.LinkedList; import java.util.Queue; // semaphore class class Semaphore { // our value int value; Queueq; public Semaphore(int value) { this.value = value; q = 新しい LinkedList(); } public void P(プロセス p) {値--; if (値< 0) { q.add(p); p.block(); } } public void V() { value++; if (value <= 0) { Process p = q.remove(); p.wakeup(); } } }> Python3 import heapq # Global Variable to track the Processes going into Critical Section COUNTER=1 class Semaphore: def __init__(self,value): # Value of the Semaphore passed to the Constructor self.value=value # The Waiting queue which will be using the heapq module of Python self.q=list() def getSemaphore(self): ''' Function to print the Value of the Semaphore Variable ''' print(f'Semaphore Value: {self.value}') def block(process): print(f'Process {process} Blocked.') def wakeup(process): print(f'Process {process} Waked Up and Completed it's work.') def P(s): global COUNTER s.value=s.value-1 if(s.value<0): heapq.heappush(s.q,COUNTER) block(COUNTER) else: print(f'Process {COUNTER} gone inside the Critical Section.') COUNTER+=1 return def V(s): global COUNTER s.value=s.value+1 if(s.value<=0): p=heapq.heappop(s.q) wakeup(p) COUNTER-=1 else: print(f'Process {COUNTER} completed it's work.') COUNTER-=1 return # Can Pass the Value of the Counting Semaphore to the Class Constructor # Example for Counting Semaphore value as 2 s1=Semaphore(2) s1.getSemaphore() P(s1) s1.getSemaphore() P(s1) s1.getSemaphore() P(s1) s1.getSemaphore() V(s1) s1.getSemaphore() V(s1) s1.getSemaphore() V(s1) s1.getSemaphore() # This Code is Contributed by Himesh Singh Chauhan> C# using System.Collections.Generic; public class Semaphore { public int value; // q contains all Process Control Blocks(PCBs) // corresponding to processes got blocked // while performing down operation. Queueq = 新しいキュー(); public void P(プロセス p) { 値--; if (値< 0) { // add process to queue q.Enqueue(p); p.block(); } } public void V() { value++; if (value <= 0) { // remove process p from queue Process p = q.Dequeue(); p.wakeup(); } } }> JavaScript // Define a Semaphore object function Semaphore() { this.value = 0; this.q = []; // Initialize an array to act as a queue } // Implement the P operation Semaphore.prototype.P = function(p) { this.value = this.value - 1; if (this.value < 0) { // Add process to queue this.q.push(p); // Assuming block() and wakeup() functions are defined elsewhere block(); } }; // Implement the V operation Semaphore.prototype.V = function() { this.value = this.value + 1; if (this.value <= 0) { // Remove process p from queue var p = this.q.shift(); // Assuming wakeup() function is defined elsewhere wakeup(p); } }; // This code is contributed by Susobhan Akhuli> この実装では、プロセスが待機するたびに、そのセマフォに関連付けられたプロセスの待機キューにプロセスが追加されます。これは、そのプロセスのシステム コール block() を通じて行われます。プロセスが完了すると、シグナル関数が呼び出され、キュー内の 1 つのプロセスが再開されます。 wakeup() システムコールを使用します。
セマフォの利点:
- プロセス同期のためのシンプルかつ効果的なメカニズム
- 複数のプロセス間の調整をサポート
- 共有リソースを管理するための柔軟かつ堅牢な方法を提供します。
- プログラム内の重要なセクションを実装するために使用できます。
- 競合状態を回避するために使用できます。
セマフォの欠点:
- 待機およびシグナル操作に関連するオーバーヘッドにより、パフォーマンスの低下につながる可能性があります。
- 誤って使用するとデッドロックが発生する可能性があります。
- これは 1965 年にダイクストラによって提案され、セマフォとして知られる単純な整数値を使用して同時プロセスを管理する非常に重要な技術です。セマフォは、スレッド間で共有される単なる整数変数です。この変数は、クリティカル セクションの問題を解決し、マルチプロセッシング環境でプロセスの同期を実現するために使用されます。
- 適切に使用しないと、プログラムでパフォーマンスの問題が発生する可能性があります。
- デバッグと保守が難しい場合があります。
- 正しく使用しないと、競合状態やその他の同期の問題が発生する可能性があります。
- サービス拒否攻撃など、特定の種類の攻撃に対して脆弱になる可能性があります。