semctl関数は、セマフォの制御操作を行ないます。セマフォとは、元々は「手旗信号」の意味で、それから派生した鉄道の腕木信号に由来します。これにより、プロセス間の待ち合わせと排他制御を行うことができます。
セマフォを操作する関数にはsemctl関数以外に、semget関数とsemop関数があります。プロセス間の待ち合わせと、排他制御の手順についてはsemop関数を、セマフォの操作手順についてはsemget関数をご覧ください。
この関数は、C言語のライブラリ関数(標準関数)ではありませんので、コンパイラにより、使えない場合があります。
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, …);
semidは操作するセマフォ集合のセマフォIDを指定します。
semnumはセマフォ集合内の操作するセマフォを番号で指定します。セマフォ番号は0から始まります。
cmdは操作の種類を表すコマンドを指定します。コマンドの種類により、第4引数に次のsemun共用体を指定します。
union semun { int val; /* SETVAL の値 */ struct semid_ds *buf; /* IPC_STAT, IPC_SET 用のバッファ */ unsigned short *array; /* GETALL, SETALL 用の配列 */ };
正常終了した場合の戻り値は、コマンドの種類により異なります。失敗した場合は-1を返します。
第3引数のcmdには、次のものがあります。
コマンド | 内容 |
---|---|
IPC_STAT | 第1引数に指定したセマフォIDに関するカーネルデータ構造体の情報を、第4引数に指定したsemun共用体の*bufで指定されたsemid_ds構造体へコピーします。第2引数のsemnumは無視します。 |
IPC_SET | 第4引数に指定したsemun共用体の*bufで指定されたsemid_ds構造体のメンバのいくつかの値を、第1引数に指定したセマフォIDに関連づいたカーネルデータ構造体に書き込みます。 |
IPC_RMID | 第1引数に指定したセマフォIDに対応するセマフォ集合を削除し、その集合上のブロック(休眠状態)されている全てのプロセスを目覚めさせます。 |
GETALL | 第1引数に指定したセマフォIDに対応するセマフォ集合の全てのセマフォの値(semvalの値)を第4引数に指定したsemun共用体の*arrayに設定します。第2引数のsemnumは無視します。 |
GETNCNT | 第2引数のsemnumに対応するセマフォの値(semvalの値)が増加するのを待っているプロセスの数(semncntの値)を戻り値として返します。 |
GETPID | 第2引数のsemnumに対応するセマフォに対して、最後にsemop関数を実行したプロセスのプロセスIDを戻り値として返します。 |
GETVAL | 第2引数のsemnumに対応するセマフォの値(semval値の)を戻り値として返します。 |
GETZCNT | 第2引数のsemnumに対応するセマフォの値が0になるのを待っているプロセスの数(semzcntの値)を戻り値として返します。 |
SETALL | 第1引数に指定したセマフォIDに対応するセマフォ集合の全てのセマフォに、第4引数に指定したsemun共用体の*arrayの値を設定します。セマフォの値の変更により、休眠状態のプロセスは起こされます。第2引数のsemnumは無視します。 |
SETVAL | 第2引数のsemnumに対応するセマフォに、第4引数に指定したsemun共用体のvalの値を設定します。セマフォの値の変更により、休眠状態のプロセスは起こされます。第2引数のsemnumは無視します。 |
次の例題プログラムは、3つのプロセスの待ち合わせを行っています。子プロセス1のファイル出力完了を待って、子プロセス2が行数をカウントして表示し、子プロセス3が容量(バイト数)を表示しています。
プログラム 例
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> #include <sys/sem.h> #define LOCK -1 #define UNLOCK 1 #define WAIT 0 /* セマフォ操作(ロック/アンロック) */ void MySemop(int SemId, int SemOp); int main() { FILE *fp; int semid; union semun { int val; /* SETVAL の値 */ struct semid_ds *buf; /* IPC_STAT, IPC_SET 用のバッファ */ unsigned short *array; /* GETALL, SETALL 用の配列 */ } ctl_arg; char *file = './sem.txt'; int child_cnt; /* 1つのセマフォを持つセマフォ集合を新規作成 */ if ((semid = semget(IPC_PRIVATE, 1, 0600)) == -1){ perror('main : semget '); exit(EXIT_FAILURE); } /* セマフォに初期値(1)を設定 */ ctl_arg.val = 1; if (semctl(semid, 0, SETVAL, ctl_arg) == -1){ perror('main : semctl '); exit(EXIT_FAILURE); } /* 1つ目の子プロセスを生成 */ if (fork() == 0) { /* 子プロセス */ int line_cnt; printf('子プロセス1開始\n'); if ((fp = fopen(file, 'w')) == NULL) { perror('子プロセス1 '); exit(EXIT_FAILURE); } for (line_cnt = 1; line_cnt <= 5; ++line_cnt) { fprintf(fp, '子プロセス1のメッセージ%d\n', line_cnt); sleep(1); } fclose(fp); /* 待ち合わせ解除 */ ctl_arg.val = 0; if (semctl(semid, 0, SETVAL, ctl_arg) == -1){ perror('子プロセス1 '); exit(EXIT_FAILURE); } printf('子プロセス1終了\n'); exit(EXIT_SUCCESS); } /* 2つ目の子プロセスを生成 */ if (fork() == 0) { /* 子プロセス */ int in_char; int line_cnt = 0; printf('子プロセス2開始\n'); /* 待ち合わせ */ MySemop(semid, WAIT); /* 自プロセスをロック */ if ((fp = fopen(file, 'r')) == NULL) { perror('子プロセス2 '); exit(EXIT_FAILURE); } while ((in_char = fgetc(fp)) != EOF) { if (in_char == '\n') { ++line_cnt; /* 行数カウント */ } } fclose(fp); printf('子プロセス2:%sの行数は%d行です\n', file, line_cnt); printf('子プロセス2終了\n'); exit(EXIT_SUCCESS); } /* 3つ目の子プロセスを生成 */ if (fork() == 0) { /* 子プロセス */ int in_char; int byte_cnt = 0; printf('子プロセス3開始\n'); /* 待ち合わせ */ MySemop(semid, WAIT); /* 自プロセスをロック */ if ((fp = fopen(file, 'r')) == NULL) { perror('子プロセス3 '); exit(EXIT_FAILURE); } while ((in_char = fgetc(fp)) != EOF) { ++byte_cnt; /* 文字数(バイト)数カウント */ } fclose(fp); printf('子プロセス3:%sの容量は%dバイトです\n', file, byte_cnt); printf('子プロセス3終了\n'); exit(EXIT_SUCCESS); } /* 親プロセス。子プロセスの終了を待つ */ for (child_cnt = 0; child_cnt < 3; ++child_cnt) { wait(NULL); } /* セマフォを削除 */ if (semctl(semid, 0, IPC_RMID, ctl_arg) == -1){ perror('main : semctl '); exit(EXIT_FAILURE); } printf('親プロセス終了\n'); return EXIT_SUCCESS; } /* セマフォ操作(ロック/アンロック) */ void MySemop(int p_semid, int p_op) { struct sembuf sops[1]; sops[0].sem_num = 0; /* セマフォ番号 */ sops[0].sem_op = p_op; /* セマフォ操作 */ sops[0].sem_flg = 0; /* 操作フラグ */ if (semop(p_semid, sops, 1) == -1) { perror('MySemop '); exit(EXIT_FAILURE); } return; }
例の実行結果
$ ./semctl.exe 子プロセス1開始 子プロセス2開始 子プロセス3開始 子プロセス2:./sem.txtの行数は5行です 子プロセス2終了 子プロセス3:./sem.txtの容量は190バイトです 子プロセス3終了 子プロセス1終了 親プロセス終了 $ $ cat sem.txt 子プロセス1のメッセージ1 子プロセス1のメッセージ2 子プロセス1のメッセージ3 子プロセス1のメッセージ4 子プロセス1のメッセージ5 $