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