学习记录——day28 信号量集

news/2024/9/17 19:07:22/ 标签: 学习

目录

一、信号量集

1、信号量集的API函数接口

二、 将信号量集函数再次封装

 1、sem.h

2、sem.c

三、使用信号量集完成共享内存的进程同步

 1、发送端

2、接收端


一、信号量集

        信号量集,其实就是无名信号量的集合,主要用于完整多个进程间的同步问题.。

        使用 semget 函数申请信号量集时,需指定申请的信号量的数量,函数会对信号量从零开始编号;对信号量进行初始化则、删除等操作需要使用 semctl 函数;申请和释放信号量时需指定信号量编号使用的时 semop 函数。

        由于直接使用函数库提供的函数操作繁琐,使用不方便,所以对信号量集相关函数进行再次封装,以便使用。

1、信号量集的API函数接口

1、创建一个信号量集
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semget(key_t key, int nsems, int semflg);
       功能:创建信号灯集
       参数1:用于创建信号量集的key值,可以是IPC_PRIVATE,也可以由ftok创建出来
       参数2:创建的信号灯集中的信号量的个数
       参数3:创建的标识
                IPC_CREAT:表示本次操作要创建一个信号量集,如果该key值对应的信号量集已经存在,则直接打开该信号量集对象
                IPC_EXCL:表示本次确保要创建一个新的信号量集,如果该信号量集已经存在,则该函数报错,错误码为EEXIST
                创建文件的权限,也在该参数中,使用位或连接
        返回值:成功返回创建出来的共享内存的id,失败返回-1并置位错误码

2、信号量集的控制函数
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semctl(int semid, int semnum, int cmd, ...);
       功能:信号量集的控制函数
       参数1:信号量集的id
       参数2:要控制的信号灯集中的信号灯的编号,编号从0开始的
       参数3:控制指令
           IPC_RMID:删除信号灯集,此时参数4可以省略不写,参数2被忽略
           IPC_STAT\IPC_SET:获取或设置信号灯集的属性
           struct semid_ds {
               struct ipc_perm sem_perm;  /* Ownership and permissions */
               time_t          sem_otime; /* Last semop time */
               time_t          sem_ctime; /* Last change time */
               unsigned long   sem_nsems; /* No. of semaphores in set */
           };

       The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):

           struct ipc_perm {
               key_t          __key; /* Key supplied to semget(2) */
               uid_t          uid;   /* Effective UID of owner */
               gid_t          gid;   /* Effective GID of owner */
               uid_t          cuid;  /* Effective UID of creator */
               gid_t          cgid;  /* Effective GID of creator */
               unsigned short mode;  /* Permissions */
               unsigned short __seq; /* Sequence number */
           };
           GETVAL\SETVAL:设置参数2这一个信号灯中的值,放到参数4提供的整数中
           GETALL\SETALL:设置或获取所有信号灯的值,放入到参数4提供的数组中
        参数4:可变参数,会根据参数3的不同,使用的类型也不同,所以是一个共用体变量
        union semun {
               int              val;    /* 参数3为 SETVAL使用该成员 */
               struct semid_ds *buf;    /* 参数3位 IPC_STAT, IPC_SET使用该成员 */
               unsigned short  *array;  /* 参数3位 GETALL, SETALL 使用该成员*/
               struct seminfo  *__buf;  /* 参数3为 IPC_INFO
 使用该成员*/
           };
        返回值:对于GETVAL成功返回获取的信号号
                对于GETVAL:返回信号量集ID
                失败全部返回-1并置位错误码

 3、对信号灯进行PV操作(申请资源和释放资源)
        #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semop(int semid, struct sembuf *sops, size_t nsops);         
       功能:完成对信号灯集的相关操作
       参数1:信号灯集的id
       参数2:执行的操作,是一个结构体,成员如下
           unsigned short sem_num;  /* 要操作的信号灯的编号 */
           short          sem_op;   执行的操作:正数表示释放资源,负数表示申请资源
           short          sem_flg;  是否阻塞:0表示阻塞,IPC_NOWAIT表示非阻塞
        参数3:参数2的个数
        返回值:        成功返回0,失败返回-1并置位错误码              

二、 将信号量集函数再次封装

 1、sem.h

#ifndef SEM_H
#define SEM_H
#include <myhead.h>union semun
{int val;               /* 参数3为 SETVAL使用该成员 */struct semid_ds *buf;  /* 参数3位 IPC_STAT, IPC_SET使用该成员 */unsigned short *array; /* 参数3位 GETALL, SETALL 使用该成员*/struct seminfo *__buf; /* 参数3为 IPC_INFO
使用该成员*/
};// 创建信号灯集并初始化
// 返回值:信号灯集id
// 参数:信号灯集中灯的个数
int sem_create(int semcont);// 执行申请某个信号灯的资源操作(P操作)
// 返回值:成功返回0,失败返回-1
// 参数1:信号灯集id
// 参数2:要操作的信号灯编号
int P(int semid, int semnum);// 执行释放某个信号灯的资源操作(V操作)
// 返回值:成功返回0,失败返回-1
// 参数1:信号灯集id
// 参数2:要操作的信号灯编号
int V(int semid, int semnum);// 删除信号灯集
// 参数:信号灯id号
int sem_del(int semid);#endif

2、sem.c

#include"sem.h"
//定义设置某个信号灯的值的函数
int init_semnum(int semid, int semnum)
{int val = 0;printf("请输入第%d号灯的初始值:", semnum+1);scanf("%d", &val);getchar();//准备共用体变量union semun buf;buf.val = val;     //要传递的数据//调用semctl函数完成对信号灯的值的设置if(semctl(semid, semnum, SETVAL, buf)==-1){perror("semctl error");return -1;}return 0;
}//创建信号灯集并初始化
int sem_create(int semcont)
{//1、创建key值key_t key = ftok("/", 't');if(key == -1){perror("ftok error");return -1;}//2、创建信号量集int semid = semget(key, semcont, IPC_CREAT|IPC_EXCL|0664);if(semid == -1){//对错误码进行判断if(errno == EEXIST){//说明消息队列已经存在,直接打开即可semid = semget(key, semcont, IPC_CREAT|0664);return semid;}perror("semget error");return -1;}//3、对信号灯进行初始化for(int i=0; i<semcont; i++){init_semnum(semid, i);}//4、返回创建的信号灯集idreturn semid;}//P操作:申请资源
int P(int semid, int semnum)
{//定义操作结构体变量struct sembuf buf;buf.sem_num = semnum;   //要操作的信号灯buf.sem_op = -1;        //表示申请资源,如果semnum灯的资源为0,则阻塞buf.sem_flg = 0;          //表示如果没有资源,则阻塞//调用函数进行申请资源if(semop(semid, &buf, 1) ==-1){perror("P error");return -1;}//成功返回0return 0;
}//V操作:释放资源
int V(int semid, int semnum)
{//定义操作结构体变量struct sembuf buf;buf.sem_num = semnum;   //要操作的信号灯buf.sem_op = 1;        //表示申请资源,如果semnum灯的资源为0,则阻塞buf.sem_flg = 0;          //表示如果没有资源,则阻塞//调用函数进行申请资源if(semop(semid, &buf, 1) ==-1){perror("V error");return -1;}//成功返回0return 0;
}//删除信号灯集
int sem_del(int semid)
{//调用semctl删除信号灯集if(semctl(semid, 1, IPC_RMID) ==-1){perror("delete error");return -1;}printf("信号灯集删除成功\n");return 0;
}

三、使用信号量集完成共享内存的进程同步

 1、发送端

#include <myhead.h>
#include <sys/user.h>
#include "sem.h"int main(int argc, char const *argv[])
{//1、创建/打开信号量集int semid = sem_create(2);//2、创建key值用于创建共享内存段key_t key = ftok("/",'t');if (key == -1){perror("ftok error");return -1;}printf("key = %d\n",key);//3、创建一个共享内存的对象int shmid = shmget(key,PAGE_SIZE,IPC_CREAT|0664);if (shmid == -1){perror("shmget error");return -1;}printf("shmid = %d\n",shmid);//4、将共享内存段映射到程序中char *addr = (char *)shmat(shmid,NULL,0);printf("addr = %p\n",addr);//5、向共享内存中写入数据while(1){P(semid,0);printf("输入信息:");fgets(addr,PAGE_SIZE,stdin);addr[strlen(addr)-1] = 0;V(semid,1);if (strcmp(addr,"quit") == 0){printf("退出\n");break;}}//6、取消映射关系if (shmdt(addr) == -1){perror("shmdt error");return -1;}return 0;
}

2、接收端

#include <myhead.h>
#include <sys/user.h>int main(int argc, char const *argv[])
{// 1、创建key值用于创建共享内存段key_t key = ftok("/", 't');if (key == -1){perror("ftok error");return -1;}printf("key = %d\n", key);// 2、创建一个共享内存的对象int shmid = shmget(key, PAGE_SIZE, IPC_CREAT | 0664);if (shmid == -1){perror("shmget error");return -1;}printf("shmid = %d\n", shmid);// 3、将共享内存段映射到程序中char *addr = (char *)shmat(shmid, NULL, 0);printf("addr = %p\n", addr);//4、创建/打开信号量集int semid = sem_create(2);// 5、读出共享内存的数据while(1){P(semid,1);printf("接收到:%s\n",addr);if (strcmp(addr,"quit") == 0){break;}V(semid,0);}// 6、取消映射关系if (shmdt(addr) == -1){perror("shmdt error");return - 1;}// 7、删除共享内存if (shmctl(shmid, IPC_RMID, NULL) == -1){perror("shmctl error");return -1;}return 0;
}


http://www.ppmy.cn/news/1507253.html

相关文章

127. Go反射基本原理

文章目录 反射基础 - go 的 interface 是怎么存储的&#xff1f;iface 和 eface 的结构体定义&#xff08;runtime/iface.go&#xff09;&#xff1a;_type 是什么&#xff1f;itab 是什么&#xff1f; 反射对象 - reflect.Type 和 reflect.Value反射三大定律Elem 方法reflect.…

【数据结构】三、栈和队列:6.链队列、双端队列、队列的应用(树的层次遍历、广度优先BFS、先来先服务FCFS)

文章目录 2.链队列2.1初始化&#xff08;带头结点&#xff09;不带头结点 2.2入队&#xff08;带头结点&#xff09;2.3出队&#xff08;带头结点&#xff09;❗2.4链队列c实例 3.双端队列考点:输出序列合法性栈双端队列 队列的应用1.树的层次遍历2.图的广度优先遍历3.操作系统…

【Kubernetes】Service 概念与实战

Service 概念与实战 1.通过 Service 向外部暴露 Pod2.Service 的多端口设置3.集群内部的 DNS 服务4.无头 Service 在 Kubernetes 中部署的应用可能对应一个或者多个 Pod&#xff0c;而每个 Pod 又具有独立的 IP 地址。Service&#xff08;服务&#xff09;能够为一组功能相同的…

大数据-72 Kafka 高级特性 稳定性-事务 (概念多枯燥) 定义、概览、组、协调器、流程、中止、失败

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Linux中安装MYSQL数据库

文章目录 一、MYSQL数据库介绍1.1、MySQL数据库的基本概述1.2、MySQL数据库的主要特性1.3、MySQL数据库的技术架构与组件1.4、MySQL数据库的应用与扩展性1.5、MySQL数据库的许可模式与开源生态 二、MySQL Workbench和phpMyAdmin介绍2.1、MySQL Workbench介绍2.2、phpMyAdmin介绍…

【学习笔记】Day 9

一、进度概述 1、inversionnet_train 试运行——成功 二、详情 1、inversionnet_train 试运行 在经历了昨天的事故后&#xff0c;今天最终成功运行了 inversionnet_train&#xff0c;运行结果如下&#xff1a; 经观察&#xff0c;最开始 loss 值大概为 0.5 左右 随着训练量的增…

使用Selenium调试Edge浏览器的常见问题与解决方案

背景介绍 在当今互联网时代&#xff0c;网页爬虫已经成为数据获取的重要手段。而Selenium作为一款功能强大的自动化测试工具&#xff0c;被广泛应用于网页爬取任务中。虽然Chrome浏览器是Selenium用户的常见选择&#xff0c;但在某些工作环境中&#xff0c;我们可能需要使用Ed…

Ubuntu24.04设置国内镜像软件源

参考文章&#xff1a; Ubuntu24.04更换源地址&#xff08;新版源更换方式&#xff09; - 陌路寒暄 一、禁用原来的软件源 Ubuntu24.04 的源地址配置文件发生改变&#xff0c;不再使用以前的 sources.list 文件&#xff0c;升级 24.04 之后&#xff0c;该文件内容变成了一行注…

牛客-热身小游戏

题目链接&#xff1a;热身小游戏 第一种写法&#xff1a;线段树 介绍第二种写法&#xff1a;并查集 对于一些已经查询过的点&#xff0c;我们可以往后跳&#xff0c;进行路径压缩&#xff0c;他们的父亲为下一个点。 a数组记录[ l , r ] 之间的乘积&#xff0c;初始值为1。…

haproxy知识点整理

haproxy知识点整理 haproxy七层代理负载均衡什么是负载均衡为什么使用负载均衡 负载均衡类型四层负载均衡七层负载均衡四层和七层的区别 环境搭建:客户端(client)haproxy服务器两台服务器hapserver1hapserver2 简单的haproxy负载均衡 haproxy的基本配置信息global配置proxies配…

17. ADC开发

1. 概述 bes2700 支持2路ADC 2. 硬件连接 3. 软件开发 电压值计算:电压 = 参考电压/4096(2的12次方) * ADC值

linux中安装nginx方法

1、首先确保系统已经安装gcc&#xff0c;如没安装&#xff0c;请先自行安装 2、安装nginx 将openssl-1.1.1j.tar.gz、pcre-8.44.tar.gz、zlib-1.3.tar.gz、nginx-1.20.0.tar.gz解压到当前目录&#xff0c;命令如下&#xff1a; tar -zxvf openssl-1.1.1j.tar.gz tar -zxvf…

【RISC-V设计-08】- RISC-V处理器设计K0A之BMU

【RISC-V设计-08】- RISC-V处理器设计K0A之BMU 文章目录 【RISC-V设计-08】- RISC-V处理器设计K0A之BMU1.简介2.顶层设计3.端口说明4.总线时序4.1 总线写时序4.2 总线读时序 5.代码设计6.总结 1.简介 总线管理单元&#xff08;Bus Management Unit&#xff0c;简称 BMU&#x…

Linux安全与高级应用(四)深入探索MySQL数据库:安装、管理与安全实践

文章目录 标题&#xff1a;全面解析LAMP平台部署及应用第一部分&#xff1a;LAMP平台概述第二部分&#xff1a;准备工作第三部分&#xff1a;安装和配置PHP第四部分&#xff1a;配置Apache第五部分&#xff1a;测试LAMP平台第六部分&#xff1a;部署phpMyAdmin总结 &#x1f44…

【海贼王航海日志:前端技术探索】CSS你了解多少?(三)

目录 1 -> 浏览器调试工具——查看CSS属性 1.1 -> 打开浏览器 1.2 -> 标签页含义 1.3 -> elements标签页使用 2 -> 元素的显示模式 2.1 -> 块级元素 2.2 -> 行内元素/内联元素 2.3 -> 改变显示模式 3 -> 盒模型 3.1 -> 边框 3.2 ->…

MySql-索引事务

在面试中&#xff0c;对于mysql相关的面试题常看的两部分也是我们学习时需要重点了解的内容&#xff1a;索引与事务。 目录 索引 B树 B树结构 B树创建 事务 重点&#xff1a;事务的基本特性 一、原子性 二、一致性 三、持久性 四、隔离性 索引 索引的核心内容&#…

白骑士的Matlab教学进阶篇 2.3 信号处理

系列目录 上一篇&#xff1a;白骑士的Matlab教学进阶篇 2.2 数值计算 信号处理在现代工程和科学领域中扮演着至关重要的角色。MATLAB作为一个强大的数学计算平台&#xff0c;提供了丰富的工具和函数来帮助研究人员和工程师处理各种信号问题。本文将深入介绍MATLAB中信号处理的…

C# 集合操作的艺术:深入解析数据分区策略与高效筛选技巧(Skip、SkipWhile、Take、TakeWhile)

文章目录 概述Skip 和 SkipWhile 方法Take 和 TakeWhile 方法综合应用示例总结 在C#中&#xff0c;LINQ&#xff08;语言集成查询&#xff09;提供了一种非常方便的方式来处理数据集合。本文将详细介绍四种数据分区方法&#xff1a;Skip、SkipWhile、Take、TakeWhile&#xff0…

【Pytorch实用教程】PyTorch中的torch.clamp()函数

torch.clamp() 是 PyTorch 中一个用于张量元素值限制的函数。它可以将张量中的元素限制在一个指定的范围内,即将所有小于最小值的元素设为最小值,将所有大于最大值的元素设为最大值。 函数签名 torch.clamp(input, min=None, max=None, *, out=None)参数

springboot Isolation.READ_COMMITTED不生效解决办法

问题描述 springbootmybatis可读已提交不生效&#xff0c;先在springboot查询出结果然后在数据库修改值后在Java再次读取&#xff0c;结果读取的还是修改之前的值 原因: 是mybatis二级缓存导致的 解决办法 方案一 ​​​​​​​Resource private SqlSession sqlSession;…