【linux】系統程式設計-6-POSIX標準下的訊號量與互斥鎖
阿新 • • 發佈:2021-01-19
[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);`** :銷燬互斥鎖
## 參考
中。程序退出後,該訊號量不會消失,需要手動刪除並釋放資源。 * 包含標頭檔案 **`#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