10

volatileメモリ マップド ハードウェア レジスタ、ISR、およびマルチスレッド プログラムでの C キーワードの使用について読みました。

1) 登録する

uint8_t volatile * pReg;
while (*pReg == 0) { // do sth } // pReg point to status register 

2) ISR

int volatile flag = 0;
int main() 
{
    while(!flag) { // do sth }
}
interrupt void rx_isr(void)
{
    //change flag
}

3) マルチスレッド

int volatile var = 0;
int task1()
{
    while (var == 0) { // do sth }
}
int task2()
{
    var++;
}

while1)volatile変数がハードウェアから変更されたため、コンパイラは code から作成された変数の変更を認識しない場合があります

しかし、ケース 2) と 3) の場合、なぜ volatile が必要なのですか? どちらの場合も、 variable はglobalと宣言されており、コンパイラはそれが複数の場所で使用されていることを確認できます。whileでは、変数が ではない場合、なぜコンパイラはループを最適化するのでしょうvolatileか?

設計上のコンパイラが「非同期呼び出し」(ISR の場合) またはマルチスレッドを認識していないためですか? しかし、これはあり得ませんよね?

volatileまた、ケース 3) はキーワードなしのマルチスレッドで一般的なプログラムのように見えます。グローバル変数 (volatileキーワードなし)にロックを追加するとします。

int var = 0;
int task1()
{
    lock();   // some mutex
    while (var == 0) { do sth }
    release()
}
int task2()
{
    lock();
    var++;
    release();
}

それは私には十分に正常に見えます。volatileマルチスレッドで本当に必要ですか?マルチスレッド プログラムでの最適化を回避するために修飾子が変数に追加されるのを見たことがないvolatileのはなぜですか?

4

5 に答える 5

10

キーワードを使用する主なポイントは、volatile変数を表す高速な方法として CPU レジスタを使用するコードをコンパイラが生成しないようにすることです。これにより、変数にアクセスするたびに、コンパイルされたコードが RAM 内の正確なメモリ位置にアクセスして、別のエンティティによって変更された可能性のある変数の最新の値を取得するように強制されます。追加volatileすることで、コードがハードウェアや ISR などの他の人によって変数に加えられた変更を認識し、一貫性の問題が発生しないようにします。

キーワードがない場合volatile、コンパイラは変数の内容を RAM から CPU レジスタに 1 回読み取り、そのキャッシュされた値をループまたは関数で使用することにより、より高速なコードを生成しようとします。RAM へのアクセスは、CPU レジスタへのアクセスよりも数十倍遅くなる可能性があります。

volatile項目 1 と 2 の経験はありますが、マルチスレッド環境のように変数を定義する必要はないと思います。ロック/ロック解除メカニズムを追加することは、同期の問題を解決するために必要であり、それが何であるかには関係ありませんvolatile

于 2012-10-05T05:09:58.293 に答える
3
設計上のコンパイラが「非同期呼び出し」(ISR の場合) またはマルチスレッドを認識していないためですか? しかし、これはあり得ませんよね?

はい、その通りです。

C では、コンパイラには同時実行の概念がないため、単一のスレッドからのビューが違いに気付かない限り、メモリ アクセスを並べ替えてキャッシュすることができます。

そのため、揮発性 (変数に対してこの種の最適化をブロックする)、メモリ バリア (すべての変数に対してプログラムの単一ポイントでブロックする)、またはロックなどの他の形式の同期 (通常はメモリ バリアとして機能する) が必要です。

于 2012-10-07T14:52:31.097 に答える
2

コンパイラは、特定の条件がすべて満たされない限り、変数を変更ないことを実際に許可します。その 1 つが揮発性アクセスです。その他は特定のコンパイラ バリアです。

マルチスレッド コードをプログラムする単純な方法を念頭に置くと、実際にはエラーが発生しやすく、未定義の動作と見なされます。正しいマルチスレッド コードがある場合は、最適化がまだ有効であるか (task1ループがまだ UB であり、スローされる可能性がある final のように)、同期プリミティブに必要なバリア含まれているか (通常、いくつかのアトミックの内部にある)変数)。

まとめとして、マルチスレッドの例の修正版を次に示します。

 for (;;)
 {
     lock();
     if (var != 0) { unlock(); break; }
     unlock();
 }

関数の実装によりunlock()、ループを最適化して除去できないようにするコンパイラ バリアが導入されます。

于 2012-10-05T02:34:15.807 に答える
0

バリアを使用すると、マルチスレッド ソフトウェアで揮発性変数を自由に回避できます。Linux カーネル ソースで多くの例を見つけることができます。また、volatile の代わりにバリアを使用すると、コンパイラはより効率的なコードを生成できます。

于 2012-10-05T12:43:41.590 に答える