带有测试和设置的信号量(代码实现可能的错误)

Semaphores with test and set (code implementation possible mistake)

我一直在学习信号量,我正在查看信号量的网站实现 (http://faculty.salina.k-state.edu/tim/ossg/IPC_sync/ts.html),但是,我不了解该实现,以节省任何访问该网站的代码显示下面。

struct semaphore {
    int value = <initial value>;
    boolean mutex = FALSE;
    boolean hold = TRUE;
    queue m, h;
};

shared struct semaphore s;

/* P() */
acquire_semaphore(struct semaphore s) {
    while(TS(s.mutex)) WAIT(s.m);    /* wait for intertal mutex */
    s.value--;
    if(s.value < 0) {
        s.mutex = FALSE;
        SIGNAL(s.m);
        while(TS(s.hold)) WAIT(s.h); /* wait - too many out */
    }
    else
        s.mutex = FALSE;
        SIGNAL(s.m);
}

/* V() */
release_semaphore(struct semaphore s) {
    while(TS(s.mutex)) WAIT(s.m);   /* wait for intertal mutex */
    s.value++;
    if(s.value >= 0) {
        s.hold = FALSE;
        SIGNAL(s.h);
    }
    s.mutex = FALSE;
    SIGNAL(s.m);
}
boolean Test_and_Set( boolean memory[m] )
{ [
    if( memory[m] )        // lock denied
        return True;
    else {                 // lock granted
        memory[m] = True;
        return False;
    }
  ]
}

我假设的 TS 方法是 TestandSet(),上面也显示了从同一个网站复制的,我的问题是,如果出现 3 个进程并调用 acquire_semaphore,信号量初始化为一个值为 1 那么信号量的值将变为 -2 并且进程 p2 和 p3 将进入 h 队列并且永远不会被唤醒,这似乎不正确所以我假设代码中有错误?我假设要解决这个问题,释放信号量中的行 "if(s.value >= 0) {" 应该是 "if(s.value <= 0) {",因为这会唤醒保持 (h) 队列中的下一个进程。下面显示了 table 我使用 3 个名为 p1、p2 和 p3 的进程手动完成代码的过程。感谢您的帮助。

Action            | Value | Mutex | Hold | m  | h       | Comments
init              | 1     | FALSE | TRUE | [] | []      | 
P1 aquire lock    | 0     | FALSE | TRUE | [] | []      | P1 now has the semaphore
P2 aquire lock    | -1    | FALSE | TRUE | [] | [P2]    | P2 added to the h queue
P3 aquire lock    | -2    | FALSE | TRUE | [] | [P2,P3] | p3 added to the h queue
P1 release lock   | -1    | FALSE | TRUE | [] | [P2,P3] | lock released but since s.value is <= 0 signal not set to wake s.h queue

第一个:

acquire_semaphore(struct semaphore s)

调用时,信号量由 value 提供,几乎可以肯定应该由 reference 提供。当它是 value 时,acquire 所做的任何更改根本不会反映在 s 中 [见注释 1]。因此,无论 acquire 做什么,它都不提供信号量获取语义。这同样很可能适用于对未指定队列类型(s->m、s->h)的引用。

这是另一个经典:

}
else
    s.mutex = FALSE;
    SIGNAL(s.m);

编译器实际理解为:

} else {
    s.mutex = FALSE;
}
SIGNAL(s.m);

这似乎不对,但很多地方似乎都不对。所以,如果这能起到任何作用,那就是运气(也许是运气不好?)。忽略它,除非你被指派去修复它。

其次,似乎试图在WAIT & SIGNAL之上实现信号量,相当于信号量;它很可能可以更正为:

struct semaphore {
    queue s;
};
acquire_semaphore(struct semaphore *s) {
    WAIT(&s->s);
}
release_semaphore(struct semaphore *s) {
    SIGNAL(&s->s);
}

并完成它。

[注]: 合理布局信号量为:

struct semaphore {
     struct _semaphore *p;
};
struct _semaphore {
     /* actual bits to make a semaphore here */
};

它具有允许信号量中的复制语义的良好效果,因此如果有人做了类似重新分配包含信号量的结构的操作,它会按预期运行。此示例中未执行此操作,但最好记住。