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) コードは次のようになります。

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 は、イベントの作成時に msgQCreate を呼び出して作成される MSG_Q_ID であり、sigMsg のデータはユーザーによって定義されます... しかし、データの単なるランダムなバイトである場合もあれば、誰が信号を送ったか、または知っておくと便利なその他の情報を含む、よりインテリジェントな構造。

複数のウェイターの更新、これは少しトリッキーです: したがって、物事を単純化するためにいくつかの仮定があります

  1. 保留中のタスクの数は、イベントの作成時に判明しており、一定です。
  2. ミューテックスのロックを解除してもよいかどうかを常に示すタスクが 1 つあります。他のすべてのタスクは、イベントが通知/完了されたときに通知を受け取るだけです。

このアプローチでは、上記と同様にカウント セマフォを使用しますが、ロジックが少し追加されています。

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

イベントの作成はこのようなものです。この例では、ウェイターの数は一定であり、イベントの作成時にわかっていることを思い出してください。動的にすることもできますが、重要なのは、イベントが発生するたびに、unlocker がミューテックスのロックを解除する前に numberOfWaiters が正しくなければならないということです。

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

numberOfWaiters について意地悪になることはできません:DI はもう一度言います: ロック解除ツールがミューテックスのロックを解除する前に、numberOfWaiters は正しくなければなりません。動的にする (それが必要な場合) には、setNumWaiters(numOfWaiters) 関数を追加し、ロック解除ツールがミューテックスのロックを解除する前に、wait_for 関数でそれを呼び出すことができます。

最後のトリックとして、上記で述べたように、1 つのタスクがミューテックスのロック解除を担当し、残りのタスクはシグナルを待機するという前提があります。つまり、1 つのタスクだけが上記の 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");
}

上記を書くと、すべてのオブジェクト指向の概念が死んでいるように感じますが、うまくいけば、実際には 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

説明から、セマフォを実装 (または使用) する必要があるように見えます。これは、condvars に似たセマンティクスを持つ標準の CS アルゴリズムであり、それらの実装方法に関する教科書がたくさんあります ( 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 に答える