LinuxでのIPC-信号量の使用


いくつかのプロセスが同じメモリ領域をマッピングするのは最も速いIPC方法であるが、単純にmmapを使用すると、各プロセスの間にデータが「一致しない」リスクがあり、共有領域がある時点で1つのプロセスのみを許可することを保護するメカニズムが必要であり、この場合、信号量を使用する必要がある.そこで本稿では,前回の「LinuxでのIPC−共有メモリの使用」の続きと考えられる.
本稿では,信号量の使用を完全なプログラムを例に説明する.以下はプログラム全体のコードです.
#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h> 
#include <stdlib.h> 
#include <errno.h>
#include <string.h>
//==========================         ==============================
#define IPC_ID 0x26
 /*          semun     。 */
union semun 
{ 
    int val; 
    struct semid_ds *buf; 
    unsigned short int *array; 
    struct seminfo *__buf; 
};
int WhenError(const char * msg, int eno)
{
    printf("semaphore error[%d]:%s, %s
", eno, msg, strerror(eno)); exit(0); return -1; } /* count 。 */ int CreateSemaphore(int count) { key_t key; key = ftok(".",IPC_ID); int semid; semid = semget (key, count, 0666|IPC_CREAT); return (semid == -1)? WhenError("create sem(semget)", errno):semid; } /* 。 。 , -1 */ int FreeSemaphore(int semid) { union semun ignored_argument; return semctl (semid, 0, IPC_RMID, ignored_argument); //the second and forth args will be ignored in this case. } /* Set a semaphore to a value。*/ int SetSemaphoreValue(int semid, int index, int value) { union semun argument; argument.val = value; int ret; ret= semctl(semid,index,SETVAL,argument); return (ret == -1)? WhenError("Set Value(semctl)", errno):ret; } int SetSemaphoreValues(int semid, unsigned short * values) { union semun argument; argument.array=values; int ret; ret= semctl(semid,0,SETALL,argument); return (ret == -1)? WhenError("Set Values(semctl)", errno):ret; } int GetSemaphoreValue(int semid, int index) { int ret = semctl(semid,index,GETVAL,0); return (ret == -1)? WhenError("Get Value(semctl)", errno):ret; } /* sem[index] , , EAGAIN, 1, 0*/ int TryLockSemaphore(int semid, int index, int milliseconds) { int ret; struct sembuf operation; operation.sem_num = index; operation.sem_op = -1; if(milliseconds<1) { operation.sem_flg = IPC_NOWAIT; ret= semop (semid, &operation, 1); } Else { operation.sem_flg = SEM_UNDO; struct timespec timeout; timeout.tv_sec = (milliseconds / 1000); timeout.tv_nsec = (milliseconds - timeout.tv_sec*1000L)*1000000L; ret= semtimedop(semid, &operation, 1, &timeout); } if(ret == -1) { if(errno == EAGAIN) return EAGAIN; } return (ret == -1)? WhenError("Wait(semop)", errno):ret; } /* sem[index] , , , 1*/ int LockSemaphore(int semid, int index) { struct sembuf operation; operation.sem_num = index; operation.sem_op = -1; /* 。 */ operation.sem_flg = SEM_UNDO; /* */ int ret; ret= semop (semid, &operation, 1); return (ret == -1)? WhenError("Wait(semop)", errno):ret; } /* sem[index] 。 , LockSemaphore 。*/ int UnlockSemaphore(int semid, int index) { struct sembuf operation; operation.sem_num = index; operation.sem_op = 1; /* */ operation.sem_flg = SEM_UNDO; /* */ int ret; ret= semop (semid, &operations, 1); return (ret == -1)? WhenError("Post(semctl)", errno):ret; } //======================================================================== int main() { pid_t pid; int semid; semid = CreateSemaphore(1); SetSemaphoreValue(semid, 0, 1); pid = fork(); if(pid==0) //child 1 { sleep(1); int res; res = LockSemaphore(semid, 0); /* Try LockSemaphore res = EAGAIN; while(res== EAGAIN) { res= TryLockSemaphore(semid, 0, 1000); // 1000ms //do something } */ printf("child 1 set[%d]....
",res); // do critical things UnlockSemaphore (semid, 0); printf("child 1 post[%d]....
",res); return 0; } int dd; dd = LockSemaphore (semid, 0); printf("parent set 1[%d]....
",dd); sleep(5); dd = UnlockSemaphore (semid, 0); sleep(2); printf("parent end: ...[%d]
",dd); FreeSemaphore(semid); }

信号量の動作は、信号量の「作成」、PV動作、信号量の制御(クエリー値、解放など)をそれぞれ担当する3つのシステム関数semget、semop、semctlに関する.この3つの関数のプロトタイプとその各パラメータの意味は、関連記事を参照してください.ここではリストしません.
信号量について、カーネルはなぜ信号量セットを作成するインタフェースしか提供しないのか、少し理解できません.もちろん、これは単一の信号量に対する拡張であるが、信号量の使用は1つ1つであり、信号量の集合を単一の信号量にカプセル化して使用することは難しくなく(本明細書では簡単なパッケージを提供する)、一般的には、中小規模のシーケンスに必要な信号量の個数は多くない.
このコードでは、前の大きなコードは、カーネルに提供される信号セットのパッケージであり、アプリケーションを容易にする.この簡単なパッケージでは、まずsemun(なぜか分からない)とIPC_を定義します.ID(一意の信号量Idを生成するために使用される)は、以下の表でカプセル化関数のいくつかの説明である.
関数#カンスウ#
説明
int WhenError(const char * msg, int eno)
エラー処理関数は、実際に適用するときは、自分で1つ書きましょう.
int CreateSemaphore(int count)
「作成」は、プログラムに必要な信号量の個数を示す信号量セットであり、戻り値は信号量グループIdであり、以下の各呼び出し信号量の関数は、(Id,index)を特定の信号量の識別とする.
int FreeSemaphore(int semid)
信号量のセットを解放し、プログラムの終了時に呼び出しを適用します.そうしないと、信号量はずっと存在します.
int SetSemaphoreValue(int semid, int index, int value) int SetSemaphoreValues(int semid, unsigned short * values)
信号量の初期値の設定、反発信号量の場合は1を設定します.
int GetSemaphoreValue(int semid, int index)
信号量の値を表示します.ブロックされず、ポーリング方式で使用できる場合があります.
int LockSemaphore(int semid, int index)
「単一プロセス操作」領域に入ると、この関数が呼び出されます.信号量の値が1未満の場合、ブロックされ、他のプロセスが操作されていることを示します.
int UnlockSemaphore(int semid, int index)
単一プロセス・オペレーション・セクションを終了し、他のプロセスでロック・Semaphoreがブロックされていることを解除します.そのため、ロック・Semaphoreと一緒に使用する必要があります.
int TryLockSemaphore(int semid, int index, int milliseconds)
使用法はLockSemaphoreと似ていますが、タイムアウトして戻るだけで、戻り値はEAGAINです.この場合、プロセスは他のことをすることができ、「単一プロセス操作」領域に入ることはできません.millisecondsが1未満の場合、「単一プロセス操作」領域に入るかどうかにかかわらず、すぐに戻ります.
信号量はシステムのグローバルリソースであるため、semgetの最初のパラメータkeyの選択は(必ずしも上記の例のようにftokで生成されるとは限らない):本プログラムの信号量が他のプログラムのプロセスと共用されないようにするには、グローバルの唯一の数(他の人が使ったことがない、他の人が考えられない)が必要である.他のプロセスと共有する必要がある場合は、「周知」の量(双方の約束)が必要です.
一般的に、プログラム終了時にFreeSemaphoreを呼び出す必要があり、プログラムが異常に異常に停止した場合、信号量が解放されず、プログラムが再起動し、同じ信号量を「作成」した場合、システムが前回作成した信号量を返すことがプログラムデバッグ時に特に重要である.
Semopの信号操作フラグsem_flgには2つの選択肢があります
  •  IPC_NOWAIT:信号の操作が満たされない場合、semop()はブロックされず、すぐに戻り、エラーメッセージを設定します.
  • SEM_UNDO:プログラム終了時(正常または異常にかかわらず)、信号値がsemop()呼び出し前の値にリセットされることを保証します.このような目的は、プログラムが異常な場合に終了したときにロックされたリソースをロック解除せず、そのリソースが永遠にロックされることを回避することである.

  • ブロックの場合はSEM_を使用UNDO、ブロックしないでIPCを使うNOWAIT.
    上記の例では1つの信号量しか使用していませんが、複数の信号量の使用に拡張できます.作成時にcountは信号量の個数に設定され、各信号量に対する操作はIndexが異なります.
    本プログラムのパッケージはC言語方式を採用しており、より一般的なため、C++のクラスを採用すれば、パッケージはより「きれい」になり、同時に意味のある文字列を採用して各信号量を識別することができ、硬い下付き(Index)方程式を採用するのではなく、プログラムでは、1つの関数を採用して文字列と下付きをマッピングすればよい.
    なお、信号量は一般的にプロセス間読み書き共有データや同期プロセスとして用いられるが、信号量自体にも整数量の情報があり、プロセス間でも「状態」のような情報を伝達することができる.例えば、UIプロセスがバックグラウンドプロセスにイベントの発生を通知し、バックグラウンドプロセスが何らかの応答を行う必要がある場合、信号量を採用してもいいです.