Linux进程间通信五 Posix 信号量简介与示例
1. 信號量簡介
信號量用于進程或線程間同步,Posix信號量是一個非負整型,只有兩種操作,加一(sem_post)和減一(sem_wait),如果信號量值為0,sem_wait默認阻塞。
Posix信號量有兩種,有名信號量和無名信號量,顧名思義,就是是否有名字。有名信號量有一個名字,長度不超過NAME_MAX-4(i.e. 251),因為內核會默認加上'sem.',所以這里要減4,名字以斜杠開頭'/',后面跟上一個或多個非斜杠字符。不同進程可以通過同一個名字來操作有名信號量,sem_open用于創建或者獲取已存在的信號量,創建好之后就可以使用sem_post或者sem_wait來操作。使用完之后可以使用sem_close來關閉信號量,sem_unlink用來刪除信號量,刪除并不立即銷毀,只有當所有進程都sem_close才開始銷毀。
無名信號量沒有名字,基于內存,通常用在同一個進程線程之間或者不同進程的共享內存里,因為同一個進程的不同線程共享進程地址空間,所以可以訪問到,放到共享內存里也可以被不同進程訪問到。使用前必須使用sem_init進行初始化,初始化之后就可以使用sem_wait和sem_post操作,使用完成后sem_destroy接口進行銷毀,下一節介紹接口的使用
2. 信號量API接口
2.1 有名信號量創建
#include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h>/** * @brief 創建或獲取有名信號量 * * @params name 有名信號量關聯的名字,斜杠開頭,長度不超過NAME_MAX-4(e.g. 251) * @params oflag 標志位,可選值包括O_CREAT| O_EXCL * 這里如果指定了O_CREAT標志位,還要填寫額外兩個參數,mode和value * * @params mode,參考open函數,通常填0即可 * @params value 信號量的初始值 * @returns 成功返回描述符,失敗返回-1 **/sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);# 編譯加上 -pthread選項 Link with -pthread.2.2 信號量減一操作
#include <semaphore.h>/** * @brief lock信號量并減1,當信號量大于0,操作可以執行,否則則阻塞。如果設置了NONBLOCK標志位,則報錯返回 * * @params sem 信號量描述符 * @returns 成功返回0,失敗返回-1 **/ int sem_wait(sem_t *sem);/** * @brief 同sem_wait,只不過如果無法減1則立即報錯返回 * **/ int sem_trywait(sem_t *sem);/** * @brief 同sem_wait,只不過如果無法減1則會等待一段時間,注意這里時間參數要設置正確 * **/ int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);# 編譯鏈接選項 -pthread Link with -pthread.2.3 信號量加1操作
#include <semaphore.h>/** * @brief 信號量的值加1 * * @params sem 信號量文件描述符 * @returns 成功返回0,失敗返回-1 **/int sem_post(sem_t *sem);# 編譯鏈接選項 -pthread Link with -pthread.2.4 關閉有名信號量
#include <semaphore.h>/** * @brief 關閉信號量,系統為當前進程分配的信號量資源會被釋放。 * * @params sem 信號量文件描述符 * @returns 成功返回0,失敗返回-1 **/int sem_close(sem_t *sem);# 編譯鏈接選項 -pthread Link with -pthread.2.5 銷毀有名信號量
#include <semaphore.h>/** * @brief 銷毀信號量,只有當所有進程都關閉信號量之后才開始銷毀工作 * * @params name 信號量名字 * @returns 成功返回0,失敗返回-1 **/int sem_unlink(const char *name);# 編譯鏈接選項 -pthread Link with -pthread.2.6 初始化無名信號量
#include <semaphore.h>/** * @brief 初始化無名信號量 * * @params sem 待初始化的信號量地址 * @params pshared 為0表示線程間共享,非0表示進程間共享 * @params value 信號量初始值,不超過SEM_VALUE_MAX * @returns 成功返回0,失敗返回-1 **/int sem_init(sem_t *sem, int pshared, unsigned int value);# 編譯鏈接選項 -pthread Link with -pthread.2.7 銷毀無名信號量
#include <semaphore.h>/** * @brief 銷毀無名信號量 * * @params sem 要銷毀的信號量 * 如果有其它線程或進程阻塞在sem上,此時銷毀會產生未定義的行為 * * @returns 成功返回0,失敗返回-1 **/int sem_destroy(sem_t *sem);# 編譯鏈接選項 -pthread Link with -pthread.3 有名信號量例子
3.1 信號量生產者
// 生產者 #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h>#define SEM_NAME "/sem0"int main(int argc, char** argv) {if (argc < 3){printf("Usage: ./sem_post timeval nums\n");return -1;}int ts = atoi(argv[1]);int total = atoi(argv[2]);if (total < 1 || ts < 1){printf("invalid param\n");return -1;}sem_t* sem_id;// 創建信號量并初始化值為0sem_id = sem_open(SEM_NAME, O_CREAT, O_RDWR, 0);if (sem_id == SEM_FAILED){perror("sem_open error");return -1;}int curr = 0;while (curr < total){ // 生成信號量,即加1while (sem_post(sem_id)){perror("sem_post error, try later");sleep(1);}printf("producing succ\n");sleep(ts);++curr;}printf("work done\n");// 關閉信號量sem_close(sem_id);return 0; }3.2 信號量消費者
// 消費者 #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h>#define SEM_NAME "/sem0"int main() {sem_t* sem_id;// 創建信號量并初始化值為0sem_id = sem_open(SEM_NAME, O_CREAT, O_RDWR, 0);if (sem_id == SEM_FAILED){perror("sem_open error");return -1;}while (1){// 消費信號量if (sem_wait(sem_id)){perror("sem_wait fail, try later\n");sleep(1);continue;}printf("consuming succ\n");}// 關閉信號量sem_close(sem_id);return 0; }3.3 編譯&運行
default:gcc -o sem_post sem_post.c -pthreadgcc -o sem_wait sem_wait.c -pthread clean:rm -rf sem_wait sem_post?
4. 無名信號量例子
創建兩個線程,分別用于生產和消費
#include <semaphore.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h>// 消費者 void* consumer_worker(void *arg) {sem_t *sem = arg;while (1){// 消費信號量if (sem_wait(sem)){perror("[consumer] sem_wait error, try later\n");sleep(1);continue;}printf("[consumer] consume succ\n");}return 0; }// 生產者 void* producer_worker(void *arg) {sem_t *sem = arg;while (1){// 生成信號量if (sem_post(sem)){perror("[producer] sem_post error, try later\n");sleep(1);continue;}printf("[producer] produce succ\n");sleep(1);}return 0; }int main(int argc, char** argv) {pthread_t consumer, producer;if (argc < 2){printf("Usage: ./unnamed_sem time\n");return -1;}int tm = atoi(argv[1]);if (tm < 1){printf("invalid param\n");return -1;}sem_t sem;// 無名信號量初始化if (sem_init(&sem, 0, 0)){perror("sem_init error");return -1;}// 創建生產者線程if (pthread_create(&producer, NULL, &producer_worker, (void *)&sem)){perror("create producer_worker error");sem_destroy(&sem);return -1;}// 創建消費者線程if (pthread_create(&consumer, NULL, &consumer_worker, (void *)&sem)){perror("create consumer_worker error");sem_destroy(&sem);return -1;}printf("main thread sleep:%d\n", tm);sleep(tm);// 無名信號量銷毀sem_destroy(&sem);return 0; }5. 參考資料
1.?https://www.man7.org/linux/man-pages/man7/sem_overview.7.html
2. 《Linux環境編程 從應用到內核》?
================================================================================================
Linux應用程序、內核、驅動、后臺開發交流討論群(745510310),感興趣的同學可以加群討論、交流、資料查找等,前進的道路上,你不是一個人奧^_^。
總結
以上是生活随笔為你收集整理的Linux进程间通信五 Posix 信号量简介与示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux进程间通信四 Posix 消息
- 下一篇: 苹果开机进u盘启动怎么办 苹果电脑如何从