プロジェクト開発——volatile


Vay 0721ブロガーがまとめた良い転送ドアの概要

説明する


volatileキーワードは、オペレーティングシステム、ハードウェア、または他のスレッドなど、いくつかのコンパイラによって不明な要素によって変更できることを宣言するタイプ修飾子です.このキーワード宣言の変数に遭遇すると、コンパイラは変数にアクセスするコードを最適化せず、特殊なアドレスへの安定したアクセスを提供することができます.
元のメモリアドレスへの直接アクセス

たんじゅんさよう


簡単に言えば、コンパイラがコードを最適化することを防止することです.
	XBYTE[2]=0x55;
	XBYTE[2]=0x56;
	XBYTE[2]=0x57;
	XBYTE[2]=0x58;

外部ハードウェアの場合、上記4つの文はそれぞれ異なる操作を表し、4つの異なる動作を生じるが、コンパイラは上記4つの文を最適化し、XBYTE[2]=0 x 58(すなわち、最初の3つの文を無視してマシンコードを1つしか生成しない)と考えている.volatileと入力すると、コンパイラは1つずつコンパイルし、対応するマシンコード(4つのコードを生成)を生成します.
正確には、オプティマイザは、レジスタに保存されたバックアップではなく、この変数を使用するたびに、この変数の値を慎重に再読み取りする必要があります.

適用シーン:


一般的にvolatileは以下のいくつかの場所で使用されています.
1、中断サービスプログラムで修正した他のプログラムが検出するための変数はvolatileを加える必要がある.
2、マルチタスク環境の下で各タスク間で共有するフラグはvolatileを加えるべきである.
3、メモリマッピングのハードウェアレジスタも通常volatile説明を加えなければならない.なぜなら、その読み書きには毎回異なる意味がある可能性があるからである.
また、これらのケースでは、データの整合性(相互に関連付けられたいくつかのフラグが半分読み切られて書き換えられた)を同時に考慮することが多く、1では割り込みをオフにすることで実現でき、2ではタスクスケジューリングを禁止することができ、3ではハードウェアの良好な設計に頼るしかない.

最適化の原理


今回のスレッドでは、1つの変数を読み出すと、アクセス速度を向上させるためにコンパイラが最適化すると、まず1つのレジスタに変数を読み出すことがあります.後で変数値を取ると、レジスタから直接値を取ります.
変数値がこのスレッドで変更されると、変数の新しい値copyがレジスタに同時に入力され、一致を保つ
変数が別のスレッドなどで値を変更した場合、レジスタの値はそれに応じて変更されず、アプリケーションが読み出す値と実際の変数値が一致しない
このレジスタが他のスレッドなどで値を変更した場合、元の変数の値は変更されず、アプリケーションが読み出す値と実際の変数値が一致しない.
変更の可能性1)パラレルデバイスのハードウェアレジスタ
2)割り込みサービスサブルーチンでアクセスされる非自動変数(Non-automatic variables)
3)マルチスレッドアプリケーションにおけるいくつかのタスクによって共有される変数

くりを一つあげる

volatile int i=10;
int a=i;

あるコード、i操作
int b=i;

コンパイラの最適化方法:コンパイラは、iからデータを読み出すコード間のコードがiを操作していないことを2回発見したため、前回読んだデータをbに自動的に配置します.iから読み直すのではなく.
volatileの追加:volatileはiがいつでも変化する可能性があることを指摘し、それを使用するたびにiのアドレスから読み出さなければならないため、コンパイラが生成したアセンブリコードはiのアドレスからデータを読み直してbに置く.
これにより、iがレジスタ変数であるか、ポートデータを表すとエラーが発生しやすいため、volatileは特殊なアドレスへの安定したアクセスを保証することができる.

その他の状況説明


volatile対応の変数は、あなたのプログラム自体が知らない場合に変更される可能性があります.例えば、マルチスレッドのプログラム、共通アクセスのメモリでは、複数のプログラムがこの変数を操作することができます.あなた自身のプログラムは、いつこの変数が変化するかを判定できません.
また、例えば、彼は外部デバイスのある状態に対応しています.外部デバイスが動作している間に、ドライバと割り込みイベントを通じて、システムはこの変数の数値を変更しましたが、あなたのプログラムは知りません.
volatileタイプの変数に対して、システムは彼を使用するたびに対応するメモリから直接抽出し、cacheの元の数値を利用して、その未知のいつ発生する変化に適応することはできません.システムはこのような変数の処理を最適化しません.明らかに、その数値がいつでも変化する可能性があるためです.

栗をもう一つ挙げる

static int i = 0;

int main(void)
{
    //...
    while(1)
    {
       if(i)
            dosomething();
    }
}

/*Interruptserviceroutine.*/
void ISR_2(void)
{
    i=1;
}

プログラムの本意は希望ISR_2割り込み発生時にmainでdosomething関数を呼び出すが,コンパイラはmain関数にiを変更したことがないと判断するため,
iからあるレジスタへの読み取り操作は1回のみ実行され、if判断のたびにこのレジスタの「iコピー」のみが使用され、dosomethingは永遠に呼び出されない可能性があります.変数にvolatile修飾を加えると、コンパイラはこの変数の読み書き操作が最適化されないことを保証します(必ず実行します).この例ではiもこのように説明すべきである.
道理で私は前回タイマーの中で遅延をして、ずっとカウントに入っていません.