1. 程式人生 > >【linux】系統程式設計-6-POSIX標準下的訊號量與互斥鎖

【linux】系統程式設計-6-POSIX標準下的訊號量與互斥鎖

[toc] --- ## 前言 [原文](https://www.cnblogs.com/lizhuming/p/14299764.html) ## 8. POSIX訊號量 ### 8.1 概念 * 訊號量(Semaphore)是一種實現程序/執行緒間通訊的機制,可以實現程序/執行緒之間同步或臨界資源的互斥訪問, 常用於協助一組相互競爭的程序/執行緒來訪問臨界資源。 * 在POSIX標準中分無名訊號量和有名訊號量: * 無名訊號量 * 一般用於**執行緒**間同步或互斥 * 無名訊號量保存於記憶體中 * 有名訊號量 * 一般用於**程序**間同步或互斥 * 有名訊號量保持於檔案中 * 訊號量P、V操作 * P操作(申請資源) * 如果有可用資源,則申請成功,訊號量減一 * 如果沒有可用資源,則申請失敗,進入阻塞或返回 * V操作(釋放資源) * 如果訊號量的等待佇列中有程序/執行緒在等待,則喚醒一個阻塞的程序/執行緒 * 如果沒有程序/執行緒等待阻塞,則訊號量加一 ### 8.2 POSIX無名訊號量 * 無名訊號量直接存於記憶體中,不同程序之間不能互相訪問。fork程序中的無名訊號量於父程序中的無名訊號量是兩個獨立的訊號量。若非要無名訊號量用於程序間,則可把訊號量放在共享記憶體中。 * 包含標頭檔案 **`#include semaphore.h`** * 相關函式 ```c int sem_init(sem_t *sem, int pshared, unsigned int value); int sem_destroy(sem_t *sem); int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_post(sem_t *sem); ``` * **`int sem_init(sem_t *sem, int pshared, unsigned int value);`** :初始化訊號量 * **`int sem_destroy(sem_t *sem);`** :銷燬訊號量 * **`int sem_wait(sem_t *sem);`** :P操作,帶阻塞。成功返回0,失敗返回-1 * **`int sem_trywait(sem_t *sem);`** :P操作,不阻塞。成功返回0,失敗返回EAGAIN * **`int sem_post(sem_t *sem);`** :V操作。成功返回0,失敗返回-1 ### 8.3 POSIX有名訊號量 * 有名訊號量保存於檔案中,一般用於程序間同步或互斥。其檔名類似 **`sem.[訊號量名字]`** ,建立該訊號量成功後,系統會將其存放在 **/dev/shm**
中。程序退出後,該訊號量不會消失,需要手動刪除並釋放資源。 * 包含標頭檔案 **`#include semaphore.h`** * 相關函式 ```c sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_post(sem_t *sem); int sem_close(sem_t *sem); int sem_unlink(const char *name); ``` * **`sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);`** :開啟或建立一個有名訊號量 * **`int sem_wait(sem_t *sem);`** :P操作,帶阻塞。成功返回0,失敗返回-1 * **`int sem_trywait(sem_t *sem);`** :P操作,不阻塞。成功返回0,失敗返回EAGAIN * **`int sem_post(sem_t *sem);`** :V操作。成功返回0,失敗返回-1 * **`int sem_close(sem_t *sem);`** :關閉訊號量,表示當前程序取消對該訊號量的使用權。不影響其它程序/執行緒對其繼續使用。 * **`int sem_unlink(const char *name);`** :刪除訊號量,其它程序/執行緒也訪問不了了。 ### 8.4 POPSIX訊號量與system V訊號量的區別 * 先簡單瞭解一下訊號量分類: 1. 二值訊號量:其值為0或為1。 2. 計數訊號量:其值為0至某個限制值(POSIX訊號量最大為32767) 3. 計數訊號量集:一個或多個計數訊號量構成一個集合。 * system V訊號量所指的是**計數訊號量集**,POSIX訊號量所指的是**單個計數訊號量** * system V訊號量,可以控制每次自增或自減的**訊號量計數**,而POSIX訊號量每次只能自增或自減**1** * system V訊號量提供的API是沒有下劃線的:**`semctl()、semget() 和 semop()`** * system V訊號量是**核心持續**的;POSIX無名訊號量是**程序持續**的;POSIX有名訊號量是**核心持續**的 ## 9. POSIX互斥鎖 ### 9.1 概念 * 當不同程序/執行緒去訪問某個臨界資源的時候,就需要進行互斥保護,這種互斥保護可以看做是一種鎖機制。好比上廁所,鎖住門,不讓別人進。 * 互斥鎖和訊號量不同的是,它具有互斥鎖所有權、遞迴訪問等特性,常用於實現對臨界資源的獨佔式處理,任意時刻互斥鎖的狀態只有兩種,開鎖或閉鎖。 * 互斥鎖所有權就是互斥鎖被執行緒持有時,互斥鎖處於閉鎖狀態,執行緒獲得互斥鎖的所有權;當該執行緒釋放互斥鎖時,該互斥鎖處於開鎖狀態,執行緒失去該互斥鎖的所有權。 * 互斥鎖遞迴訪問,持有該互斥鎖的執行緒具有對該互斥鎖進行遞迴訪問。 * **避免死鎖需要遵循的規則** * 對共享資源操作前一定要獲得鎖 * 完成操作後一定要釋放鎖 * 儘量短時間佔用鎖 * 如果有多鎖, 如獲得順序是ABC連環扣, 釋放順序也應該是ABC。 * 互斥鎖比訊號量更適合的應用場景: * 保護臨界資源 * 執行緒可能會多次獲取互斥鎖的情況下。*這樣可以避免同一執行緒多次遞迴持有而造成死鎖的問題。* * 這裡的POSIX互斥鎖用於執行緒間 ### 9.2 初始化互斥鎖 * 包含標頭檔案 **`#include `** * **互斥鎖靜態初始化** 選擇以下其一即可 ```c pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER; // 快速互斥鎖 pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; // 遞迴互斥鎖 pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; // 檢錯互斥鎖 ``` * 快速互斥鎖:具有阻塞機制,不具備遞迴特性。 * 遞迴互斥鎖:遞迴獲取互斥鎖時,持有互斥鎖的計數加1 * 檢錯互斥鎖:快速互斥鎖的非阻塞版本 * **互斥鎖動態初始化** * **mutex** 初始化互斥鎖結構的指標 * **mutexattr** 屬性引數,如果該引數為 NULL,則表示選擇預設配置,為快速互斥鎖。 ```c int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); ``` ### 9.3 獲取互斥鎖與釋放互斥鎖 * 注:非遞迴互斥鎖不具備遞迴特性。 * **`int pthread_mutex_lock(pthread_mutex_t *mutex);`** :獲取互斥鎖並上鎖,具有阻塞功能 * **`int pthread_mutex_lock(pthread_mutex_t *mutex);`** :獲取互斥鎖並上鎖,不阻塞,發現鎖被佔用後會返回**EBUSY**錯誤 * **`int pthread_mutex_unlock(pthread_mutex_t *mutex);`** :解鎖並釋放互斥鎖 ### 9.4 銷燬互斥鎖 * **`int pthread_mutex_destroy(pthread_mutex_t *mutex);`** :銷燬互斥鎖 ## 參考