【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 使用缓存改善…...
Aspose.Words避坑指南:Java实现Word转PDF时如何去除水印(2023最新版)
Aspose.Words商业应用实战:Java版Word转PDF无水印解决方案深度解析 在企业级文档处理系统中,Word到PDF的转换需求几乎无处不在——合同归档、报告生成、电子发票导出等场景都依赖这一基础功能。作为Java开发者,当我们选择Aspose.Words这一业界…...
别再只会发文本了!SpringBoot整合钉钉机器人,这5种高级消息模板让你的通知更专业
SpringBoot与钉钉机器人:五种高级消息模板实战指南 如果你还在用单调的文本消息推送系统通知,那么你的团队协作工具可能只发挥了50%的潜力。钉钉机器人提供的富文本消息类型,能够将枯燥的系统通知转化为直观、交互式的信息卡片,显…...
英伟达黄仁勋力荐!2026年AI Agent元年,掌握这5大关键技术,成为行业风口!
0****1 什么是AI Agent? 随着人工智能技术加速演进,AI Agent(人工智能代理,常称智能体)正悄然渗透到企业运营与日常生活的各个角落,从大家熟悉的虚拟助手(如Siri、小爱同学、豆包)&a…...
XUnity.AutoTranslator:Unity游戏翻译解决方案的创新方法 | 玩家与开发者实战指南
XUnity.AutoTranslator:Unity游戏翻译解决方案的创新方法 | 玩家与开发者实战指南 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 你是否曾因语言障碍错失优秀的外语游戏?是否在尝…...
EVA-01保姆级教程:Qwen2.5-VL-7B多模态大模型在EVA-01中的本地化安全部署
EVA-01保姆级教程:Qwen2.5-VL-7B多模态大模型在EVA-01中的本地化安全部署 1. 引言:欢迎来到NERV指挥中心 想象一下,你面前有一个能看懂图片、理解图表、甚至能和你讨论图片里发生了什么的智能助手。现在,我们把这个助手装进了一…...
QEMU监视器隐藏玩法:用TCP端口转发实现远程调试(2024最新版)
QEMU监视器隐藏玩法:用TCP端口转发实现远程调试(2024最新版) 在边缘计算和物联网设备调试中,经常需要跨越物理距离管理虚拟机。传统方式要求开发者必须物理接触设备或依赖图形界面,这在分布式场景中显得笨拙且低效。实…...
如何用Python零依赖快速获取百度搜索结果?python-baidusearch深度解析
如何用Python零依赖快速获取百度搜索结果?python-baidusearch深度解析 【免费下载链接】python-baidusearch 自己手写的百度搜索接口的封装,pip安装,支持命令行执行。Baidu Search unofficial API for Python with no external dependencies …...
破解B站评论区识人困境!B站成分检测器让用户画像识别效率飙升8倍
破解B站评论区识人困境!B站成分检测器让用户画像识别效率飙升8倍 【免费下载链接】bilibili-comment-checker B站评论区自动标注成分,支持动态和关注识别以及手动输入 UID 识别 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-comment-checke…...
douyin-downloader:让每个人都能轻松获取无水印视频的技术利器
douyin-downloader:让每个人都能轻松获取无水印视频的技术利器 【免费下载链接】douyin-downloader 项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader 一、问题破局:揭开抖音内容获取的神秘面纱 1.1 内容获取的三大拦路虎 …...
宁波小程序公司提供专业的小程序开发服务
在宁波小程序公司的服务中,我们致力于为客户提供清晰的内容结构和流畅的表达。我们通过深入的需求分析,确保每个项目都能符合客户特定的期望和市场需求。设计阶段注重市场调研,力求在视觉和功能上都能满足用户的使用习惯和偏好。开发过程中&a…...
