信号量的标准原子操作

时间:2015-08-02 21:18:01

标签: c semaphore

我是自学C,这是信号量的练习题:

  

回想一下,计数信号量S是一个可以的整数变量   只能通过两个标准的原子操作P(探针)和V来控制   (信号),如图所示:

/*The probe or wait operation */

P(S) {
    while(S <= 0); // do nothing
    S--;
}

/*The signal operation */

V(S) {
    S++;
}
     

计数信号量的值可以超过不受限制的范围   整数域(也就是说,信号量可以保持任意   值)而二进制信号量的值只能是0或1。   显示如何仅使用二进制来实现计数信号量   信号量和普通(即可抢占)机器指令   和数据结构。

     

为P和V操作提供伪代码。

我在网上找到了相关的答案:

struct semaphore {                                                        
    int value;                                                            
    queue L; // l list of processes                                       
}                                                                         

wait(S) {                                                                 

    if(s.value > 0){                                                      
        s.value = s.value - 1;                                            
    } else {                                                              
        add this process to S.L;                                          
        block;                                                            
    }                                                                     
}                                                                         

signal(S){                                                                

    if(S.L != EMPTY) {                                                    
        remove a process P from S.L;                                      
        wakeup(P);                                                        
    } else {                                                              
        s.value = s.value + 1;                                            
    }                                                                     
} 

但说实话,我不知道它在做什么。我真的很感激有人可以解释答案,或者用伪代码演示如何回答这个问题。

1 个答案:

答案 0 :(得分:5)

  

显示如何仅使用二进制来实现计数信号量   信号量和普通(即可抢占)机器指令   和数据结构。

二进制信号量非常像互斥体(存在一些显着的差异,但出于这个问题的目的,假设它们是等价的),因此您可以实现具有计数器的数据结构的通用信号量,二进制信号量。二进制信号量用于同步对计数器的访问。

可以通过获取二进制信号量(即等待信号量),递增计数器然后发信号通知二进制信号量(释放锁定)来实现信令。

通过重复获取锁(等待二进制信号量),测试计数器是否大于0以及释放锁来实现等待。如果计数器确实大于0,那么这意味着我们得到了一个位置,所以我们递减计数器并返回。请注意,这必须是原子的:我们不能释放二进制信号量并在之后递减计数器,因为这会打开一个时间窗口,其中另一个线程可能会错误地看到相同的东西并获取我们在该行中的位置。同时。因此,二进制信号量用于原子地测试计数器并减少它(如果它大于0)。

假设二进制信号量具有类型bin_sem_t。这里有一些代码说明了这一点。数据结构如下:

/* A semaphore implemented with a binary semaphore */
struct semaphore {
    bin_sem_t sem; /* Controls concurrent access to the counter */
    int counter;
};

信令:

void sem_signal(struct semaphore *semaphore) {
    /* We use the binary semaphore to atomically increment the counter */
    wait(semaphore->sem);
    semaphore->counter++;
    signal(semaphore);
}

等待:

void sem_wait(struct semaphore *semaphore) {
    int acquired = 0;
    /* Here we use the binary semaphore to atomically test and
     * decrement the counter
     */
    while (!acquired) {
        wait(semaphore->sem);
        if (semaphore->counter > 0) {
            semaphore->counter--;
            acquired = 1;
        }
        signal(semaphore->sem);
    }
}

一些重要的注释:

  • 该代码假定有一个初始化函数将计数器正确设置为0,并在对sem_signal()sem_wait()进行任何调用之前初始化二进制信号量。
  • 此实现使用普通的机器指令,如上所述。请注意,它会一直循环,直到有一个&#34;位置在行#34;。这就像有一个讨厌的旅行伴侣反复询问&#34;我们还在吗?&#34;。一点都不好。这称为 polling ,它很糟糕,因为它会浪费CPU周期。请记住,在现实世界中,信号量不是这样实现的;相反,只有当信号量空缺时,进程才会进入休眠状态并且可以运行。

说实话,我不认为您在网上找到的答案是正确的,它似乎没有在柜台或流程队列上进行任何形式的同步。因此,它无法解决实现同步原语时的一个核心问题:显然,它们必须是线程安全的!

这看起来也不正确:

  

计数信号量的值可以超过不受限制的范围   整数域(也就是说,信号量可以保持任意   价值)[...]

首先,通用信号量通常不具有负值,它总是大于或等于0.其次,计数器的值有明显的上限;电脑没有无限的记忆。在Linux中,信号量可以容纳的最大值在SEM_VALUE_MAX中定义为semaphore.h

所以请注意这些在线教程,其中大部分都是不准确的,缺乏细节,或者只是完全错误。你应该从一本好书中学习。我通常喜欢在UNIX环境中推荐高级编程,虽然它不是专门针对线程的,但是它深入介绍了同步原语。