当前位置: 首页 > news >正文

14-6-进程间通信-信号量

前面学习了pipe,fifo,共享内存,信号。

本章将讲述信号量。

一、什么是信号量/信号量集?

1.什么是信号量

        信号量是一个计数器。信号量用于实现进程间的同步和互斥。而可以取多个正整数的信号量被称为通用信号量。

        对信号量的使用场景的解读

        房间:临界资源(操作系统中的多个进程,他们共享各种资源,然而很多资源1次只能供1个进程使用。这种1次仅允许1个进程使用的资源称为临界资源。)

        钥匙:信号量

        只有拿到钥匙才能进入房间(如果进程A正在访问临界资源,则不允许进程B访问临界资源)。

2.什么是信号量集

linux中的信号量不止一个,有很多个。

二、P操作和V操作

p操作:取钥匙

V操作:放回钥匙

三、相关API

1.semget-----创建信号量

通过semget 来创建信号量或获取一个已有的信号量,

函数原型:#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);参数:key 信号量的关键字,可以通过ftok() 创建,详细看 进程间通信——消息队列nsems 信号量的数目(比如1代表信号量集合中有1个信号量)。如果是创建新的信号量,必须要指定nsems。如果是引用现有的信号量,nsems指定为0。shmflg 有两个选项,IPC_CREAT 表示内核中没有此信号量则创建它。IPC_EXCL 当和IPC_CREAT一起使用时,如果信号量已经存在,则返回错误。返回值:
成功返回标识符,否则返回-1。通过errno和perror函数可以查看错误信息。注意:
通过nsems 可以看出,创建一个信号量,里面可能包含多个信号量值。该信号量就相当于一个集合。该值表明有多少个共享资源单位可供共享应用。下面semctl 也是通过nsems 中的某一个进行操作。
如果nsems 设为1,该信号量又被称为二元信号量(binary semaphore)

2.semctl------初始化信号量

这个函数可以有3个参数或者4个参数

       #include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);@semid:信号量数组标识@semnum:要操作的信号量数组的信号量下标(比如:0代表操作第0个信号量)(该下表和数组下标一致,比如有1个信号量,则下表为0,有2个信号量则下表为1)@cmd:IPC_STAT 查询此信号量数组的数据存入arg.buf(buf为struct semid_ds结构体指针)IPC_RMID 删除指定semid的信号量数组GETVAL 获取信号量的当前值,SETVAL 设置信号量的值,初始化要用的命令
————————————————
版权声明:本文为CSDN博主「abist」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/abist/article/details/117606809This  function  has  three  or  four arguments, depending on cmd.  Whenthere are four, the fourth has the type union semun.  The calling  pro‐gram must define this union as follows:当使用4个参数时,要定义下面的联合体union semun {int              val;    /* 钥匙的数量 */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */};

3.semop---信号量的操作

int  semop(int  semid,struct sembuf  *sops,size_t nsops);函数的参数 
参数semid 为信号量集的标识符;
参数 sops 指向进行操作的结构体数组的首地址;
参数 nsops 指出将要进行操作的信号的个数。返回值
semop 函数调用成功返回 0,失败返回 -1。sembuf 结构体对应一个特定信号的操作。
该结构定义在 linux/sem.h,如下所示:
struct  sembuf{unsigned short   sem_num;      //信号在信号集中的索引,0代表第一个信号,1代表第二个信号  short            sem_op;      //操作类型short            sem_flg;    //操作标志
};
sem_num标识信号量集中的第几个信号量,0表示第1个,1表示第2个,nsems - 1表示最后一个。sem_op标识对信号量的所进行的操作类型。对信号量的操作有三种类型:sem_op > 0,对该信号量执行挂出操作,挂出的值由sem_op决定,系统会把sem_op的值加到该信号量的当前值semval(参考文章开头关于每个信号量结构的定义)上。如果sem_flag指定了SEM_UNDO(还原)标志,那么相应信号量的semadj值会减掉sem_op的值。下面会说明semadj的含义。sem_op < 0,对该信号量执行等待操作,当信号量的当前值semval >= -sem_op时,semval减掉sem_op的绝对值,为该线程分配对应数目的资源。如果指定SEM_UNDO,相应信号量的semadj就加上sem_op的绝对值。    当semval < -sem_op时,相应信号量的semncnt就加1,调用线程被阻塞,直到semval >= -sem_op,当此条件满足时,调用线程被唤醒,执行相应的分配操作,然后semncnt减去1。sem_op = 0,表示调用者希望semval变为0。如果为0则立即返回,如果不为0,相应信号量的semzcnt加1,调用调用线程被阻塞。sem_flag:信号量操作的属性标志,如果为0,表示正常操作,如果为IPC_WAIT,使对信号量的操作时非阻塞的。即指定了该标志,调用线程在信号量的值不满足条件的情况下不会被阻塞,而是直接返回-1,并将errno设置为EAGAIN。如果为SEM_UNDO,那么将维护进程对信号量的调整值,以便进程结束时恢复信号量的状态。下面解释一下与单个信号量相关的几个值:
semval:信号量的当前值,在文章开头信号量的结构中已提到。
semncnt:等待semval变为大于当前值的线程数。在文章开头信号量的结构中已提到。
semzcnt:等待semval变为0的线程数。在文章开头信号量的结构中已提到。
semadj:指定信号量针对某个特定进程的调整值。只有sembuf结构的sem_flag指定为SEM_UNDO后,semadj才会随着sem_op而更新。讲简单一点:对某个进程,在指定SEM_UNDO后,对信号量semval值的修改都会反应到semadj上,当该进程终止的时候,内核会根据semadj的值,重新恢复信号量之前的值。

4.P操作(取钥匙)

自定义

要利用semop()来实现void PGetKey(int id)//id是semget()的返回值
{struct sembuf set;//该结构定义在 linux/sem.hset.sem_num = 0;//这是下标,0代表第1个信号;set.sem_op = -1;//是指对钥匙数量减1;set.sem_flag = SEM_UNDO;//配置为SEM_UNDO当进程终止时,自动取消对该钥匙的操作semop(id,&set,1);
}

5.V操作(放回钥匙)

自定义

要利用semop()来实现void VputbackKey(int id)//id是semget()的返回值
{struct sembuf set;//该结构定义在 linux/sem.hset.sem_num = 0;//这是下标,0代表第1个信号;set.sem_op = 1;//是指对钥匙数量加1;set.sem_flag = SEM_UNDO;//配置为SEM_UNDO当进程终止时,自动取消对该钥匙的操作semop(id,&set,1);
}

四、实验

实验要求:

最初状态:没有钥匙。

创建子进程后,

        让父进程取钥匙(由于钥匙的数量为0,所以父进程取钥匙的动作被阻塞),使用结束(访问临界资源)后打印“father process”,放回钥匙。

        让子进程放钥匙,然后打印“child process”。

(由于最初父进程会被阻塞,所以程序运行结果是先打印"child process"后打印“father process")

思路分析

(1)生成键值

(2)创建信号量

(3)初始化信号量:定义联合体-->钥匙的个数置为0--->使用semctl初始化信号量

(4)创建子进程

(5)判断父子进程

                --->在父进程里:取钥匙,打印“father  process”,放回钥匙。

                ---->在子进程里:放钥匙,打印“child process"

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>union semun {int              val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */};void VputbackKey(int id)//id是semget()的返回值
{struct sembuf set;//该结构定义在 linux/sem.hset.sem_num = 0;//这是下标,0代表第1个信号;set.sem_op = 1;//是指对钥匙数量加1;set.sem_flg = SEM_UNDO;//配置为SEM_UNDO当进程终止时,自动取消对该钥匙的操作semop(id,&set,1);printf("put back key\n");
}void PGetKey(int id)//id是semget()的返回值
{struct sembuf set;//该结构定义在 linux/sem.hset.sem_num = 0;//这是下标,0代表第1个信号;set.sem_op = -1;//是指对钥匙数量减1;set.sem_flg = SEM_UNDO;//配置为SEM_UNDO当进程终止时,自动取消对该钥匙的操作semop(id,&set,1);printf("get key\n");
}int main()
{key_t key;int semid;key = ftok(".",2);semid = semget(key,1,IPC_CREAT|0666);union semun initsem;initsem.val = 0;semctl(semid,0,SETVAL,initsem);int pid = fork();if(pid > 0){PGetKey(semid);printf("father process\n");VputbackKey(semid);}else if(pid == 0){VputbackKey(semid);printf("child process\n");}else{printf("fork error\n");}return 0;
}

五、如何销毁钥匙

semctl(semid,0,IPCIPC_RMID);//3个参数

http://www.lryc.cn/news/64367.html

相关文章:

  • 《中国教育报》投稿邮箱编辑部征稿
  • Photoshop如何使用绘画和图像修饰之实例演示?
  • 【C++】布隆过滤器
  • 功能齐全的 ESP32 智能手表,具有多个表盘、心率传感器硬件设计
  • 微服务不是本地部署的最佳选择,不妨试试模块化单体
  • 解读Toolformer
  • FCOS3D Fully Convolutional One-Stage Monocular 3D Object Detection 论文学习
  • Xpath学习笔记
  • 网络编程之 Socket 套接字(使用数据报套接字和流套接字分别实现一个小程序(附源码))
  • What Are Docker Image Layers?
  • 范数详解-torch.linalg.norm计算实例
  • postgresdb备份脚本
  • MATLAB程序员投简历的技巧解析,如何写出有亮点的简历
  • 颜色空间转换RGB-YCbCr
  • 年薪40万程序员辞职炒股,把一年工资亏光了,得了抑郁症,太惨了
  • 10分钟如何轻松掌握JMeter使用方法?
  • [NLP]如何训练自己的大型语言模型
  • LeetCode1047. 删除字符串中的所有相邻重复项
  • 3。数据结构(3)
  • QT停靠窗口QDockWidget类
  • 【LeetCode】139. 单词拆分
  • 【三维重建】NeRF原理+代码讲解
  • IntelliJ IDEA 社区版2021.3配置SpringBoot项目详细教程及错误解决方法
  • Qt中QDebug的使用
  • vue使用路由的query配置项时如何清除地址栏的参数
  • Redis-列表(List)
  • ripro主题修改教程-首页搜索框美化教程
  • 写作业用白光还是暖光?盘点色温4000K的护眼台灯
  • Java时间类(一)-- SimpleDateFormat类
  • 07 Kubernetes 网络与服务管理