当前位置:   article > 正文

Linux进程通信——信号量_linux 进程通信 信号量

linux 进程通信 信号量

目录

概念

特点

使用原理

semget函数

函数原型

参数解读

semctl函数

函数原型

参数解读

semop函数

函数原型

参数解读

代码示例


概念

信号量(semaphore) 与已经介绍过的 PC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

特点

1.信号量用于进程间同步,若要在进程间传递数据需要结合共享内存
2.信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作

PV操作一种实现进程互斥与同步的有效方法。PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思
原子操作指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch(切换到另一个线程)

3.每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数
4.支持信号量组

使用原理

最简单的信号量是只能取 0和 1的变量,这也是信号量最常见的一种形式,叫做二值信号量 Binary Semaphore) 。而可以取多个正整数的信号量被称为通用信号量

Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

semget函数

创建或获取一个信号量组:若成功返回信号量标识符ID,失败返回-1。

函数原型

int semget(key_t key, int nsems, int semflg);

参数解读

key所创建或打开信号量集的键值
nsems信号量的个数,通常为1;如果是引用一个现有的集合,则将nsems指定为 0,该参数只在创建信号量集时有效
semflg调用函数的操作类型,也可用于设置信号量集的访问权限,两者通过 | 表示

semctl函数

控制信号量的相关信息。

函数原型

int semctl(int semid, int semnum, int cmd, ...);

参数解读

semidsemget函数返回的信号量标识符ID
semnum操作信号在信号集中的编号,这里是以数组为单位的,所以第一个单位是数组中的第一个,即为0
cmd对信号量进行相关操作

在semctl函数中的cmd有多种,这里就说两个常用的:

SETVAL用于初始化信号量为一个已知的值。所需要的值作为联合体semun的val成员来传递,在信号量第一次使用之前需要设置信号量
IPC_RMID删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源

如果有第四个参数,它通常是联合体union semun,定义格式如下:

  1. union semun
  2. {
  3. int val; /* Value for SETVAL */
  4. struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
  5. unsigned short *array; /* Array for GETALL, SETALL */
  6. struct seminfo *__buf; /* Buffer for IPC_INFO
  7. (Linux-specific) */
  8. };

semop函数

对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1。

函数原型

int semop(int semid, struct sembuf *sops, unsigned nsops);

参数解读

semidsemget函数返回的信号量标识符ID
*sops通常是一个结构体数组,但信号量理论上一次只能执行一个,所以在定义该结构体时,不需要以数组格式定义
nsops信号操作结构的数量,恒大于或等于1

*sops结构体定义格式如下:

  1. struct sembuf
  2. {
  3. short sem_num; // 信号量的编号,默认为0
  4. short sem_op; // 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
  5. // 一个是+1,即V(发送信号)操作。
  6. short sem_flg; // 通常为SEM_UNDO,使操作系统跟踪信号,当进程结束之前,取消对信号量的任何操作
  7. };

代码示例

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/ipc.h>
  4. #include <sys/sem.h>
  5. //模拟用钥匙开锁场景
  6. union semun//定义一个联合体
  7. {
  8. int val; /* Value for SETVAL */
  9. struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
  10. unsigned short *array; /* Array for GETALL, SETALL */
  11. struct seminfo *__buf; /* Buffer for IPC_INFO*/
  12. };
  13. void pGetKey(int semid)
  14. {
  15. struct sembuf set;//定义一个结构体
  16. set.sem_num = 0;//第一个信号量
  17. set.sem_op = -1;//拿走钥匙 数量-1
  18. set.sem_flg = SEM_UNDO;//拿不到钥匙就等待
  19. semop(semid,&set,1);//第二个参数为指针,这里需要以地址形式写入
  20. printf("get the key\n");
  21. }
  22. void vPutBackKey(int semid)
  23. {
  24. struct sembuf put;
  25. put.sem_num = 0;
  26. put.sem_op = +1;//放回钥匙 数量+1
  27. put.sem_flg = SEM_UNDO;
  28. semop(semid,&put,1);
  29. printf("put back the key\n");
  30. }
  31. int main()
  32. {
  33. int semid;
  34. int pid;
  35. key_t key;
  36. key = ftok(".",1);
  37. semid = semget(key,1,IPC_CREAT|0666);//以可读可写权限创建信号量
  38. union semun initsem;//调用联合体:初识化钥匙数量
  39. initsem.val = 0;//一开始钥匙数量为0
  40. semctl(semid,0,SETVAL,initsem);
  41. pid = fork();
  42. if(pid > 0)
  43. {
  44. pGetKey(semid);//拿钥匙 但钥匙数量为0 需等待钥匙放回 即等待子进程放回钥匙
  45. printf("this is father\n");
  46. vPutBackKey(semid);//拿到钥匙后使用完毕放回钥匙
  47. }
  48. else if(pid == 0)
  49. {
  50. printf("this is child\n");
  51. vPutBackKey(semid);//将钥匙放回,钥匙数量+1,父进程检测到有钥匙就拿钥匙,即先进入子进程再进入父进程
  52. }
  53. else
  54. printf("error!\n");
  55. return 0;
  56. }



可见,一开始没有钥匙,父进程中没有任何操作,当子进程中将钥匙放回时,父进程检测到后拿到钥匙使用完毕后并放回。实现信号量的检测。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/959470
推荐阅读
相关标签
  

闽ICP备14008679号