3

我正在尝试使用 System V 信号量来实现条件变量。为简单起见,我们假设一次只有一个进程可以等待一个条件变量。这个概念看起来很简单,所以看看我对互斥锁和条件变量包装器的实现,其中:

  • 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);
}

现在看看我的最小测试程序。它由共享两个信号量的两个进程组成。一个用作互斥锁,另一个用作条件变量。他们都有相同的流程:

  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

尤其是看前两行。他们告诉您,通知编号 (0) 在 while 循环的同一运行中以不同的值打印了两次!当然,它最终陷入僵局,所以我 ^C 它。

不要为 get_unique_key() 函数操心——它的功能正如其名。

任何帮助完成这项工作或解释奇怪的输出将不胜感激。谢谢你。

4

0 回答 0