※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

 仕事で、複数のプロセスに同時に処理をさせる仕組みが必要になった。
 最初に考えたのは以下のようなアルゴリズム。

 1) プロセス数分のセマフォ値をもつセマフォを1つ作成。
 2) セマフォ値をすべて減算(プロセス数分減算)して0にする。
 3) 各プロセスは、同時に開始したい処理の直前でセマフォを1つ減算。
    ー>セマフォ値が0なので待ちになる。
 4) 全プロセスが減算待ちになったら、1)で減算した値を加算する。
    ー>(3)で待ちになった全プロセスが1減算可能になるため同時に動き出す。

 なお、2)でいっきょにセマフォ値を0にすると、他のプロセスがセマフォ値を減算しようとしてもできず、他のプロセスは減算待ちとなる。これによりセマフォ値を0にしたプロセスは排他的に動作できるため、これを占有排他と呼ぶことにする。
 また、3)でセマフォ値がN(>1)の場合に
 しかし、これだと全プロセスが減算待ちになったのを知る方法が必要となる。通知などの仕組みを作るのは面倒である。
 なので、以下のアルゴリズムで最後のプロセスの準備ができたところで、全プロセスが動き出せるようにした。

1) プロセス数分の配列要素をもち、各配列要素がプロセス数分のセマフォ値をもつセマフォ集合を1つ作成。
2) セマフォ集合の全要素のセマフォ値をすべて減算(プロセス数分減算)して0にする。
3) 各プロセスは、同時に開始したい処理の直前で、以下を順に行う。
3-1) 自プロセス用の配列要素をプロセス数分加算して、その要素だけセマフォ値を元に戻す。
     ※自プロセス用とは、5プロセスなら1番目のプロセスが要素番号1で、5番目なら配列の要素番号5という意味。
3-2) セマフォ集合の全要素のセマフォ値をⅠずつ減算する。

 これにより、最後のプロセスがに3-1)を実行した時点で占有排他された配列要素(セマフォ値が0の要素)がなくなり、全プロセスが全配列要素から1ずつ減算できるようになり、すべてのプロセスが同時に動き出します。
 言葉では、なかなか理解しづらいと思うので、C言語のサンプルを作成してみました。(Cで作成したのは仕事で使うのがCのため。)

  • サンプルプログラム
#include <unistd.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

/* 定数宣言 */
#define PROCNUM 10
#define SLEEPTIME 2

/* グローバル変数 */
int semid;                   /* セマフォ集合識別子               */

/* man semctlよりコピー */
union semun {
    int              val;    /* SETVAL の値                   */
    struct semid_ds *buf;    /* IPC_STAT, IPC_SET 用のバッファ  */
    unsigned short  *array;  /* GETALL, SETALL 用の配列      */
    struct seminfo  *__buf;  /* IPC_INFO 用のバッファ(Linux 固有) */
} semunion;

struct sembuf semb[PROCNUM];
unsigned short semarray[PROCNUM];


/* 関数プロトタイプ */
void cre_semaphore(union semun semunion, int semcnt);
void op_semaphore(int semid, struct sembuf *semb, unsigned semnum);
void del_semaphore(int semid);
void debugsp(int semnum, char *str);


/* Main */
int main(void) {
    int pid, i, j;

    /* セマフォ値設定 */
    for (i = 0; i < PROCNUM; i++) {
        semarray[i] = PROCNUM;
    }
    semunion.array = semarray;

    /* セマフォの作成 */
    cre_semaphore(semunion, PROCNUM);

    /* 全プロセスのセマフォ集合要素を占有排他 */
    for (i = 0; i < PROCNUM; i++) {
        semb[i].sem_num = i;
        semb[i].sem_op = 0 - PROCNUM;
        semb[i].sem_flg = SEM_UNDO;
    }
    op_semaphore(semid, semb, PROCNUM);

    /* 子プロセス起動 */
    errno = 0;
    for (i = 1; i < PROCNUM; i++) {
        pid = fork();
        switch (pid) {
            case 0:
                /* 子プロセス */
                sleep(SLEEPTIME * i);

                /* 自プロセスのセマフォ集合要素の占有排他解除 */
                semb[i].sem_num = i;
                semb[i].sem_op = PROCNUM;
                semb[i].sem_flg = SEM_UNDO;
                op_semaphore(semid, &semb[i], 1);

                /* 全プロセスのセマフォ集合要素の共有排他 */
                for (j = 0; j < PROCNUM; j++) {
                    semb[j].sem_num = j;
                    semb[j].sem_op = -1;
                    semb[j].sem_flg = SEM_UNDO;
                }
                printf("Proc%02d exclude start!!\n", i);
                op_semaphore(semid, semb, PROCNUM);
                sleep(1);
                printf("Proc%02d exclude end!!\n", i);

                exit(0);
                break;
            case -1:
                printf("Proc%02d fork failure\n", i);
                perror("fork failure");
                exit(EXIT_FAILURE);
                break;
            default:
                break;
        }
    }
    i = 0;
    //printf("Proc%02d sleep start!!\n", i);

    /* 自プロセスのセマフォ集合要素の占有排他解除 */
    semb[i].sem_num = i;
    semb[i].sem_op = PROCNUM;
    semb[i].sem_flg = SEM_UNDO;
    op_semaphore(semid, &semb[i], 1);

    /* 全プロセスのセマフォ集合要素の共有排他 */
    for (j = 0; j < PROCNUM; j++) {
        semb[j].sem_num = j;
        semb[j].sem_op = -1;
        semb[j].sem_flg = SEM_UNDO;
    }
    printf("Proc%02d exclude start!!\n", i);
    op_semaphore(semid, semb, PROCNUM);
    sleep(1);
    printf("Proc%02d exclude end!!\n", i);

    sleep(5);
    del_semaphore(semid);
    exit(EXIT_SUCCESS);
}

void cre_semaphore(union semun semunion, int semcnt) {
    int i;

    /* セマフォの作成 */
    errno = 0;
    semid = semget((key_t) 1111, semcnt, 0666 | IPC_CREAT);
    if (semid == -1) {
        perror("semget failure");
        exit(EXIT_FAILURE);
    }

    /* セマフォの初期化 */

    if (semctl(semid, semcnt, SETALL, semunion) == -1) {
        perror("semctl(init) failure");
        exit(EXIT_FAILURE);
    }
}

void op_semaphore(int semid, struct sembuf *semb, unsigned semnum) {
    int i, j;
    /* セマフォの取得(ロック) */
    errno = 0;
    debugsp(semnum, "sigsp_bef");
    if (semop(semid, semb, semnum) == -1) {
        perror("semop(wait) failure");
    }
    debugsp(semnum, "sigsp_aft");
}

/* セマフォの削除 */
void del_semaphore(int semid) {
    union semun semunion;

    errno = 0;
    if (semctl(semid, 0, IPC_RMID, semunion) == -1) {
        perror("semctl(del_semaphore) failure");
    }

}

void debugsp(int semnum, char *str) {
    int i;
#ifdef DEBUG
    if (semctl(semid, PROCNUM, GETALL, semunion) == -1) {
        perror("semctl(init) failure");
        exit(EXIT_FAILURE);
    }
    for (i = 0; i < PROCNUM; i++) {
        printf("%s_semun.array[%d] = %d\n", str, i, semunion.array[i]);
    }
    for (i = 0; i < semnum; i++) {
        printf("%s_semb[%d] sem_num=%d, sem_op=%d, sem_flg=%d\n", str, i, semb[i].sem_num, semb[i].sem_op, semb[i].sem_flg);
    }
#endif
}

  • 実行結果
$ gcc -o Semaphore Semaphore.c
$ ./Semaphore 
Proc00 exclude start!!
Proc01 exclude start!!
Proc02 exclude start!!
Proc03 exclude start!!
Proc04 exclude start!!
Proc05 exclude start!!
Proc06 exclude start!!
Proc07 exclude start!!
Proc08 exclude start!!
Proc09 exclude start!!
Proc00 exclude end!!
Proc09 exclude end!!
Proc04 exclude end!!
Proc01 exclude end!!
Proc06 exclude end!!
Proc02 exclude end!!
Proc05 exclude end!!
Proc03 exclude end!!
Proc08 exclude end!!
Proc07 exclude end!!


名前:
コメント: