linux進程間通信之Posix 信號量用法詳解代碼舉例
Posix信號量不同於System V信號量的信號量集,Posix信號量是單一的信號量,分為有名信號量和無名信號量。
Posix有名信號量是使用Posix IPC名字標示的信號量,可用於進程和線程間的同步;Posix無名信號量是指基於內存的信號量,存放在共享內存區中,用於進程與線程間的同步。
Posix有名信號量可以是內核維護,也可以在文件系統中維護,這取決於信號量對應的路徑名是否映射到了真正的磁盤文件上,如果有映射到則在文件系統中維護,否則在內核中維護,Posix有名信號量由函數sem_open(),sem_close(),sem_unlink(),sem_wait(),sem_trywait(),sem_post(),sem_getvalue()來操作使用。
Posix無名信號量根據sem_init()函數調用時的輸入參數不同而分為進程間共享和線程間共享,函數原型int sem_init(sem_t *sem,int shared,unsigned int value); 的第二個參數shared若為0,則表示是線程間共享;若shared是1,則表示是進程間共享,同時第一個參數變量sem_t數據類型變量sem必須駐留在所有希望共享它的進程所共享的內存區中。Posix無名信號量由函數sem_init(),sem_destroy(),sem_wait(),sem_trywait(),sem_post(),sem_getvalue()來操作使用。
Posix信號量相關函數的原型及頭文件:
#include <semaphore.h>
sem_t *sem_open(const char*name,int oflag,.../*mode_t mode, unsigned int value*/);
功能:創建一個新的有名信號量或打開一個已存在的有名信號量
返回值:若成功返回指向信號量的指針,該指針用作sem_close(),sem_wait(),sem_trywait(),sem_post,sem_getvalue()的參數;若出錯返回SEM_FAILED.
參數:name為路徑名;oflag可以是0,O_CREAT或O_CREAT|O_EXCL;mode參數可選是指定權限位,在O_CREAT是有效;value參數可選是指定信號量的初始值,不能超過SEM_VALUE_MAX,二值信號量的初始值通常為1,計數信號量初始值通常大於1。
int sem_close(sem_t *sem);
功能:關閉由sem_open()打開的有名信號量。
返回值:若成功返回0,若失敗返回-1
int sem_unlink(const char *name);
功能:從系統中真正刪除信號量
返回值:若成功返回0,若失敗返回-1
int sem_wait(sem_t *sem);
功能:測試指定信號量的值,如果值大於0,則減1並立即返回;如果值等於0,則調用的進程或線程阻塞進入睡眠,直到該值變為大於0,此時會再減1,函數隨後返回。這種“測試病減1”的操作必須是原子的。
返回值:成功返回0,出錯返回-1
int sem_trywait(sem_t *sem);
功能:與sem_wait()相同,只是當所測試的指定信號量是0時,並不阻塞進入睡眠,而是返回一個EAGAIN錯誤。
返回值:成功返回0,出錯返回-1
int sem_post(sem_t *sem);
功能:把所指定的信號量值加1,然後喚醒正在等待該信號量值變為正數的任意進程或線程。
返回值:成功返回0,出錯返回-1
int sem_getvalue(sem_t *sem, int *valp);
功能:獲取指定信號量的當前值存入valp指針中,如果信號量已上鎖,則獲取值為0或某個負數,絕對值是等待該信號量解鎖的線程數。
返回值:成功返回0,出錯返回-1.
int sem_int(sem_t *sem, int shared, unsigned int value);
功能:初始化Posix共享內存的無名信號量。
返回值: 出錯返回-1.
參數:sem是信號量的指針;shared為0是線程共享,為1是進程共享(sem需駐留共享內存);value是初始化值。
int sem_destory(sem_t *sem);
功能:摧毀sem_init()初始化的無名信號量。
返回值:成功返回0,出錯返回-1
代碼舉例,父子進程采用二值信號量sem_test.c:
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <semaphore.h>
- #include <fcntl.h>
- #define USE_POSIX_SEM 1
- #define MYSEM "/mysem"
- #define RUN_TIMES 5
- int main(void)
- {
- #if USE_POSIX_SEM
- sem_t *semId;
- int val;
- //sem_unlink(MYSEM);
- semId=sem_open(MYSEM,O_CREAT,0666,0);
- if(SEM_FAILED==semId)
- {
- printf("sem open failed!\n");
- return 0;
- }
- sem_getvalue(semId,&val);
- printf("sem_val init=%d\n",val);
- sem_post(semId);
- sem_getvalue(semId,&val);
- printf("sem_val after post=%d\n",val);
- #endif
- int pid=fork();
- if (0==pid)
- {
- int i;
- #if USE_POSIX_SEM
- sem_wait(semId);
- #endif
- for(i=0;i<RUN_TIMES;i++)
- {
- printf("child running!\n");
- sleep(1);
- }
- #if USE_POSIX_SEM
- sem_post(semId);
- #endif
- printf("child end\n");
- exit(0);
- }
- else if (pid>0)
- {
- int i;
- #if USE_POSIX_SEM
- sem_wait(semId);
- #endif
- for(i=0;i<RUN_TIMES;i++)
- {
- printf("parent running!\n");
- sleep(1);
- }
- #if USE_POSIX_SEM
- sem_post(semId);
- #endif
- printf("parent end\n");
- }
- waitpid(pid,NULL,0);
- printf("progam finished\n");
- #if USE_POSIX_SEM
- sem_close(semId);
- sem_unlink(MYSEM);
- #endif
- return 0;
- }
運行結果:
$ ./a.out
sem_val init=0
sem_val after post=1
parent running!
parent running!
parent running!
parent running!
parent running!
parent end
child running!
child running!
child running!
child running!
child running!
child end
progam finished
可以看出,父進程先執行,執行5次打印後post 信號量後,子進程才執行。
如果關閉Posix 信號量,條件編譯宏設為“#define USE_POSIX_SEM 0”,運行結果為:
$ ./a.out
parent running!
child running!
parent running!
child running!
parent running!
child running!
parent running!
child running!
child running!
parent running!
parent end
child end
progam finished
可以看到父子進程交替執行,存在競爭關系。
linux進程間通信之Posix 信號量用法詳解代碼舉例