3

System V セマフォを使用して条件変数を実装しようとしています。簡単にするために、一度に 1 つのプロセスだけが条件変数を待機できると仮定しましょう。概念は単純に見えるので、ミューテックスと条件付き変数ラッパーの私の実装を見てください:

  • Mutex は、1 がフリー、0 がロックされたバイナリ セマフォです。
  • 条件変数はバイナリ セマフォであり、1 は誰かが待機していることを意味し、0 はそれ以外の場合を意味します。

ここに行きます:

//Sets value of 'num' semaphore from 'semid' semaphore set to be 'val'.
// Returns 0 on success and -1 on failure.
int set_value(int semid, int num, int val)
{
    union semun setval_semun;
    setval_semun.val = val;
    return semctl(semid, num, SETVAL, setval_semun);
}

// Returns the value of semaphore 'num' from 'semid' semaphore set
// or -1 on failure.
int get_value(int semid, int num)
{
    return semctl(semid, num, GETVAL);
}

// Locks 'mutex'. Returns 0 on success and 1 if 'mutex' is invalid.
// Returns -1 on other error.
int lock_mutex(int semid, int mutex)
{
    struct sembuf op;

    if (get_value(semid, mutex) > 1) return 1;

    op.sem_num  = mutex;
    op.sem_op   = -1;
    op.sem_flg  = 0;

    return semop(semid, &op, 1);
}

// Unlocks 'mutex'. Returns 0 on success and 1 ig the 'mutex' isn't locked.
// Returns -1 on different error.
int unlock_mutex(int semid, int mutex)
{
    struct sembuf op;

    if (get_value(semid, mutex) != 0) return 1;

    op.sem_num  = mutex;
    op.sem_op   = 1;
    op.sem_flg  = 0;

    return semop(semid, &op, 1);
}

// Waits on condition 'cond' and frees 'mutex'.
// Returns 0 on success and 1 if either some other process is waiting on 'cond'
// or 'mutex' is not locked. Returns -1 on other failure.
int wait_cond(int semid, int cond, int mutex)
{
    struct sembuf   ops[2];

    if (get_value(semid, cond) != 0 || get_value(semid, mutex) != 0)
        return 1;

    ops[0].sem_num  = cond;
    ops[0].sem_op   = 1;
    ops[0].sem_flg  = 0;

    semop(semid, ops, 1);

    ops[0].sem_num  = mutex;
    ops[0].sem_op   = 1;
    ops[0].sem_flg  = 0;

    ops[1].sem_num  = cond;
    ops[1].sem_op   = 0;
    ops[1].sem_flg  = 0;

    return semop(semid, ops, 2);
}

// Wakes up process waiting for 'cond'. Returns 0 on success. Returns 1 when
// no process is waiting for 'cond' and -1 on other error.
int wake_cond(int semid, int cond)
{
    struct sembuf   op;

    if (semctl(semid, cond, GETVAL) != 1)
        return 1;

    op.sem_num  = cond;
    op.sem_op   = -1;
    op.sem_flg  = 0;

    return semop(semid, &op, 1);
}

次に、最小限のテスト プログラムを確認してください。これは、2 つのセマフォを共有する 2 つのプロセスで構成されます。1 つはミューテックスとして機能し、もう 1 つは条件変数として機能します。どちらも同じフローを持っています:

  1. ミューテックスをロックします。
  2. 何かをしてください。
  3. 条件変数待ちのウェイクアップ処理。
  4. ミューテックスを使用して条件変数を待ちます。

私がこのすべてを正しければ、彼らは交互に「何か」をしているはずです.

int main(int argc, char** argv)
{
    key_t       key     = 0;
    pid_t       pid     = 0;
    int         semid   = 0;
    int const   mutex   = 0;
    int const   cond    = 1;
    int         repeat  = 5;
    char const* name    = NULL;
    union semun setval_semun;

    // Create unique key.
    key = get_unique_key(argv[0], 47); 
    if (key == -1)
        return 1;

    // Create and intialize semaphore set.
    semid = semget(key, 2, 0666 | IPC_CREAT);
    if (semid == -1)
        return 1;
    if (set_value(semid, mutex, 1) != 0 || set_value(semid, cond, 1) != 0)
    {
        semctl(semid, 0, IPC_RMID);
        return 1;
    }

    // Fork.
    pid = fork();
    if (fork() < 0)
        return 1;
    else if(pid > 0)
        name = "parent";
    else
        name = " child";

    while(repeat--)
    {
        printf("%s: (0), mutex: %d, cond: %d, repeat: %d\n", name,
            get_value(semid, mutex), get_value(semid, cond), repeat);

        if (lock_mutex(semid, mutex) != 0) return 1;

        sleep(1);

        printf("%s: (1), mutex: %d, cond: %d, repeat: %d\n", name,
            get_value(semid, mutex), get_value(semid, cond), repeat);

        if (wake_cond(semid, cond) != 0) return 1;

        printf("%s: (2), mutex: %d, cond: %d, repeat: %d\n", name,
            get_value(semid, mutex), get_value(semid, cond), repeat);

        if (repeat == 0)
        {
            if (unlock_mutex(semid, mutex) != 0) return 1;
        }
        else if (wait_cond(semid, cond, mutex) != 0) return 1;

        printf("%s: (3), mutex: %d, cond: %d, repeat: %d\n", name,
            get_value(semid, mutex), get_value(semid, cond), repeat);
    }

    //Wait for child and clear resources.
    if (pid > 0)
    {
        wait(NULL);
        semctl(semid, 0, IPC_RMID);
    }

    return 0;
}

そして、ここに私が得た出力がありますが、これは非常に奇妙です:

parent: (0), mutex: 1, cond: 1, repeat: 4 
parent: (0), mutex: 0, cond: 1, repeat: 4
 child: (0), mutex: 0, cond: 1, repeat: 4
 child: (0), mutex: 0, cond: 1, repeat: 4
parent: (1), mutex: 0, cond: 1, repeat: 4
parent: (2), mutex: 0, cond: 0, repeat: 4
^C

特に最初の 2 行を見てください。それらは、通知番号 (0) が異なる値で while ループの同じ実行で 2 回出力されたことを示しています! もちろんデッドロックに陥るので^Cします。

get_unique_key() 関数について気にしないでください。名前が示すとおりのことを行います。

この作業を行うため、または奇妙な出力を説明するための助けをいただければ幸いです。ありがとうございました。

4

0 に答える 0