使用System V信号量实现条件变量

时间:2013-01-29 18:54:13

标签: c linux ipc semaphore

我正在尝试使用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. 使用互斥锁等待条件变量。
  5. 如果我把这一切都弄好了,他们应该交替做'某事'。

    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循环的同一次运行中打印了两次,具有不同的值!对于cource来说,它最终会陷入僵局,所以我就这样了。

    不要担心get_unique_key()函数 - 它只是它的名字。

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

0 个答案:

没有答案