C Language
プロセス間通信(IPC)
サーチ…
前書き
プロセス間通信(IPC)メカニズムは、異なる独立したプロセスが互いに通信することを可能にする。標準Cは、IPCメカニズムを提供しません。したがって、そのようなメカニズムはすべてホストオペレーティングシステムによって定義されます。 POSIXは広範なIPCメカニズムを定義しています。 Windowsは別のセットを定義します。他のシステムでは独自のバリアントが定義されています。
セマフォー
セマフォは、2つ以上のプロセス間で操作を同期させるために使用されます。 POSIXでは、セマフォ関数の2つの異なるセットを定義しています。
- 'System V IPC' -
semctl()
、semop()
、semget()
です。 - 'POSIXセマフォ' -
sem_close()
、sem_destroy()
、sem_getvalue()
、sem_init()
、sem_open()
、sem_post()
、sem_trywait()
、sem_unlink()
この節では、System V IPCセマフォについて説明します。これは、Unix System Vで始まったためです。
まず、必要なヘッダーを含める必要があります。古いバージョンのPOSIXでは、 #include <sys/types.h>
必要でした。現代のPOSIXとほとんどのシステムはそれを必要としません。
#include <sys/sem.h>
次に、親と子の両方でキーを定義する必要があります。
#define KEY 0x1111
この鍵は、両方のプログラムで同じである必要があります。または、同じIPC構造を参照しません。その値をハードコーディングせずに一致したキーを生成する方法があります。
次に、コンパイラによっては、この手順を実行する必要がある場合としない場合があります。セマフォ操作の目的で共用体を宣言します。
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
次に、あなたの試み (定義semwait
)および( 上げる semsignal
)の構造を。名前PおよびVはオランダ語
struct sembuf p = { 0, -1, SEM_UNDO}; # semwait
struct sembuf v = { 0, +1, SEM_UNDO}; # semsignal
さて、あなたのIPCセマフォのIDを取得することから始めましょう。
int id;
// 2nd argument is number of semaphores
// 3rd argument is the mode (IPC_CREAT creates the semaphore set if needed)
if ((id = semget(KEY, 1, 0666 | IPC_CREAT) < 0) {
/* error handling code */
}
親では、カウンタが1になるようにセマフォを初期化します。
union semun u;
u.val = 1;
if (semctl(id, 0, SETVAL, u) < 0) { // SETVAL is a macro to specify that you're setting the value of the semaphore to that specified by the union u
/* error handling code */
}
これで、必要に応じてセマフォをデクリメントまたはインクリメントできます。クリティカルセクションの開始時に、 semop()
関数を使用してカウンタを減らします。
if (semop(id, &p, 1) < 0) {
/* error handling code */
}
セマフォをインクリメントするには、 &p
代わりに&p
&v
を使用します。
if (semop(id, &v, 1) < 0) {
/* error handling code */
}
すべての関数は、成功すれば0
を返し、失敗した場合は-1
を返します。これらの戻り状況をチェックしないと、壊滅的な問題が発生する可能性があります。
例1.1スレッドを使用したレース
以下のプログラムでは、子プロセスをfork
、親と子の両方が同期せずに文字を端末に出力しようとします。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
}
出力(1回目):
aAABaBCbCbDDcEEcddeFFGGHHeffgghh
(2回目):
aabbccAABddBCeeCffgDDghEEhFFGGHH
このプログラムをコンパイルして実行すると、毎回異なる出力が得られるはずです。
例1.2:セマフォーによるレースの回避
セマフォーを使用するように例1.1を変更すると、
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KEY 0x1111
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
struct sembuf p = { 0, -1, SEM_UNDO};
struct sembuf v = { 0, +1, SEM_UNDO};
int main()
{
int id = semget(KEY, 1, 0666 | IPC_CREAT);
if(id < 0)
{
perror("semget"); exit(11);
}
union semun u;
u.val = 1;
if(semctl(id, 0, SETVAL, u) < 0)
{
perror("semctl"); exit(12);
}
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(13);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(14);
}
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(15);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(16);
}
sleep(rand() % 2);
}
}
}
出力:
aabbAABBCCccddeeDDffEEFFGGHHgghh
このプログラムをコンパイルして実行すると、毎回同じ出力が得られます。