<acronym id="xonnx"></acronym>
      <td id="xonnx"></td>
    1. <pre id="xonnx"></pre>

      1. 專注電子技術學習與研究
        當前位置:單片機教程網 >> MCU設計實例 >> 瀏覽文章

        volatile與嵌入式程序員千絲萬縷的聯系

        作者:CHEERS   來源:RTOS-CHEERS   點擊數:  更新時間:2014年06月08日   【字體:

               像大家更熟悉的const一樣,volatile是一個類型修飾符(type specifier)。它是被設計用來修飾被不同線程訪問和修改的變量。如果沒有volatile,基本上會導致這樣的結果:要么無法編寫多線程程序,要么編譯器失去大量優化的機會。

                一個嵌入式軟件工程師經常打交道的有芯片、中斷、RTOS,關系到整個系統協調有序運轉,多線程顧名思義就是多任務,就像一個人吃飯的時候要去用大腦控制手夾食物,然后要張開嘴送進食物,最后咀嚼咽食,多道工序嚴密配合才能吃飯,同樣智能系統的CPU也是一樣,這個過程一個系統要處理實現一件事,往往需要高效的處理方式,需要多任務處理,多任務之間更需要合理的調度方式,一般都需要嵌入一種操作系統,這時候volatile的作用體現出來了,定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地在內存中重新讀取這個變量的值,而不是使用保存在寄存器里的備份。如果沒有volatile那整個系統將會亂成一鍋粥,根本無法實現目標。

              很多人會問為什么啊,這個關鍵在于多任務處理這個事情的時候會共享系統的一些資源,包括一些重要的變量,這就出現了問題,編譯器在編譯程序后都會進行優化,程序運行時本次任務訪問CPU時調用一些參數變量,為提高存取速度,編譯器優化時有時會先把變量讀取到一個寄存器中,進行緩存;以后再取變量值時,就直接從寄存器中取值;當變量值在本線程里改變時,會同時把變量的新值copy到該寄存器中,以便保持一致,當變量在因別的線程而改變了值,這個線程中該寄存器的值不會相應改變,從而造成應用程序以后讀取的值和實際的變量值不一致,當該寄存器在因此線程而改變了值,原內存中變量的值不會改變,從而造成應用程序讀取的值和實際的變量值不一致,所以會發生意想不到的危險,有些人會問,那這個值為什么會變啊,我只想告訴你這是一個嵌入式系統,比如說伺服電機的控制系統,采樣得到的轉速和位置會不會隨時改變,可能因為在調速進程中改變,可能在會在制動進程中改變,這個系統的處理必須實時性極強,不然不可能實現高精度控制!

        舉一個不太準確的例子:發薪資時,會計每次都把員工叫來登記他們的銀行卡號;一次會計為了省事,沒有即時登記,用了以前登記的銀行卡號;剛好一個員工的銀行卡丟了,已掛失該銀行卡號;從而造成該員工領不到工資

        員工 -- 原始變量地址

        銀行卡號 -- 原始變量在寄存器的備份

        另外很多人可能還是對volatile認識不清,我想說,用volatile修飾的全局變量,不是因為被volatile修飾 了就會發生改變,不修飾就不會改變,這個變量的改變與否是外因引起的,比如像伺服控制中的轉速,抑或是PID 調節增量,無論這些變量是否被修飾,他都會因為外部原因運行一些線程的時候改變,而volatile的作用就是讓它相對性不變,始終在上一個線程執行完后讓當前執行線程調用這個數據的時候,都會小心的從內存地址中調用它最實時準確的數據,保證系統運行流暢,而不是去選擇CASH--緩沖寄存器中保留的那個原始備份,若果選擇系統選擇備份文件,可能導致上邊例子中說的“領不到錢”的尷尬,就是用了一個錯誤的數據執行了任務,如果伺服控制中出現這種情況可能出現電機不可控場面,后果不堪設想,所以對于嵌入式軟件人員來說系統的協調處理和實時性必須嚴格。

        這種變量經常會出現在:

        1). 并行設備的硬件寄存器

        2). 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)

        3). 多線程應用中被幾個任務共享的變量

        補充:volatile應該解釋為“直接存取原始內存地址”比較合適,“易變的”這種解釋簡直有點誤導人;

        “易變”是因為外在因素引起的,像多線程,中斷等,并不是因為用volatile修飾了的變量就是“易變”了,假如沒有外因,即使用volatile定義,它也不會變化;

        volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如:操作系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。

        這里它與const不同,const一般是對一些變量進行保護,它修飾的一些變量一般不讓改變,主要針對是多個程序員在進行一個項目的開發時,各自的工作不同,不同的人為了保證自己這部分功能的絕對可靠,往往需要用const來保護自己的某些對象,防止別的程序員無意中改變它。

        下面給出三個問題:

        1). 一個參數既可以是const還可以是volatile嗎?解釋為什么。

        2). 一個指針可以是volatile 嗎?解釋為什么。

        3). 下面的函數被用來計算某個整數的平方,它能實現預期設計目標嗎?如果不能,試回答存在什么問題:

        1

        2

        3

        4

        int square( volatile int *ptr )

        {

            return *ptr * *ptr;

        }

        下面是答案:

        1). 是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。

        2). 是的。盡管這并不很常見。一個例子是當一個中斷服務子程序修改一個指向一個buffer的指針時。

        3). 這段代碼是個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:

        1

        2

        3

        4

        5

        6

        7

        int square( volatile int *ptr )

        {

            int a,b;

            a = *ptr;

            b = *ptr;

            return a * b;

        }

        由于*ptr的值可能在兩次取值語句之間發生改變,因此a和b可能是不同的。結果,這段代碼可能返回的不是你所期望的平方值!正確的代碼如下:

        1

        2

        3

        4

        5

        6

        long square( volatile int *ptr )

        {

            int a;

            a = *ptr;

            return a * a;

        }

        同樣在網上看到兩個比較好的例子,說volatile的用法,在這里和大家分享:

         

        嵌入式編程中經常用到 volatile這個關鍵字,的用法可以歸結為以下兩點:

         

        一:告訴compiler不能做任何優化

         

           比如要往某一地址送兩指令: 

           int *ip =...; //設備地址 

           *ip = 1; //第一個指令 

           *ip = 2; //第二個指令 

           以上程序compiler可能做優化而成: 

           int *ip = ...; 

           *ip = 2; 

           結果第一個指令丟失。如果用volatile, compiler就不允許做任何的優化,從而保證程序的原意: 

           volatile int *ip = ...; 

           *ip = 1; 

           *ip = 2; 

           即使你要compiler做優化,它也不會把兩次付值語句間化為一。它只能做其它的優化。

         

        這對device driver程序員很有用。

         

        二:表示用volatile定義的變量會在程序外被改變,每次都必須從內存中讀取,

         

        而不能把他放在cache或寄存器中重復使用。

         

           如   volatile char a;   

                a=0; 

               while(!a){ 

        //do some things;   

               }   

               doother(); 

           如果沒有 volatile doother()不會被執行。

         

        以上個人見解,有不對的地方,希望各位博友指正,和大家一起進步一起探討,謝謝

         

        ----2014年3月21日14點20

         

        ----RTOS-CHEERS

         
         
        關閉窗口

        相關文章

        欧美性色欧美精品视频,99热这里只有精品mp4,日韩高清亚洲日韩精品一区二区,2020国自产拍精品高潮