2005年3月14日
排他制御をおこなうための機能
IPC (Inter Process Communication) プロセス間通信を行うための手段のひとつ。
現在のプログラミング言語には、Thread機能など排他制御機能が備わっているが
C言語自体に排他制御は備わっていないため、PosixのPThreadやMachカーネルの
C-Threadなどで多くの排他制御を実装してきた。セマフォはOSの機能を利用すること
による排他制御の実装である。これはThread以前でのプロセス間通信時の排他制御
に利用されてきた。
セマフォは以下のAPIをつかう。
semctl セマフォの制御(control)
semget セマフォの取得(get)
semopt セマフォの操作(operation)
詳しくはmanを確認すること。
ソースコード
[s-okita@stoc semaphore]$ cat semaphore_sample.c
/* semaphore_sample.c
*
* System V IPC のセマフォ
*
* Linux Kernel 2.4.20
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
/* sys/sem.h内部でbit/sem.hを読んでいる
* bit/sem.hを読むとLinu Kernel 2.4.20ではunion semunを定義しなければ
* ならないのが分かる。以前はここに実装されていたようだ。
*
* またmanを読むと#include <sys/ipc.h>が必要と書いてあるが、
* sys/sem.h内でincludeされているので必須ではない。
* セマフォを使ってますよ。というラベルとしての意味にとどまる
*/
#include <sys/sem.h>
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int main(int argc, char *argv[]) {
union semun semaphore_union;
struct sembuf semaphore_buffer;
int counter;
char op_char = 'o';
int semaphore_id;
/* セマフォ識別子を戻り値として取得し、これを今後使いまわす */
semaphore_id = semget((key_t)1000, 1, 0666 | IPC_CREAT);
if ( argc > 1 ) {
/* セマフォの初期化 */
if ( semctl(semaphore_id, 0, SETVAL, semaphore_union) != 0 ) {
fprintf(stderr, "Failed to initialize\n");
exit(1);
} else {
printf("Success to initialize\n");
}
op_char = 'x';
sleep(2);
}
for ( counter = 0; counter < 10; counter++ ) {
/* セマフォの設定をする */
semaphore_buffer.sem_num = 0;
semaphore_buffer.sem_op = -1;
semaphore_buffer.sem_flg = SEM_UNDO;
/* セマフォデータをsemop関数で操作する
* これにより、ここ以降は排他制御になる
*/
if ( semop(semaphore_id, &semaphore_buffer, 1) == -1 ) {
fprintf(stderr, "Failed to operation\n");
exit(1);
}
printf("%c", op_char);
fflush(stdout);
/* プログラムの挙動を確認させるためランダムに停止させる */
sleep(rand() % 2);
printf("%c", op_char);
fflush(stdout);
/* セマフォの設定をする */
semaphore_buffer.sem_num = 0;
semaphore_buffer.sem_op = 1;
semaphore_buffer.sem_flg = SEM_UNDO;
/* セマフォの操作をおこなう。
* sem_op = 1を実行することで、排他制御の解除をしている
* この1という数値は、特に決められた数値ではなく任意
* 0と1などのように2つのデータで判断することをバイナリセマフォ
* と呼ぶ
*/
if ( semop(semaphore_id, &semaphore_buffer, 1) == -1 ) {
fprintf(stderr, "Failed to operation\n");
exit(1);
}
}
if ( argc > 1 ) {
sleep(10);
/* セマフォの開放をおこなう */
if ( semctl(semaphore_id, 0, IPC_RMID, semaphore_union) != 0 ) {
fprintf(stderr, "Failed to free\n");
exit(1);
} else {
printf("Success to free\n");
}
}
printf("\n%d - program finished\n", getpid());
return 0;
}
コンパイル
[s-okita@stoc semaphore]$ gcc -O2 -Wall -o semaphore_sample semaphore_sample.c
実行結果
./semaphore_sampleを引数有りでバックグラウンドで動作させ、もうひとつ引数なしで起動している
[s-okita@stoc semaphore]$ ./semaphore_sample 1 &
[1] 22075
Success to initialize
[s-okita@stoc semaphore]$ ./semaphore_sample
oxooooxxxxooxxooxxooxxooooooxxxxxxooxxo
22076 - program finished
[s-okita@stoc semaphore]$ xSuccess to free
22075 - program finished
[1]+ Done ./semaphore_sample 1