http://www.cnblogs.com/biyeymyhjob/archive/2012/07/21/2602015.htmlhtml
http://blog.chinaunix.net/uid-20543672-id-3252604.htmllinux
http://bbs.chinaunix.net/thread-2333160-1-1.html數據結構
1、信號量ide
信號量又稱爲信號燈,它是用來協調不一樣進程間的數據對象的,而最主要的應用是共享內存方式的進程間通訊。本質上,信號量是一個計數器,它用來記錄對某個資源(如共享內存)的存取情況。通常說來,爲了得到共享資源,進程須要執行下列操做: 函數
(1) 測試控制該資源的信號量。
(2) 若此信號量的值爲正,則容許進行使用該資源。進程將信號量減1。
(3) 若此信號量爲0,則該資源目前不可用,進程進入睡眠狀態,直至信號量值大於0,進程被喚醒,轉入步驟(1)。
(4) 當進程再也不使用一個信號量控制的資源時,信號量值加1。若是此時有進程正在睡眠等待此信號量,則喚醒此進程。 post
維護信號量狀態的是Linux內核操做系統而不是用戶進程。咱們能夠從頭文件/usr/src/linux/include/linux/sem.h 中看到內核用來維護信號量狀態的各個結構的定義。信號量是一個數據集合,用戶能夠單獨使用這一集合的每一個元素。要調用的第一個函數是semget,用以得到一個信號量ID。測試
Linux2.6.26下定義的信號量結構體:ui
struct semaphore { spinlock_t lock; unsigned int count; struct list_head wait_list; };
從以上信號量的定義中,能夠看到信號量底層使用到了spin lock的鎖定機制,這個spinlock主要用來確保對count成員的原子性的操做(count--)和測試(count > 0)。atom
2、互斥體spa
互斥體實現了「互相排斥」(mutual exclusion)同步的簡單形式(因此名爲互斥體(mutex))。互斥體禁止多個線程同時進入受保護的代碼「臨界區」(critical section)。所以,在任意時刻,只有一個線程被容許進入這樣的代碼保護區。
任何線程在進入臨界區以前,必須獲取(acquire)與此區域相關聯的互斥體的全部權。若是已有另外一線程擁有了臨界區的互斥體,其餘線程就不能再進入其中。這些線程必須等待,直到當前的屬主線程釋放(release)該互斥體。
何時須要使用互斥體呢?互斥體用於保護共享的易變代碼,也就是,全局或靜態數據。這樣的數據必須經過互斥體進行保護,以防止它們在多個線程同時訪問時損壞
struct mutex { /* 1: unlocked, 0: locked, negative: locked, possible waiters */ atomic_t count; spinlock_t wait_lock; struct list_head wait_list; #ifdef CONFIG_DEBUG_MUTEXES struct thread_info *owner; const char *name; void *magic; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif };
對比前面的struct semaphore,struct mutex除了增長了幾個做爲debug用途的成員變量外,和semaphore幾乎長得同樣。可是mutex的引入主要是爲了提供互斥機制,以免多個進程同時在一個臨界區中運行。
從原理上講,mutex其實是count=1狀況下的semaphore,底層實現和semaphore幾乎同樣。
3、自旋鎖
自旋鎖它是爲爲實現保護共享資源而提出一種鎖機制。其實,自旋鎖與互斥鎖比較相似,它們都是爲了解決對某項資源的互斥使用。不管是互斥鎖,仍是自旋鎖,在任什麼時候刻,最多隻能有一個保持者,也就說,在任什麼時候刻最多隻能有一個執行單元得到鎖。可是二者在調度機制上略有不一樣。對於互斥鎖,若是資源已經被佔用,資源申請者只能進入睡眠狀態。可是自旋鎖不會引發調用者睡眠,若是自旋鎖已經被別的執行單元保持,調用者就一直循環在那裏看是否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是所以而得名。
自旋鎖通常原理
跟互斥鎖同樣,一個執行單元要想訪問被自旋鎖保護的共享資源,必須先獲得鎖,在訪問完共享資源後,必須釋放鎖。若是在獲取自旋鎖時,沒有任何執行單元保持該鎖,那麼將當即獲得鎖;若是在獲取自旋鎖時鎖已經有保持者,那麼獲取鎖操做將自旋在那裏,直到該自旋鎖的保持者釋放了鎖。由此咱們能夠看出,自旋鎖是一種比較低級的保護數據結構或代碼片斷的原始方式,這種鎖可能存在兩個問題:死鎖和過多佔用cpu資源
自旋鎖適用狀況
自旋鎖比較適用於鎖使用者保持鎖時間比較短的狀況。正是因爲自旋鎖使用者通常保持鎖時間很是短,所以選擇自旋而不是睡眠是很是必要的,自旋鎖的效率遠高於互斥鎖。信號量和讀寫信號量適合於保持時間較長的狀況,它們會致使調用者睡眠,所以只能在進程上下文使用(若是在中斷上下文中使用,調用者是系統進程,可能引發系統進程睡眠),而自旋鎖適合於保持時間很是短的狀況,它能夠在任何上下文使用。若是被保護的共享資源只在進程上下文訪問,使用信號量保護該共享資源很是合適,若是對共享資源的訪問時間很是短,自旋鎖也能夠。可是若是被保護的共享資源須要在中斷上下文訪問(包括底半部即中斷處理句柄和頂半部即軟中斷),就必須使用自旋鎖。自旋鎖保持期間是搶佔失效的,而信號量和讀寫信號量保持期間是能夠被搶佔的。自旋鎖只有在內核可搶佔或SMP(多處理器)的狀況下才真正須要,在單CPU且不可搶佔的內核下,自旋鎖的全部操做都是空操做。另外格外注意一點:自旋鎖不能遞歸使用。
4、信號量、互斥體和自旋鎖的區別
信號量/互斥體和自旋鎖spinlock的區別
信號量/互斥體容許進程睡眠屬於睡眠鎖,自旋鎖則不容許調用者睡眠,而是讓其循環等待,因此有如下區別應用
1)、信號量和讀寫信號量適合於保持時間較長的狀況,它們會致使調用者睡眠,於是自旋鎖適合於保持時間很是短的狀況
2)、自旋鎖能夠用於中斷,不能用於進程上下文(會引發死鎖)。而信號量不容許使用在中斷中,而能夠用於進程上下文
3)、自旋鎖保持期間是搶佔失效的,自旋鎖被持有時,內核不能被搶佔,而信號量和讀寫信號量保持期間是能夠被搶佔的
另外須要注意的是
1)、信號量鎖保護的臨界區可包含可能引發阻塞的代碼,而自旋鎖則絕對要避免用來保護包含這樣代碼的臨界區,由於阻塞意味着要進行進程的切換,若是進程被切換出去後,另外一進程企圖獲取本自旋鎖,死鎖就會發生。
2)、在你佔用信號量的同時不能佔用自旋鎖,由於在你等待信號量時可能會睡眠,而在持有自旋鎖時是不容許睡眠的。
信號量和互斥體之間的區別
概念上的區別:
信號量:是進程間(線程間)同步用的,一個進程(線程)完成了某一個動做就經過信號量告訴別的進程(線程),別的進程(線程)再進行某些動做。有二值和多值信號量之分。
互斥鎖:是線程間互斥用的,一個線程佔用了某一個共享資源,那麼別的線程就沒法訪問,直到這個線程離開,其餘的線程纔開始可使用這個共享資源。能夠把互斥鎖當作二值信號量。
上鎖時:
信號量: 只要信號量的value大於0,其餘線程就能夠sem_wait成功,成功後信號量的value減一。若value值不大於0,則sem_wait阻塞,直到sem_post釋放後value值加一。一句話,信號量的value>=0。
互斥鎖: 只要被鎖住,其餘任何線程都不能夠訪問被保護的資源。若是沒有鎖,得到資源成功,不然進行阻塞等待資源可用。一句話,線程互斥鎖的vlaue能夠爲負數。
使用場所:
信號量主要適用於進程間通訊,固然,也可用於線程間通訊。而互斥鎖只能用於線程間通訊。
----------------------------------------------------------------------------------------------------------------------------------
進程上下文:
在Linux中,用戶程序裝入系統造成一個進程的實質是系統爲用戶程序提供一個完整的運行環境。進程的運行環境是由它的程序代碼和程序運行所須要的數據結構以及硬件環境組成的。進程的運行環境主要包括:
1.進程空間中的代碼和數據、各類數據結構、進程堆棧和共享內存區等。
2.環境變量:提供進程運行所需的環境信息。
3.系統數據:進程空間中的對進程進行管理和控制所需的信息,包括進程任務結構體以及內核堆棧等。
4.進程訪問設備或者文件時的權限。
5.各類硬件寄存器。
6.地址轉換信息。
從以上組成狀況能夠看到,進程的運行環境是動態變化的,尤爲是硬件寄存器的值以及進程控制信息是隨着進程的運行而不斷變化的。在Linux中把系統提供給進程的的處於動態變化的運行環境總和稱爲進程上下文。
系統中的每個進程都有本身的上下文。一個正在使用處理器運行的進程稱爲當前進程(current)。當前進程因時間片用完或者因等待某個事件而阻塞時,進程調度須要把處理器的使用權從當前進程交給另外一個進程,這個過程叫作進程切換。此時,被調用進程成爲當前進程。在進程切換時系統要把當前進程的上下文保存在指定的內存區域(該進程的任務狀態段TSS中),而後把下一個使用處理器運行的進程的上下文設置成當前進程的上下文。當一個進程通過調度再次使用CPU運行時,系統要恢復該進程保存的上下文。因此,進程的切換也就是上下文切換。
在系統內核爲用戶進程服務時,一般是進程經過系統調用執行內核代碼,這時進程的執行狀態由用戶態轉換爲內核態。可是,此時內核的運行是爲用戶進程服務,也能夠說內核在代替當前進程執行某種服務功能。在這種狀況下,內核的運行還是進程運行的一部分,因此說這時內核是運行在進程上下文中。內核運行在進程上下文中時能夠訪問和修改進程的系統數據。此外,若內核運行在進程上下文中須要等待資源和設備時,系統能夠阻塞當前進程
中斷上下文:
硬件經過觸發信號,致使內核調用中斷處理程序,進入內核空間。這個過程當中,硬件的一些變量和參數也要傳遞給內核,內核經過這些參數進行中斷處理。所謂的「中斷上下文」,其實也能夠看做就是硬件傳遞過來的這些參數和內核須要保存的一些其餘環境(主要是當前被打斷執行的進程環境)。中斷時,內核不表明任何進程運行,它通常只訪問系統空間,而不會訪問進程空間,內核在中斷上下文中執行時通常不會阻塞
--------------------------------------------------------------------------------------------------------------------------
可搶佔式內核:即當進程位於內核空間時,有一個更高優先級的任務出現時,若是當前內核容許搶佔,則能夠將當前任務掛起,執行優先級更高的進程。
非搶佔式內核:高優先級的進程不能停止正在內核中運行的低優先級的進程而搶佔CPU運行。進程一旦處於核心態(例如用戶進程執行系統調用),則除非進程自願放棄CPU,不然該進程將一直運行下去,直至完成或退出內核
搶佔式內核的意義:首先,這是將Linux應用於實時系統所必需的。實時系統對響應時間有嚴格的限定,當一個實時進程被實時設備的硬件中斷喚醒後,它應在限定的時間內被調度執行。而Linux不能知足這一要求,由於Linux的內核是不可搶佔的,不能肯定系統在內核中的停留時間。事實上當內核執行長的系統調用時,實時進程要等到內核中運行的進程退出內核才能被調度,由此產生的響應延遲,在現在的硬件條件下,會長達100ms級。這對於那些要求高實時響應的系統是不能接受的。而可搶佔的內核不只對Linux的實時應用相當重要,並且能解決Linux對多媒體(video, audio)等要求低延遲的應用支持不夠好的缺陷。