进程间通信的机制——信号量。注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物。有关信号的很多其它内容,能够阅读我的还有一篇文章:Linux进程间通信——使用信号。以下就进入信号量的解说。


一、什么是信号量
为了防止出现因多个程序同一时候訪问一个共享资源而引发的一系列问题,我们须要一种方法,它能够通过生成并使用令牌来授权,在任一时刻仅仅能有一个运行线程訪问代码的临界区域。临界区域是指运行数据更新的代码须要独占式地运行。而信号量就能够提供这种一种訪问机制,让一个临界区同一时间仅仅有一个线程在訪问它,也就是说信号量是用来调协进程对共享资源的訪问的。

信号量是一个特殊的变量,程序对其訪问都是原子操作,且仅仅同意对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是仅仅能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而能够取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

二、信号量的工作原理
因为信号量仅仅能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这种:

P操作 负责把当前进程由执行状态转换为堵塞状态,直到另外一个进程唤醒它。

操作为:申请一个空暇资源(把信号量减1),若成功,则退出;若失败,则该进程被堵塞;

V操作 负责把一个被堵塞的进程唤醒,它有一个參数表,存放着等待被唤醒的进程信息。

操作为:释放一个被占用的资源(把信号量加1),假设发现有被堵塞的进程,则选择一个唤醒之。

补充:查看共享信息的内存的命令是ipcs [-m|-s|-q] (所有的话是ipcs -a) ;查看共享信息的内存的命令是ipcs [-m|-s|-q]。


举个样例,就是两个进程共享信号量sv,一旦当中一个进程运行了P(sv)操作,它将得到信号量,并能够进入临界区,使sv减1。而第二个进程将被阻止进入临界区,由于当它试图运行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并运行V(sv)释放信号量,这时第二个进程就能够恢复运行。

三、Linux的信号量机制
Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不仅仅是针对二进制信号量,以下将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头文件sys/sem.h中。

1、semget函数
它的作用是创建一个新信号量或取得一个已有信号量,原型为:
  1. int semget(key_t key, int num_sems, int sem_flags);

第一个參数key是整数值(唯一非零),不相关的进程能够通过它訪问一个信号量,它代表程序可能要使用的某个资源,程序对全部信号量的訪问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个对应的信号标识符(semget函数的返回值),仅仅有semget函数才直接使用信号量键,全部其它的信号量函数使用由semget函数返回的信号量标识符。假设多个程序使用同样的key值,key将负责协调工作。


第二个參数num_sems指定须要的信号量数目,它的值差点儿总是1。

第三个參数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,能够和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则能够创建一个新的,唯一的信号量,假设信号量已存在,返回一个错误。

semget函数成功返回一个对应信号标识符(非零),失败返回-1.

2、semop函数
它的作用是改变信号量的值,原型为:
  1. int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);

sem_id是由semget返回的信号量标识符,sembuf结构的定义例如以下:
  1. struct sembuf{
  2. short sem_num;//除非使用一组信号量,否则它为0
  3. short sem_op;//信号量在一次操作中须要改变的数据,一般是两个数,一个是-1,即P(等待)操作,
  4. //一个是+1,即V(发送信号)操作。
  5. short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,
  6. //并在进程没有释放该信号量而终止时,操作系统释放信号量
  7. };

3、semctl函数
该函数用来直接控制信号量信息,它的原型为:
  1. int semctl(int sem_id, int sem_num, int command, ...);

假设有第四个參数,它一般是一个union semum结构,定义例如以下:
  1. union semun{
  2. int val;
  3. struct semid_ds *buf;
  4. unsigned short *arry;
  5. };

前两个參数与前面一个函数中的一样,command一般是以下两个值中的当中一个
SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

四、进程使用信号量通信
以下使用一个样例来说明进程间怎样使用信号量来进行通信,这个样例是两个同样的程序同一时候向屏幕输出数据,我们能够看到怎样使用信号量来使两个进程协调工作,使同一时间仅仅有一个进程能够向屏幕输出数据。注意,假设程序是第一次被调用(为了区分,第一次调用程序时带一个要输出到屏幕中的字符作为一个參数),则须要调用set_semvalue函数初始化信号并将message字符设置为传递给程序的參数的第一个字符,同一时候第一个启动的进程还负责信号量的删除工作。假设不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次执行此程序时引发问题,并且信号量是一种有限的资源。

在main函数中调用semget来创建一个信号量,该函数将返回一个信号量标识符,保存于全局变量sem_id中,然后以后的函数就使用这个标识符来訪问信号量。

建立一个项目測试一下:
semun.h
  1. #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
  2. /* union semun is defined by including <sys/sem.h> */
  3. #else
  4. /* according to X/OPEN we have to define it ourselves */
  5. union semun {
  6. int val; /* value for SETVAL */
  7. struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
  8. unsigned short int *array; /* array for GETALL, SETALL */
  9. struct seminfo *__buf; /* buffer for IPC_INFO */
  10. };
  11. #endif

MySem.C  source code:
  1. /* After the #includes, the function prototypes and the global variable, we come to the
  2.  
  3. main function. There the semaphore is created with a call to semget, which returns the
  4.  
  5. semaphore ID. If the program is the first to be called (i.e. it's called with a parameter
  6.  
  7. and argc > 1), a call is made to set_semvalue to initialize the semaphore and op_char is
  8.  
  9. set to X. */
  10.  
  11. #include <unistd.h>
  12. #include <stdlib.h>
  13. #include <stdio.h>
  14. #include <sys/sem.h>
  15. #include "semun.h"
  16.  
  17. static int set_semvalue(void);
  18. static void del_semvalue(void);
  19. static int semaphore_p(void);
  20. static int semaphore_v(void);
  21. static int sem_id;
  22. int main(int argc, char *argv[])
  23.  
  24. { int i;
  25. int pause_time;
  26. char op_char = 'O';
  27. srand((unsigned int)getpid());
  28. sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
  29. if (argc > 1) {
  30. if (!set_semvalue()) {
  31. fprintf(stderr, "Failed to initialize semaphore\n");
  32. exit(EXIT_FAILURE);
  33. }
  34. op_char = 'X';
  35. sleep(2);
  36. }
  37.  
  38. /* Then we have a loop which enters and leaves the critical section ten times.
  39. There, we first make a call to semaphore_p which sets the semaphore to wait, as
  40. this program is about to enter the critical section. */
  41. for(i = 0; i < 10; i++) {
  42. if (!semaphore_p()) exit(EXIT_FAILURE);
  43. printf("%c", op_char);fflush(stdout);
  44. pause_time = rand() % 3;
  45. sleep(pause_time);
  46. printf("%c", op_char);fflush(stdout);
  47. /* After the critical section, we call semaphore_v, setting the semaphore available,
  48. before going through the for loop again after a random wait. After the loop, the call
  49. to del_semvalue is made to clean up the code. */
  50. if (!semaphore_v()) exit(EXIT_FAILURE);
  51.  
  52. pause_time = rand() % 2;
  53. sleep(pause_time);
  54. }
  55. printf("\n%d - finished\n", getpid());
  56. if (argc > 1) {
  57. sleep(10);
  58. del_semvalue();
  59. }
  60.  
  61. exit(EXIT_SUCCESS);
  62. }
  63.  
  64. /* The function set_semvalue initializes the semaphore using the SETVAL command in a
  65. semctl call. We need to do this before we can use the semaphore. */
  66. static int set_semvalue(void)
  67. {
  68. union semun sem_union;
  69. sem_union.val = 1;
  70. if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0);
  71. return(1);
  72. }
  73. /* The del_semvalue function has almost the same form, except the call to semctl uses
  74. the command IPC_RMID to remove the semaphore's ID. */
  75. static void del_semvalue(void){
  76. union semun sem_union;
  77. if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
  78. fprintf(stderr, "Failed to delete semaphore\n");
  79. }
  80. /* semaphore_p changes the semaphore by -1 (waiting). */
  81. static int semaphore_p(void){
  82. struct sembuf sem_b;
  83. sem_b.sem_num = 0;
  84. sem_b.sem_op = -1; /* P() */
  85. sem_b.sem_flg = SEM_UNDO;
  86. if (semop(sem_id, &sem_b, 1) == -1) {
  87. fprintf(stderr, "semaphore_p failed\n");
  88. return(0);
  89. }
  90. return(1);
  91. }
  92.  
  93. /* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1,
  94. so that the semaphore becomes available. */
  95. static int semaphore_v(void)
  96. {
  97. struct sembuf sem_b;
  98. sem_b.sem_num = 0;
  99. sem_b.sem_op = 1; /* V() */
  100. sem_b.sem_flg = SEM_UNDO;
  101. if (semop(sem_id, &sem_b, 1) == -1) {
  102. fprintf(stderr, "semaphore_v failed\n");
  103. return(0);
  104. }
  105. return(1);
  106. }
同一时候两次执行该程序,编译后输出:

XX00XXOOXX00XXOOXX00XXOO……

linux进程间通讯-System V IPC 信号量的更多相关文章

  1. Linux 进程间通讯方式 pipe()函数 (转载)

    转自:http://blog.csdn.net/ta893115871/article/details/7478779 Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道( ...

  2. Linux 进程间通讯

    一.Linux 下进程间通讯方式 1)管道(Pipe)及有名管道(named pipe): 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

  3. Linux进程通信之System V共享内存

    前面已经介绍过了POSIX共享内存区,System V共享内存区在概念上类似POSIX共享内存区,POSIX共享内存区的使用是调用shm_open创建共享内存区后调用mmap进行内存区的映射,而Sys ...

  4. Linux进程通信之System V消息队列

    System V消息队列是Open Group定义的XSI,不属于POSIX标准.System V IPC的历史相对很早,在上个世70年代后期有贝尔实验室的分支机构开发,80年代加入System V的 ...

  5. c#进程间通讯方案之IPC通道

    转载:http://www.cnphp.info/csharp-ipc-channel-remoting.html 最近一直纠结与使用多进程还是多线程来构建程序.多线程的方法似乎不错,但是一个进程可承 ...

  6. Linux 进程间通讯详解一

    进程间的通讯 两台主机间的进程通讯 --socket 一台主机间的进程通讯 --管道(匿名管道,有名管道) --System V进程间通信(IPC)包括System V消息队列,System V信号量 ...

  7. linux进程间通讯的几种方式的特点和优缺点

    # 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲缘关系通常是指父子进程关系.# 有名管道 (named pipe) : 有名管道也是 ...

  8. Linux 进程间通讯详解二

    消息队列 --消息队列提供了本机上从一个进程向另外一个进程发送一块数据的方法 --每个数据块都被认为有一个类型,接收者进程接收的数据块可以有不同的类型值 --消息队列也有管道一样的不足,就是每个消息的 ...

  9. Linux进程间通讯的几种方式的特点和优缺点,和适用场合

    http://blog.csdn.net/jeffcjl/article/details/5523569 由于不同的进程运行在各自不同的内存空间中.一方对于变量的修改另一方是无法感知的.因此.进程之间 ...

随机推荐

  1. nexus3添加第三方jar

    最近在看maven的打包及管理,然後就看到nexus,自己在安裝的時候就下載了最新版的nexus-3.2.0-01-win64,按照文档部署后启动,浏览.之前一致使用的是2.0的,所以还是需要导出点点 ...

  2. CSS3------background-size(背景图片尺寸属性)

    background-size 可以设置背景图片的大小,数值包括 长度length和百分比percentage. 并且会根据背景原点位置 background-origin 设置其图片覆盖的范围.那么 ...

  3. 定位 -CLGeocoder - 编码

    #import "ViewController.h" #import <CoreLocation/CoreLocation.h> @interface ViewCont ...

  4. Pentaho Data Integration (三) Pan

    官网连接: http://wiki.pentaho.com/display/EAI/Pan+User+Documentation Pan Pan 是一个可以执行使用Spoon编辑的transforma ...

  5. EXCEL : We can't do that to a merged cell.

  6. bzoj 3527: [Zjoi2014]力 快速傅里叶变换

    题意: 给出n个数qi,给出Fj的定义如下:  令Ei=Fi/qi,求Ei. fft的那一堆东西还是背不到啊...这次写虽说完全自己写的,但是还是在参见了以前fft程序的情况下调了很久,主要在如下几点 ...

  7. MIME

    http://www1.huachu.com.cn/read/readbookinfo.asp?sectionid=1000000558 http://www.jb51.net/hack/10623. ...

  8. 教你如何监控 Apache?

    什么是 Apache? Apache 是一款 HTTP 服务器软件,现在更名为 "http",而 Apache 则成了一个(包含httpd的项目)巨大的基金组织,根据习惯后文都用 ...

  9. Hard Life

    poj3155:http://poj.org/problem?id=3155 题意:最大密度子图的模板题. 题解:直接看代码. /* 题意简述一个公司有n个人,给出了一些有冲突的人的对数(u,v),所 ...

  10. ruby迭代器iterator和枚举器Enumerator

    编写自定义的迭代器 The defining feature of an iterator method is that it invokes a block of code associatedwi ...