1. 程式人生 > >linux進程間通信之Posix 信號量用法詳解代碼舉例

linux進程間通信之Posix 信號量用法詳解代碼舉例

lee lag tar 不同 sem_init sign 基於內存 target PC

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:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <semaphore.h>
  6. #include <fcntl.h>
  7. #define USE_POSIX_SEM 1
  8. #define MYSEM "/mysem"
  9. #define RUN_TIMES 5
  10. int main(void)
  11. {
  12. #if USE_POSIX_SEM
  13. sem_t *semId;
  14. int val;
  15. //sem_unlink(MYSEM);
  16. semId=sem_open(MYSEM,O_CREAT,0666,0);
  17. if(SEM_FAILED==semId)
  18. {
  19. printf("sem open failed!\n");
  20. return 0;
  21. }
  22. sem_getvalue(semId,&val);
  23. printf("sem_val init=%d\n",val);
  24. sem_post(semId);
  25. sem_getvalue(semId,&val);
  26. printf("sem_val after post=%d\n",val);
  27. #endif
  28. int pid=fork();
  29. if (0==pid)
  30. {
  31. int i;
  32. #if USE_POSIX_SEM
  33. sem_wait(semId);
  34. #endif
  35. for(i=0;i<RUN_TIMES;i++)
  36. {
  37. printf("child running!\n");
  38. sleep(1);
  39. }
  40. #if USE_POSIX_SEM
  41. sem_post(semId);
  42. #endif
  43. printf("child end\n");
  44. exit(0);
  45. }
  46. else if (pid>0)
  47. {
  48. int i;
  49. #if USE_POSIX_SEM
  50. sem_wait(semId);
  51. #endif
  52. for(i=0;i<RUN_TIMES;i++)
  53. {
  54. printf("parent running!\n");
  55. sleep(1);
  56. }
  57. #if USE_POSIX_SEM
  58. sem_post(semId);
  59. #endif
  60. printf("parent end\n");
  61. }
  62. waitpid(pid,NULL,0);
  63. printf("progam finished\n");
  64. #if USE_POSIX_SEM
  65. sem_close(semId);
  66. sem_unlink(MYSEM);
  67. #endif
  68. return 0;
  69. }
復制代碼

運行結果:
$ ./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 信號量用法詳解代碼舉例