【Linux-Day10-信号量,共享内存,消息队列】
信号量
信号量描述
信号量是一个特殊的变量,一般取正数值。它的值代表允许访问的资源数目,获取资源 时,需要对信号量的值进行原子减一,该操作被称为 P 操作。
当信号量值为 0 时,代表没有资源可用,P 操作会阻塞。
释放资源时,需要对信号量的值进行原子加一,该操作被称为 V 操作。
信号量主要用来同步进程。
信号量的值如果只取 0,1,将其称为二值信号量。
如果信 号量的值大于 1,则称之为计数信号量。
**临界资源:同一时刻,只允许被一个进程或线程访问的资源 **
**临界区:访问临界资源的代码段 **
信号量使用
semget(); 创建或者获取已存在的信号量
int semget(key_t key, int nsems, int semflg);
semget()成功返回信号量的 ID, 失败返回-1
key:两个进程使用相同的 key 值,就可以使用同一个信号量
nsems:内核维护的是一个信号量集,在新建信号量时,其指定信号量集中信号 量的个数
semflg 可选: IPC_CREAT IPC_EXCL
semop()对信号量进行改变,做 P 操作或者 V 操作
int semop(int semid, struct sembuf *sops, unsigned nsops);
semop()成功返回 0,失败返回-1
struct sembuf
{
unsigned short sem_num; //指定信号量集中的信号量下标
short sem_op; //其值为-1,代表 P 操作,其值为 1,代表 V 操作
short sem_flg; //SEM_UNDO
};
semctl()控制信号量
int semctl( int semid, int semnum, int cmd, …);
semctl()成功返回 0,失败返回-1semid:信号量的ID
cmd 选项: SETVAL IPC_RMID
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
封装一个c文件实现创建一个信号
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/sem.h>union semun
{int val;
};
static int semid = -1;
void sem_init()
{semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//全新创建信号量,如果存在就失败if ( semid == -1 )//失败,表示该(key_t)1234)信号已存在{semid = semget((key_t)1234,1,0600);//获取已存在的信号量idif ( semid == -1){printf("semget err\n");}}else//全新创建成功,那么要进行初始化{union semun a;a.val = 1;//信号量的初始值if ( semctl(semid,0,SETVAL,a) == -1)//设置初始值{printf("semctl err\n");}}
}
void sem_p()
{struct sembuf buf;buf.sem_num = 0;buf.sem_op = -1; //p操作buf.sem_flg = SEM_UNDO;if ( semop(semid,&buf,1) == -1){printf("semop p err\n");}}
void sem_v()
{struct sembuf buf;buf.sem_num = 0;buf.sem_op = 1; //v操作buf.sem_flg = SEM_UNDO;if ( semop(semid,&buf,1) == -1){printf("semop v err\n");}
}
void sem_destroy()
{if ( semctl(semid,0,IPC_RMID) == -1){printf("semctl destroy err\n");}}
假设资源只有一份,每轮a进程使用2次 ,b进程使用3次,如何解决。
我们可以使用信号量解决临界资源问题

a进程代码
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "sem.c"
int main()
{sem_init();//for(int i = 0; i < 5; i++){//psem_p();printf("a");fflush(stdout);int n = rand() % 3;sleep(n);printf("a");fflush(stdout);sem_v();n = rand() % 3;sleep(n);}sleep(10);sem_destroy();return 0;
}
b进程代码
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "sem.c"
int main()
{sem_init();for(int i = 0; i < 5; i++){sem_p();printf("b"); int n = rand() % 3;sleep(n);printf("bb");fflush(stdout);sem_v();n = rand() % 3;sleep(n);}return 0;
}
效果如下:a,b不会同时访问该资源

共享内存
共享内存原理
共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理 内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访 问共享内存中的地址,就好像它们是由 malloc 分配的一样。如果某个进程向共享内存写入了 数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。由于它并未提供 同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。

shemget()创建共享内存
int shmget(key_t key, size_t size, int shmflg);
shmget()用于创建或者获取共享内存
shmget()成功返回共享内存的 ID, 失败返回-1
key: 不同的进程使用相同的 key 值可以获取到同一个共享内存
size: 创建共享内存时,指定要申请的共享内存空间大小
shmflg: IPC_CREAT IPC_EXCL
shmat() 用来创建映射
void * shmat( int shmid, const void *shmaddr, int shmflg);
shmat()将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上
shmat()成功返回返回共享内存的首地址,失败返回 NULL
shmaddr:一般给 NULL,由系统自动选择映射的虚拟地址空间
shmflg: 一般给 0, 可以给 SHM_RDONLY 为只读模式,其他的为读写
shmdt()用来断开映射
int shmdt( const void *shmaddr);
shmdt()断开当前进程的 shmaddr 指向的共享内存映射
shmdt()成功返回 0, 失败返回-1
shmctl()用来控制共享内存
int shmctl( int shmid, int cmd, struct shmid_ds *buf);
shmctl()成功返回 0,失败返回-1
cmd: IPC_RMID 32. *
测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{int shmid = shmget((key_t)1234,sizeof(char)*128,IPC_CREAT|0600);if(shmid == -1){printf("shmget error\n");exit(1);}char* p = shmat(shmid,NULL,SHM_W);if(p == NULL){printf("shmat error\n");exit(2);}while(1){char buff[128]={0};printf("parent input: ");fflush(stdout);fgets(buff,127,stdin);if(strncmp(buff,"end",3) == 0){break;}int pid = fork();if(pid == -1) break;if(pid != 0){ strcpy(p,buff);}if(pid == 0){char* ptr=shmat(shmid,NULL,0);printf("child read: %s\n",ptr);shmdt(ptr);exit(0);}wait(NULL);}shmdt(p);exit(0);
}
结果如图:

下面我们用信号量来实现对共享内存的访问。

代码如下:
sem.c
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/sem.h>#define SEM1 0
#define SEM2 1
union semun
{int val;
};
static int semid = -1;void sem_init()
{semid = semget((key_t)1234, 2, IPC_CREAT | IPC_EXCL | 0600); // 全新创建信号量,如果存在就失败if (semid == -1) // 失败,表示已存在{semid = semget((key_t)1234, 2, 0600); // 获取已存在的信号量idif (semid == -1){printf("semget err\n");}}else // 全新创建成功,那么要进行初始化{union semun a;const int ar[2] = {1, 0};for (int i = 0; i < 2; ++i){a.val = i; // 信号量的初始值if (semctl(semid, i, SETVAL, a) == -1) // 设置初始值{printf("semctl err\n");}}}
}
void sem_p(int sem)
{struct sembuf buf;buf.sem_num = sem;buf.sem_op = -1; // pbuf.sem_flg = SEM_UNDO;if (semop(semid, &buf, 1) == -1){printf("semop p err\n");}
}
void sem_v(int sem)
{struct sembuf buf;buf.sem_num = sem;buf.sem_op = 1; // vbuf.sem_flg = SEM_UNDO;if (semop(semid, &buf, 1) == -1){printf("semop v err\n");}
}
void sem_destroy()
{if (semctl(semid, 0, IPC_RMID) == -1){printf("semctl destroy err\n");}
}
read.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
#include "sem.c"int main()
{int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);if ( shmid == -1 ){printf("shmget err\n");exit(1);}char * s = (char*)shmat(shmid,NULL,0);if ( s == (char*)-1){printf("shmat err\n");exit(1);}sem_init();while( 1 ){sem_p(SEM2);if ( strncmp(s,"end",3) == 0 ){break;}printf("read:%s\n",s);sem_v(SEM1);} shmdt(s);shmctl(shmid,IPC_RMID,NULL);sem_destroy();
}
write.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
#include "sem.c"int main()
{int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);if ( shmid == -1 ){printf("shmget err\n");exit(1);}char* s = (char*)shmat(shmid,NULL,0);if ( s == (char*)-1) {printf("shmat err\n");exit(1);} sem_init();while( 1 ){printf("input: ");char buff[128] = {0};fflush(stdout);fgets(buff,128,stdin);sem_p(SEM1);strcpy(s,buff);sem_v(SEM2);if ( strncmp(buff,"end",3) == 0){break;}}shmdt(s);
}
消息队列

接口介绍
1.msgget() 获取消息队列
int msgget(key_t key, int msqflg);
msgget()创建或者获取一个消息队列
msgget()成功返回消息队列 ID,失败返回-1
msqflg: IPC_CREAT
2.msgsnd()发送信息
int msgsnd( int msqid, const void *msqp, size_t msqsz, int msqflg);
msgsnd()发送一条消息,消息结构为:
struct msgbuf
{
long mtype; // 消息类型, 必须大于 0 必须有
char mtext[1]; // 消息数据
};
msgsnd()成功返回 0, 失败返回-1
msqsz: 指定 mtext 中有效数据的长度
msqflg:一般设置为 0 可以设置 IPC_NOWAIT
3.msgrcv()接收消息
ssize_t msgrcv( int msqid, void *msgp, size_t msqsz, long msqtyp, int msqflg);
msgrcv()接收一条消息
msgrcv()成功返回 mtext 中接收到的数据长度, 失败返回-1
msqtyp: 指定接收的消息类型,类型可以为 0(忽略类型)
msqflg: 一般设置为 0 可以设置 IPC_NOWAIT
4.msgctl()控制消息队列
int msgctl( int msqid, int cmd, struct msqid_ds *buf);
msgctl()控制消息队列
msgctl()成功返回 0,失败返回-1
cmd: IPC_RMID
测试代码:
msgread.c //从消息队列中读取
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/msg.h>
struct message
{long type;//固定char msg[16];
};
int main()
{int msgid=msgget((key_t)1234,IPC_CREAT|0600);if(msgid==-1){printf("msgget err\n");exit(1);}struct message dt;msgrcv(msgid,&dt,16,1,0);//0代表不区分类型printf("read message:%s\n",dt.msg);exit(0);
}
msgcreat.c //写入数据
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/msg.h>
struct message //自定义结构体
{long type;//固定的char msg[16];
};
int main()
{int msgid=msgget((key_t)1234,IPC_CREAT|0600);if(msgid==-1){printf("msgget err\n");exit(1);}struct message dt;dt.type=1;strcpy(dt.msg,"China");msgsnd(msgid,&dt,16,0);exit(0);
}

自定义的结构体第一个是消息类型,读取消息是按类型进行的,0为不区分消息类型,可以全部读取。
写入到消息队列的数据在内存中,除了删除和重启系统,不会丢失。
相关文章:
【Linux-Day10-信号量,共享内存,消息队列】
信号量 信号量描述 信号量是一个特殊的变量,一般取正数值。它的值代表允许访问的资源数目,获取资源 时,需要对信号量的值进行原子减一,该操作被称为 P 操作。 当信号量值为 0 时,代表没有资源可用,P 操作…...
使用IntelliJ IDEA本地启动调试Flink流计算工程的2个异常解决
记录:471 场景:使用IntelliJ IDEA本地启动调试Flink流计算时,报错一:加载DataStream报错java.lang.ClassNotFoundException。报错二:No ExecutorFactory found to execute the application。 版本:JDK 1.…...
对象及日期对象
对象 1.什么是对象 类是对象的抽象,对象是类的实例 程序算法数据结构 万物皆对象,对象是一个具体的事物,看到见摸得着,对象是一组无序相关属性和方法的集合(无序,所以对象没有length属性),所有事物都是对象,列如字符串,数值,数组,函数等. 属性:事物的特征,在对象中用属性表…...
鼠标滚轮编码器解析
文章目录 前言一、鼠标滚轮编码器逻辑?二、使用步骤 1.引入库2.读入数据总结 前言 鼠标滚轮编码器为三脚接入,一个COM脚C(一般是接地),两个脉冲波形输入脚A、B,转动滚轮编码器会在两个脉冲输入脚上产生脉冲…...
【PTA】攀拓(PAT)- 程序设计(甲级)2023年春季考试
个人学习记录,代码难免不尽人意。 今天又斥资买了今年春季的真题一试,呃,感觉尽力了,89分,在当年排名23,感觉还不错,没有出现读不懂的题目和没有思路的情况,扣的11分分别是第二题两个…...
Spring Cloud Gateway 实现原理
Spring Cloud Gateway是Spring Cloud生态系统中的一个组件,用于构建基于Spring Boot的微服务架构中的网关服务。它的主要目的是提供一种灵活的方式来路由、过滤和转换HTTP请求,从而允许您构建强大、高性能的微服务应用程序。 以下是Spring Cloud Gatewa…...
嘉泰实业:真实低门槛,安全有保障
在互联网金融大行其道的当下,无论用户是多么的青睐、喜爱这种便捷的理财方式,也一定得把资金安全放在心上。要投就投那些实力背景雄厚,诚信经营的平台,可以选择投资用户基数庞大的理财老品牌,也可以选择发展势头迅猛的…...
spring boot 2.7 -> 3.0升级指南
spring boot提供一个版本迁移指南 2.7 -> 3.0...
MQTT 连接优化指南
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...
算法和数据结构学习中的一些小的工具函数
算法和数据结构学习中的一些小的工具函数 作者:Grey 原文地址: 博客园:算法和数据结构学习中的一些小的工具函数 CSDN:算法和数据结构学习中的一些小的工具函数 提取一个数二进制最右侧的 1 比如二进制为:0100 0…...
解决2K/4K高分屏下Vmware等虚拟机下Kail Linux界面显示问题
问题现象 在我们日常使用VirtualBox、Vmware workstation、Hyper-V等虚拟机安装使用Kali系统,在2K/4K高分辨率电脑下Kali系统界面显示太小,包括各种软件及命令终端字体均无法很直观的看出,影响我们的正常测试及使用。 常规处理思路 很多人…...
【校招VIP】java语言考点之双亲委派模型
考点介绍: 双亲委派是校招面试中的高频考点之一。双亲委派机制定义: 当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载,只有父加载器无法加载这个类的时候࿰…...
2023年阿里云新用户云服务器价格表
阿里云,作为国内领先的云计算服务提供商,一直致力于为全球用户提供安全、稳定、高效的云计算服务。对于新用户来说,阿里云服务器是一个非常不错的选择。那么,阿里云新用户云服务器的价格是怎样的呢?本文将为大家详细介…...
信号相关名词概念汇总-采样周期、泄露、窗函数等
信号相关名词概念汇总-采样周期、泄露、窗函数等 以下为信号相关名词概念的汇总 1 名词解释 采样周期/间隔:采样频率的倒数,两次相邻采样之间的时间间隔采样时间:采样的总时长,即采样点数N和采样周期的乘积采样频率: …...
数字化新零售营销模式如何落地?数字化新零售营销功能推荐
通过科技手段,针对对线下零售店面的客户进行消费行为、频次等的分析,并进一步整合线上线下资源,实现实体零售的效率充分化,便是目前很火的新零售营销模式,能够将实体门店与数字化技术进行有机结合,通过为…...
712. 两个字符串的最小ASCII删除和 -- 动规
712. 两个字符串的最小ASCII删除和 class MinimumDeleteSum:"""712. 两个字符串的最小ASCII删除和https://leetcode.cn/problems/minimum-ascii-delete-sum-for-two-strings/"""def solution(self, s1: str, s2: str) -> int:""&qu…...
python中的小tips
1、注释 1、注释快捷键: Ctrl/ 可以注释掉光标所在的这一行,或者是选中的区域。 对于注释掉的这一行或者这一区域,按下ctrl/则会去掉注释。 2、多行注释 在写多行注释时,英文状态下写三个",会自动变成六个"&…...
高精度(加减乘除)
高精度算法出现的原因 当参与运算的数的范围大大的超出了标准数据类型,如int(-2147483648 ~ 2147483647)或者long long的范围,就需要使用高精度算法来进行数的运算。高精度运算的特点是代码长度比较长,本质是对数学运算…...
java企业数据管理系统
项目介绍 此项目为企业数据管理系统的后端部分,前端部分请参考vue-admin,项目实现了菜单管理、用户管理、角色管理和权限管理四个基础模块,前端菜单管理结合动态路由可自由添加菜单。结合Shiro权限管理实现了菜单和按钮的权限控制。 ❝ 前端…...
【云原生进阶之PaaS中间件】第二章Zookeeper-3.1分布式架构介绍
1 分布式架构详解 1.1 分布式发展历程 1.1.1 单点集中式 特点:App、DB、FileServer都部署在一台机器上。并且访问请求量较少 1.1.2 应用服务和数据服务拆分 特点:App、DB、FileServer分别部署在独立服务器上。并且访问请求量较少 1.1.3 使用缓存改善…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
