【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 使用缓存改善…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...