0

組み込みシステムでは、さまざまな正当な理由から、トレンドはまだ完全にマルチコアプロセッサに移行していません。

したがって、ユニプロセッサシステムに関して、さまざまなメカニズムとマルチスレッド機能を使用した同期動作を理解することは依然として重要です。また、インタビューに直面するたびに、ユニプロセッサシステムでの特定のCプログラムの動作について質問されます。

それで、ユニプロセッサシステムでサンプルCプログラムを分析して、自宅での動作を確認したい場合、どうすればよいでしょうか。自宅のCPUにはCorei3プロセッサが搭載されています。1つのCPUだけを考慮して、OSまたはコンパイラに動作を強制的にチェックするように依頼する方法はありますか?

例:

int x=0;

スニペット-1

    while(x);
    x++;

Snippet-2

    while(!x);
    x--;

ユニプロセッサシステムを考えて、Cプログラムの動作を確認したい

  • スニペット1とスニペット2は複数のスレッドにあります
  • スニペット1はメインプログラムにあり、スニペット2はISRにあります
  • Snippet1とSnippet2は両方とも2つの異なるISRにあります(割り込みが優先的にキャッチされることを考慮してください。また、ISR内で、より高い優先度の新しい割り込みがある場合、最も優先度の高い割り込みがすぐに実行されます-例:リセット)

上記の質問での私の主な目標は、デッドロックが発生するかどうかを特定することであり、存在する場合は解決策を特定する必要があります。あなたの考えを入れてください。ありがとう。

4

4 に答える 4

2

必要に応じて「maxcpus」カーネルパラメータを設定してLinuxを起動するだけです。これは、SMPLinuxカーネルが使用する必要のあるプロセッサーの最大数を指定します。たとえば、maxcpus=1です。

于 2012-08-06T07:59:05.097 に答える
0

i3 で 1 つのコアのみを有効にして新しいカーネルをコンパイルして、ユニプロセッサ システムを作成することをお勧めします (明らかに、パフォーマンスは低下します)。

次のリンクの手順に従ってください。

http://www.cyberciti.biz/tips/compiling-linux-kernel-26.html

構成中に、

プロセッサーのタイプと機能に移動-->

Symmetric Multiprocessing Supportのチェックを外します。

ユニプロセッサシステムを作成するための指示を学びます。

ここに画像の説明を入力

于 2012-08-06T07:36:01.850 に答える
0

これは、Windows で簡単に実行できます。Windows タスク マネージャーで、[プロセス] タブをクリックします。プロセス リストで目的のプログラムを見つけて右クリックし、ドロップダウン メニューで [アフィニティの設定...] をクリックします。これにより、プロセスの実行に使用できるプロセッサを設定できるダイアログが表示されます。1 つを除くすべてのプロセッサのチェックを外すと、プログラムはその 1 つのプロセッサでのみ実行されます。残念ながら、プログラムを起動するたびにこれを行う必要があります。

于 2012-08-06T13:21:19.617 に答える
0

ユニプロセッサ システムとマルチプロセッサ システムの違いは、プログラムが既に無効になっている領域 (標準では「未定義の動作を引き起こす」) だけです。

サンプル プログラムは、volatile修飾子を使用せず、他の ISR の同時実行を防ぐことなく、ISR から共有変数を変更します。

x前者には、変更できないと仮定してコンパイラがコードを最適化する可能性があるという効果があります。

while(x);
x++;

次の手順を実行するアセンブラー命令にコンパイルすることが期待されます。

loop:
    read x into register0
    test register0 != 0
    if true => goto loop
    increment register0
    write register0 to x

最適化中に、コンパイラは が でxはないことを認識しvolatile、メモリ アクセスをループの外に移動します。

    read x into register0
loop:
    test register0 != 0
    if true => goto loop
    increment register0
    write register0 to x

その後、register0ループの実行中に変更されないことがわかるため、テストをループの外に移動することもできます。

    read x into register0
    test register0 != 0
loop:
    if true => goto loop
    increment register0
    write register0 to x

一部のコンパイラは、追加のステップに進み、テストを反転して、ループ内で安価な命令を使用できるようにします。

    read x into register0
    test register0 != 0
    if false => goto skip
loop:
    goto loop
skip:
    increment register0
    write register0 to x

明らかに、これはあなたが望むものではありません。

もう 1 つの問題は、IRQ の優先度レベルによって ISR が相互に割り込む場合と割り込まない場合があることと、マルチプロセッサ システムでは複数の ISR が異なるプロセッサで同時に実行されている可能性があることです。

コードで が適切に使用されていると仮定すると、volatile優先度の高い割り込みとタスクのスケジューリングが任意の 2 つの命令間で発生する可能性があると仮定することで、理論上の動作を検証できます。スニペットのアセンブラー疑似コードは

    push register0
loop:
    load x into register0
    test register0 != 0
    if true => goto loop
    write 1 to x            // can you see what I did there?
    pop register0

    push register0
loop:
    load x into register0
    test register0 == 0
    if true => goto loop
    decrement register0
    write register0 to x
    pop register0

考えられる星座は

CPU1    push register0
CPU2    push register0
CPU1    load x into register0 [value = 0]
CPU2    load x into register0 [value = 0]
CPU1    test register0 != 0 [false]
CPU2    test register0 == 0 [true]
CPU1    if true => goto loop [not taken]
CPU2    if true => goto loop [taken]
CPU1    increment register0 [value = 1]
CPU2    read x into register0 [value = 0]
CPU1    write register0 to x [value = 1]
CPU2    test register0 == 0 [true]
CPU1    pop register0
CPU2    if true => goto loop [taken]
CPU1    ...
CPU2    read x into register0 [value = 1]
CPU1    ...
CPU2    test register0 == 0 [false]
CPU1    ...
CPU2    if true => goto loop [not taken]
CPU1    ...
CPU2    decrement register0 [value = 0]
CPU1    ...
CPU2    write register0 to x [value = 0]
CPU1    ...
CPU2    pop register0

これを理論的に解決する通常の方法は、特定の仮定が保持されている命令の範囲を特定し、同時実行に直面してこれらの仮定がどのように間違っているかを調べることです。

    // precondition: address at stack pointer is unused
    // precondition: decrementing the stack pointer will not bring us to a used address
    push register0
    // postcondition: address at stack pointer is unused
    // postcondition: register0 is unused

これらの条件が満たされるためには、現在のスタック ポインターより下のすべてのメモリは使用されないというシステム全体の規則があります。このように、ISR は、スタックへのデータのプッシュが許可されていると常に想定できます。データの書き込みとスタック ポインターのデクリメントはアトミック操作であることに注意してください。別の割り込みがここに到着すると、そのデータもスタックにプッシュされますが、別のアドレスが使用されます。

loop:
    // precondition: register0 is unused
    read x into register0
    // begin assumption: register0 contains a copy of x

これがどこに向かっているのかがわかると思います。ここから中断されて の値がx変化すると、この仮定は間違ったものになります。

    test register0 != 0
    // postcondition: processor status contains result of (register0 != 0)

    if true => goto loop
    // postcondition[true]: register0 != 0
    // postcondition[false]: register0 == 0

これは、ループを終了する唯一の方法が when であることを証明した場所register0 == 0です。したがって:

    increment register0
    write register0 to x
    // end assumption: register0 contains a copy of x

に拡張することができます

    // precondition: register0 is 0
    increment register0
    // postcondition: register0 is 1

    // precondition: register0 is 1
    write register0 to x
    // end assumption: register0 contains a copy of x

これは次のように簡略化できます

    // precondition: register0 is 0
    // modified assumption: register0 contains a copy of x, minus one
    // due to precondition, x needs to be written as 1
    write 1 to x
    // end assumption: register0 contains a copy of x, minus one

最後の命令はレジスタ 0 を使用しないため、「想定終了」ステートメントは、削除されたincrement操作の前に上に移動できます。

    // end assumption: register0 contains a copy of x
    // precondition: register0 is 0
    write 1 to x

前提条件はループから簡単に証明されます

    // precondition: stack pointer points at address below where we placed the saved copy
    // precondition: memory below the stack pointer is unused
    pop register0
    // postcondition: stack pointer points at unused memory
    // postcondition: stack pointer points at the same address as before the push
    // postcondition: register0 is restored

したがって、仮定に違反した場合、つまり、x読み取り時と新しい値が書き戻された時の間に の値が変更された場合、および条件が満たされない場合を処理する必要があります。それを作成できるコードを呼び出すことができないためです。

どちらのケースも、ユニプロセッサとマルチプロセッサの設計で発生する可能性があります。違いは、マルチプロセッサにはいくつかのエラーを隠す追加の障害モードがあることです。

ユニプロセッサの障害モードは次のとおりです。

  • ISR1 読み取り
  • ISR2 読み取り (ISR2 の方が優先度が高い)
  • ISR2 書き込み
  • ISR1 書き込み

  • ISR2 はビジー ループに入り、状態が変化するのを待ちます
  • ISR2 (優先順位が高い) がアクティブであるため、ISR1 はブロックされます。

ケース 1 は次と同等です。

  • メインループ読み取り
  • ISR読み取り
  • ISR 書き込み
  • メインループ書き込み

  • スレッド 1 読み取り
  • スレッド 2 読み取り
  • スレッド 2 書き込み
  • スレッド 1 書き込み

ケース 2 は次と同等です。

  • ISR はビジー ループに入り、状態が変化するのを待ちます
  • ISR がアクティブであるため、メインループがブロックされている

マルチスレッドの場合、スレッドは互いにブロックしないため、デッドロックは発生しません。

マルチプロセッサ (およびデッドロックではなくマルチスレッドの場合) の場合、追加の障害モードがあります。

  • ISR1 読み取り
  • ISR2 読み取り
  • ISR1 書き込み
  • ISR2 書き込み

メインループでは発生しませんが (IRQ は常に優先され、メインループをブロックするため)、複数のスレッドでは発生します。

  • スレッド 1 読み取り
  • スレッド 2 読み取り
  • スレッド 1 書き込み
  • スレッド 2 書き込み

これらすべてのケースに対する解決策は、 のコピーを含む仮定を保持する必要があるクリティカル セクションで、他のすべての人がロックアウトされるようにするか、エラーが事後に検出され、適切に処理されるようにすることです。register0x

これらはどちらも実際には同等です。変数の現在の状態を取得し、新しい状態を一度に書き込む (または、古い状態がまだ残っているという条件で新しい状態を書き込む) アトミック命令が必要です。無傷)。次に、誰かがクリティカル セクション内にいるかどうかを表す別の変数を使用するか、変数に対してこの特別な命令をx直接使用できます。

于 2012-08-06T13:03:18.633 に答える