天天看點

[轉] 同步互斥阻塞

1. 原子操作

原子操作指的是在執行過程中不會被别的代碼路徑所中斷的操作。

常用原子操作函數舉例:

atomic_t v = ATOMIC_INIT(0);     //定義原子變量v并初始化為0

atomic_read(atomic_t *v);        //傳回原子變量的值

void atomic_inc(atomic_t *v);    //原子變量增加1

void atomic_dec(atomic_t *v);    //原子變量減少1

int atomic_dec_and_test(atomic_t *v); //自減操作後測試其是否為0,為0則傳回true,否則傳回false。

2. 信号量

信号量(semaphore)是用于保護臨界區的一種常用方法,隻有得到信号量的程序才能執行臨界區代碼。

當擷取不到信号量時,程序進入休眠等待狀态。

定義信号量

struct semaphore sem;

初始化信号量

void sema_init (struct semaphore *sem, int val);

void init_MUTEX(struct semaphore *sem);//初始化為0

static DECLARE_MUTEX(button_lock);     //定義互斥鎖

獲得信号量

void down(struct semaphore * sem);   //休眠

int down_interruptible(struct semaphore * sem); 

int down_trylock(struct semaphore * sem);

釋放信号量

void up(struct semaphore * sem);  // 喚醒

static DECLARE_MUTEX(button_lock);     //定義互斥鎖

驅動 open中

if (file->f_flags & O_NONBLOCK)

    {

        if (down_trylock(&button_lock))   //如果沒有阻塞的話,應用層一讀就會立馬傳回

            return -EBUSY;

    }

    else

    {

        down(&button_lock);

    }

驅動 release

up(&button_lock);

3. 阻塞

阻塞操作    

是指在執行裝置操作時若不能獲得資源則挂起程序,直到滿足可操作的條件後再進行操作。

被挂起的程序進入休眠狀态,被從排程器的運作隊列移走,直到等待的條件被滿足。

非阻塞操作  

程序在不能進行裝置操作時并不挂起,它或者放棄,或者不停地查詢,直至可以進行操作為止。

fd = open("...", O_RDWR | O_NONBLOCK); 

---------------------------------------------------------------------------------------------

come from: https://blog.csdn.net/xiaodingqq/article/details/80353639

并發(concurrency):在作業系統中,是指一個時間段中有幾個程式都處于已啟動運作到運作完畢之間,且這幾個程式都是在同一個處理機上運作。其中兩種并發關系分别是同步和互斥。

所謂互斥,是指分布在不同程序之間的若幹程式片斷,當某個程序運作其中一個程式片段時,其它程序就不能運作它們之中的任一程式片段,隻能等到該程序運作完這個程式片段後才可以運作。 

所謂同步,是指分布在不同程序之間的若幹程式片斷,它們的運作必須嚴格按照規定的 某種先後次序來運作,這種先後次序依賴于要完成的特定的任務。 顯然,同步是一種更為複雜的互斥,而互斥是一種特殊的同步。也就是說互斥是兩個線程之間不可以同時運作,他們會互相排斥,必須等待一個線程運作完畢,另一個才能運作,而同步也是不能同時運作,但他是必須要安照某種次序來運作相應的線程(也是一種互斥)! 

總結: 

互斥:是指某一資源同時隻允許一個通路者對其進行通路,具有唯一性和排它性。但互斥無法限制通路者對資源的通路順序,即通路是無序的。   

同步:是指在互斥的基礎上(大多數情況),通過其它機制實作通路者對資源的有序通路。在大多數情況下,同步已經實作了互斥,特别是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個通路者同時通路資源。

異步(asynchronous):異步和同步是相對的,同步就是順序執行,執行完一個再執行下一個,需要等待、協調運作。異步就是彼此獨立,在等待某事件的過程中繼續做自己的事,不需要等待這一事件完成後再工作。線程就是實作異步的一個方式。異步是讓調用方法的主線程不需要同步等待另一線程的完成,進而可以讓主線程幹其它的事情。 

同步和異步是針對應用程式和核心的互動而言的, 

同步指的是使用者程序觸發IO操作并等待或者輪詢的去檢視IO操作是否就緒,而異步是指使用者程序觸發IO操作以後便開始做自己的事情, 

而當IO操作已經完成的時候會得到IO完成的通知(異步的特點就是通知)。 

異步和多線程并不是一個同等關系,異步是最終目的,多線程隻是我們實作異步的一種手段。異步是當一個調用請求發送給被調用者,而調用者不用等待其結果的傳回而可以做其它的事情。實作異步可以采用多線程技術或則交給另外的程序來處理。

3.阻塞,非阻塞 

而阻塞和非阻塞是針對于程序在通路資料的時候,根據IO操作的就緒狀态來采取的不同方式,說白了是一種讀取或者寫入操作函數的實作方式,阻塞方式下讀取或者寫入函數将一直等待,而非阻塞方式下,讀取或者寫入函數會立即傳回一個狀态值。 

一般來說IO模型可以分為:同步阻塞,同步非阻塞,異步阻塞,異步非阻塞。

同步阻塞IO 

在此種方式下,使用者程序在發起一個IO操作以後,必須等待IO操作的完成,隻有當真正完成了IO操作以後,使用者程序才能運作。

同步非阻塞IO 

在此種方式下,使用者程序發起一個IO操作以後可傳回做其它事情, 

但是使用者程序需要時不時的詢問IO操作是否就緒,這就要求使用者程序不停的去詢問,進而引入不必要的CPU資源浪費。

異步阻塞IO 

此種方式下是指應用發起一個IO操作以後,不等待核心IO操作的完成, 

等核心完成IO操作以後會通知應用程式,這其實就是同步和異步最關鍵的差別同步必須等待或者主動的去詢問IO是否完成,

異步非阻塞IO 

在此種模式下,使用者程序隻需要發起一個IO操作然後立即傳回, 

等IO操作真正的完成以後,應用程式會得到IO操作完成的通知, 

此時使用者程序隻需要對資料進行處理就好了,不需要進行實際的IO讀寫操作因為真正的IO讀取或者寫入操作已經由核心完成了。

同步與異步是對應的,它們是線程之間的關系,兩個線程之間要麼是同步的,要麼是異步的。 

阻塞與非阻塞是對同一個線程來說的,在某個時刻,線程要麼處于阻塞,要麼處于非阻塞。 

阻塞是使用同步機制的結果,非阻塞則是使用異步機制的結果。

---------------------------------------------------------------------------------------------------------------------------

come from:https://blog.csdn.net/yikai2009/article/details/8650259

信号量:

          Linux 核心的信号量在概念和原理上與使用者态的信号量是一樣的,

          但是它不能在核心之外使用,隻能在核心中,它是一種睡眠鎖.

          如果有一個任務想要獲得 已經被占用 的信号量時,信号量會将這個程序放入一個等待隊列, 然後讓其睡眠,

         當持有信号量的程序将信号釋放後,處于等待隊列中的任務将被喚醒,并讓其獲得信号量.

          1,信号量在建立時需要設定一個初始值,表示允許有幾個任務同時通路該信号量保護的資源.

                初始值為 1 就變成互斥鎖 (  Mutex ) ,即同時隻能有一個任務可以通路信号量保護的資源.

          2,當任務通路完被信号量保護的共享資源後,必須釋放信号量,釋放信号量通過把信号量的值加 1 實作.

                 如果釋放後信号量的值為非正數,表明有任務等待目前信号量,是以要喚醒等待該信号量的任務.

定義信号量 struct   semaphore:

          信号量的實作也是與體系結構相關的,定義在 < asm/semaphore.h > 中,

          struct   semaphore 類型用來表示信号量.

          eg: struct   semaphore   sem;

初始化信号量 sema_init   init_MUTEX   init_MUTEX_LOCKED :

          void  sema_init  ( struct  semaphore *sem , int  val )

          該函數用于初始化設定 信号量 的初值,它設定信号量 sem 的值 為 val .

          初始化一互斥鎖 MUTEX:

          void  init_MUTEX ( struct  semaphore  *sem )

          該函數用于初始化一個互斥鎖,即它把信号量 sem 的值設定為 1.

          void  init_MUTEX_LOCKED ( struct  semaphore *sem)

          該函數也用于初始化一個互斥鎖,但是它把信号量 sem 的值設定為 0,即一開始就處在已鎖狀态.

初始化 定義一步完成 DECLARE_MUTEX  DECLARE_MUTEX_LOCKED  :

          定義與初始化的工作可由如下宏一步完成:

          DECLARE_MUTEX ( name )

          定義一個信号量 name,并初始化她的值為 1 .

          DECLARE_MUTEX_LOCKED ( name )

          定義一個信号量 name ,但把它的初始化設定為 0 ,即鎖在建立時就處在已鎖狀态.

擷取信号量  down  down_interruptible  down_trylock  :

        1,  void   down ( struct  semaphore * sem )

          獲得信号量 sem . 可能會導緻程序睡眠,是以不能在中斷上下文使用該函數.

          該函數将把 sem 的值減 1,如果信号量 sem 的值非負,就直接傳回,

          否則調用者将被挂起,直到别的任務釋放該信号量才能繼續使用.

        2,  int  down_interruptible ( struct  semaphore * sem )

          擷取信号量 sem . 如果信号量不可用,程序将被置為 TASK_INTERRUPTIBLE 類型的睡眠狀态.

          該函數由傳回值來區分是正常傳回還是被信号中斷傳回,

          如果傳回 0 ,表示獲得信号量正常傳回,

          如果被信号打斷,傳回 ——EINTR .

        3,  down_trylock ( struct  semaphore * sem )

          擷取信号量 sem . 如果信号量不可擷取,down_trylock  會立即傳回 一個 非零值.  down_trylock  它永遠不會休眠

          注:

                    down () 函數現已不建議繼續使用,建議使用  down_killable ()或  down_interruptible ()

釋放信号量  up  :

          void  up ( struct  semaphore *sem )

          該函數釋放信号量 sem ,即把 sem 的值加 1,如果 sem 的值為非負數,

          表明有任務等待該信号量,是以喚醒這些等待者.

執行個體 --- 按鍵信号量使用:

1,初始化,定義信号量:

在驅動程式開頭初始化,定義信号量:

static DECLARE_MUTEX(button_lock);     //定義互斥鎖

2,擷取信号量:

在 open 函數中擷取信号量,擷取到就繼續執行,否則休眠;

down(&button_lock);

3,釋放信号量:

程式退出,close 釋放資訊量:

up(&button_lock);

return 0;