<<戻る

2005年3月14日

セマフォ(semaphore)


排他制御をおこなうための機能


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