Linux核心開發之並發控制(一)

來源:互聯網
上載者:User

“小濤,你說十一黃金周,火車站,飛機場那些售票系統咋沒一個宕掉的呢。你不宕掉也沒關係,來兩個賣錯票的,說不定哥就去上海看世博,去北京看青梅竹馬的表妹了…”小王抱怨道。

“暈死..哥鄙視你,你說都老大不小的人了,怎麼腦子裡天天都是MM之類的事了,能不能有點男子氣概啊..”。

“靠,能跟你比啊,你是飽漢不知餓漢饑,要是像你一樣十一和…”

  "嗯,啊,哼哼.."沒等他說完,我趕忙塞了雙臭襪子(哪天的也記不住了)。“得得,I 服了 you,ok”。

“不過話說回來,小王,你說的還真是個問題,想想這樣的問題,你和GF兩個要去西湖看白娘子,偏偏遇到老天跟你過不去,就只剩下一張票了,你和GF兩個誰去…“

"不是吧,我這命苦,好不容易有個GF,應該不會出現的,呵呵,如果出現…那好辦,我和她商量好,咱們一起在晚上12:00一起買票,這樣我們兩邊的售票員怎麼看都各自有一張票,我們兩個就可以一起了"小王狡黠的笑著。

“笨,我一口一個鹽水噴死你,分明一張票,你們兩個同一個時間去兩個不同的售票點去買,它還是一張票,怎麼可能說去兩個不同地方,兩個售票員都看到有一張票,然後就把這唯一的一張同時賣給了你們兩個人”我打斷到。"算了,看在室友兼我的最忠實狗仔隊員的身份,哥就傳授一招只傳MM的絕學----Linux裝置驅動程式之並發控制"。

聽說過並發沒,那你肯定聽說過競爭,比如競爭上崗,還有你最熟悉的追MM,這也是競爭。那麼

並發(concurrency)就是說多個執行單元同時,並行被執行,這多個單元卻不巧要同時訪問一些資源。這其中要分三種情況:

    

正所謂:道高一尺,魔高一丈,你孫悟空有72變,人家二郎神還有73變不是。有問題,沒關係,找小濤哥不是..呵呵。現在就教你幾招以備不時只需:

大家不是要競爭嗎,那好,總體原則就是不讓你競爭:保證一個執行單元在訪問共用資源的時候,其他的執行單元被禁止訪問,將競爭扼殺在萌芽狀態。這就是傳說中的對共用資源的互斥訪問。

出招表一:中斷屏蔽(可以保證正在執行的核心執行路徑不被中斷處理常式搶佔,由於Linux核心的進程調度都依賴中斷來實現,核心搶佔進程之間的競態就不存在了)

    使用方法: local_irq_disable()  //屏蔽中斷                                   說明:local_irq_disable()和local_irq_enable()都只能禁止和使能本CPU內的中斷

                 ….                                                                               並不能解決SMP多CPU引發的競爭。

                 critical section  //臨界區

                 ….

                 local_irq_enable()  //開中斷 

    與local_irq_disable()不同,local_irq_save(flags)除了進行禁止中斷操作以外,還保證目前CPU的中斷位資訊,local_irq_save(flags)進行相反的操作。

    致命弱點: 由於Linux系統的非同步I/O,進程調度等很多重要操作都依賴於中斷,在屏蔽中斷期間所有的中斷都無法處理,因此長時間屏蔽中斷是很危險的,有可能造

                成資料丟失甚至系統奔潰。 

出招表二:原子操作(忘了是物理還是化學老師拉著我的手說:原子是最小的,不能再分的東西.看多形象,執行過程不能被別的代碼路徑中斷的操作就是原子操作,還

             想跟我競爭,門都沒有)。 分為整形原子和位原子操作。 

    使用方法一:整形原子操作

                    1)設定原子變數的值

              void atomic_set(atomic_t *v, int i);//設定原子變數的值為i            atomic_t v = ATOMIC_INIT(0);//定義原子變數v並初始化為0
             2)擷取原子變數的值
              atomic_read(atomic_t *v);//返回原子變數的值
                    3)原子變數加/減
              void atomic_add(int i,atomic_t *v);  //原子變數增加i            void atomic_sub(int i,atomic_t *v);  //原子變數減少i
            4)原子變數自增/自減
              void atomic_inc(atomic_t *v);  //原子變數加1            void atomic_dec(atomic_t *v);  //原子變數減1
            5)操作並測試
              int atomic_inc_and_test(atomic_t *v);//這些操作對原子變數執行自增,自減,減操作後測試是否為0,是返回true,否則返回false            int atomic_dec_and_test(atomic_t *v);            int atomic_sub_and_test(int i, atomic_t *v);
                   6)操作並返回
              int atomic_add_return(int i,atomic_t *v);  //這些操作對原子變數進行對應操作,並返回新的值。              int atomic_sub_return(int i, atomic_t *v);            int atomic_inc_return(atomic *v);            int atomic_dec_return(atomic_t *v);
   使用方法二:位原子操作。
             1)設定位
              void set_bit(nr, void *addr);  //設定addr地址的第nr位,所謂設定位即將位寫為1           2)清除位
              void clear_bit(nr,void *addr);  //清除addr地址的第nr位,所謂清除位即將位寫為0           3)改變位
              void change_bit(nr,void *addr);  //對addr地址的第nr位反置             4)測試位
              void test_bit(nr, void  *addr);  //返回addr地址的第nr位                      5)測試並操作位
              int test_and_set_bit(nr, void *addr);            int test_and_clear_bit(nr, void *addr);            int test_and_change_bit(nr, void *addr);
                 光說不練,不是好漢。這誰說的呢,咋就是記不得呢,看段代碼:
              static atomic_t ato_avi = ATOMIC_INIT(1); //定義原子變數              static int ato_open(struct inode *inode, struct file *filp)              {              ...              if (!atomic_dec_and_test(&ato_avi))              {                atomic_inc(&ato_avi);                return = - EBUSY;  //已經開啟                }              ..              return 0;  //已經開啟              }            static int ato_release(struct inode *inode, struct file *filp)            {              atomic_inc(&ato_avi);              return 0;            }
 出招表三:自旋鎖。正如其名,CPU上將要執行的代碼將會執行一個測試並設定某個記憶體變數的原子操作,若測試結果表明鎖已經空閑,則程式獲得這個自旋
         鎖繼續運行;若仍被佔用,則程式將在一個小的迴圈內重複測試這個"測試並設定"的操作.這就是自旋。
使用方法:1)spinlock_t spin; //定義自旋鎖
             2)spin_lock_init(lock);  //初始化自旋鎖
             3)spin_lock(lock);  //成功獲得自旋鎖立即返回,否則自旋在那裡直到該自旋鎖的保持者釋放
               spin_trylock(lock); //成功獲得自旋鎖立即返回真,否則返回假,而不是像上一個那樣"在原地打轉"
           4)spin_unlock(lock);//釋放自旋鎖
    自旋鎖一般像下邊這樣使用:
      spinlock_t lock;     spin_lock_init(&lock);     spin_lock (&lock);     ....//臨界區      spin_unlock(&lock);
     還記的前邊說的第一招:中斷屏蔽中致命的弱點步,自旋鎖就是針對SMP或單個CPU但核心可搶佔的情況,對於但CPU和核心不可搶佔的系統,自旋鎖退化為空白操作。還有就是自旋鎖解決了臨界區不受別的CPU和本CPU內的搶佔進程打擾,但是得到鎖的代碼路徑在執行臨界區的時候還可能受到中斷和底半部的影響。     那咋辦呢,天要下雨,娘要嫁人,可二郎神的就是比你孫悟空多了一變,你能咋辦,打一架?打不過..所以說嘛,Linux社區的開發人員們早想到了辦法:在自旋鎖的基礎上進行衍生,具體是怎麼回事,且聽下回分解(每次說這句話是感覺好爽,這難道就是高手耍酷的樂趣..)..
 

                     

 

                   


 
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.