14

我们在 VxWorks 5.5 之上的专有嵌入式平台上进行编程。在我们的工具箱中,我们有一个条件变量,它是使用 VxWorks 二进制信号量实现的。

现在,POSIX 提供了一个等待函数,它也接受一个互斥体。这将解锁互斥锁(以便其他任务可能写入数据)并等待其他任务发出信号(完成写入数据)。我相信这实现了所谓的监视器,ICBWT。

我们需要这样一个等待函数,但实现它很棘手。一个简单的方法可以做到这一点:

bool condition::wait_for(mutex& mutex) const {
    unlocker ul(mutex);    // relinquish mutex
    return wait(event);
}                          // ul's dtor grabs mutex again

但是,这具有竞争条件,因为它允许另一个任务在解锁之后和等待之前抢占该任务。另一个任务可以写入解锁后的日期,并在该任务开始等待信号量之前发出条件信号。(我们已经对此进行了测试,并且确实发生了这种情况并永远阻止了等待任务。)

鉴于 VxWorks 5.5 似乎没有提供 API 来在等待信号时暂时放弃信号量,有没有办法在提供的同步例程之上实现这一点?

注意: 这是一个非常旧的 VxWorks 版本,在没有 POSIX 支持的情况下 编译 (据我了解,由专有硬件的供应商提供)

4

3 回答 3

5

使用本机 vxworks 这应该很容易,这里需要一个消息队列。您的 wait_for 方法可以按原样使用。

bool condition::wait_for(mutex& mutex) const 
{
    unlocker ul(mutex);    // relinquish mutex
    return wait(event);
}                          // ul's dtor grabs mutex again

但是等待(事件)代码如下所示:

wait(event)
{
    if (msgQRecv(event->q, sigMsgBuf, sigMsgSize, timeoutTime) == OK)
    {
        // got it...
    }
    else
    {
        // timeout, report error or something like that....
    }
}

你的信号代码会是这样的:

signal(event)
{
    msgQSend(event->q, sigMsg, sigMsgSize, NO_WAIT, MSG_PRI_NORMAL);
}

因此,如果信号在您开始等待之前被触发,则 msgQRecv 将在最终被调用时立即返回该信号,然后您可以如上所述在 ul dtor 中再次使用互斥锁。

event->q 是一个 MSG_Q_ID,它是在事件创建时通过调用 msgQCreate 创建的,sigMsg 中的数据由您定义……但可以只是一个随机数据字节,或者您可以想出一个更智能的结构,包含有关谁发出信号或其他可能很高兴知道的信息。

更新多个服务员,这有点棘手:所以我会做一些假设来简化事情

  1. 待处理的任务数量在事件创建时是已知的并且是恒定的。
  2. 将有一项任务始终负责指示何时可以解锁互斥锁,所有其他任务只需要在事件发出/完成时得到通知。

这种方法使用计数信号量,类似于上面的,只是有一点额外的逻辑:

wait(event)
{
    if (semTake(event->csm, timeoutTime) == OK)
    {
        // got it...
    }
    else
    {
        // timeout, report error or something like that....
    }
}

你的信号代码会是这样的:

signal(event)
{
    for (int x = 0; x < event->numberOfWaiters; x++)
    {
        semGive(event->csm);
    }
}

事件的创建是这样的,记住在这个例子中,服务员的数量是恒定的,并且在事件创建时是已知的。您可以使其动态化,但关键是每次事件将要发生时 numberOfWaiters 必须正确,然后解锁器才能解锁互斥锁。

createEvent(numberOfWaiters)
{
    event->numberOfWaiters = numberOfWaiters;
    event->csv = semCCreate(SEM_Q_FIFO, 0);
    return event;
}

您不能对 numberOfWaiters 抱有幻想:DI 会再说一遍:在解锁器解锁互斥锁之前,numberOfWaiters 必须是正确的。要使其动态化(如果需要),您可以添加一个 setNumWaiters(numOfWaiters) 函数,并在解锁器解锁互斥锁之前在 wait_for 函数中调用它,只要它始终正确设置数字即可。

现在到最后一个技巧,如上所述,假设是一个任务负责解锁互斥锁,其余的只是等待信号,这意味着只有一个任务会调用上面的 wait_for() 函数,其余的的任务只是调用 wait(event) 函数。

考虑到这一点,numberOfWaiters 的计算如下:

  • 将调用 wait() 的任务数
  • 调用 wait_for() 的任务加 1

当然,如果你真的需要,你也可以让它变得更复杂,但这很可能会起作用,因为通常 1 个任务会触发一个事件,但许多任务想知道它是否已完成,这就是它所提供的。

但是您的基本流程如下:

init()
{
    event->createEvent(3);
}

eventHandler()
{
    locker l(mutex);
    doEventProcessing();
    signal(event);
}

taskA()
{
    doOperationThatTriggersAnEvent();
    wait_for(mutex);
    eventComplete();
}

taskB()
{
    doWhateverIWant();
    // now I need to know if the event has occurred...
    wait(event);
    coolNowIKnowThatIsDone();
}

taskC()
{
    taskCIsFun();
    wait(event);
    printf("event done!\n");
}

当我写上面的内容时,我觉得所有 OO 概念都已经死了,但希望你明白,实际上 wait 和 wait_for 应该采用相同的参数,或者没有参数,而是成为同一个类的成员,该类也拥有它们的所有数据需要知道......但仍然是它如何工作的概述。

于 2013-10-05T12:34:25.087 回答
3

如果每个等待的任务都在一个单独的二进制信号量上等待,则可以避免竞争条件。这些信号量必须在一个容器中注册,信令任务使用该容器来解除对所有等待任务的阻塞。容器必须受互斥体保护。

wait_for()方法获得一个二进制信号量,等待它并最终将其删除。

void condition::wait_for(mutex& mutex) {
    SEM_ID sem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
    {
        lock l(listeners_mutex);    // assure exclusive access to listeners container
        listeners.push_back(sem);       
    }                               // l's dtor unlocks listeners_mutex again

    unlocker ul(mutex);             // relinquish mutex
    semTake(sem, WAIT_FOREVER);

    {
        lock l(listeners_mutex);
        // remove sem from listeners
        // ...
        semDelete(sem);
    }
}                                   // ul's dtor grabs mutex again

signal()方法遍历所有注册的信号量并解锁它们。

void condition::signal() {
    lock l(listeners_mutex);
    for_each (listeners.begin(), listeners.end(), /* call semGive()... */ )
}

这种方法确保wait_for()永远不会错过任何信号。缺点是需要额外的系统资源。为了避免为每个wait_for()调用创建和销毁信号量,可以使用池。

于 2013-10-09T14:12:21.760 回答
-1

从描述来看,您可能想要实现(或使用)信号量 - 它是一种标准的 CS 算法,其语义类似于 condvars,并且有大量关于如何实现它们的教科书(https://www.google。 com/search?q=信号量+算法)。

一个解释信号量的随机 Google 结果位于:http ://www.cs.cornell.edu/courses/cs414/2007sp/lectures/08-bakery.ppt ‎(见幻灯片 32)。

于 2013-10-05T11:05:08.897 回答