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

【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,失败返回-1

semid:信号量的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-信号量,共享内存,消息队列】

信号量 信号量描述 信号量是一个特殊的变量&#xff0c;一般取正数值。它的值代表允许访问的资源数目&#xff0c;获取资源 时&#xff0c;需要对信号量的值进行原子减一&#xff0c;该操作被称为 P 操作。 当信号量值为 0 时&#xff0c;代表没有资源可用&#xff0c;P 操作…...

使用IntelliJ IDEA本地启动调试Flink流计算工程的2个异常解决

记录&#xff1a;471 场景&#xff1a;使用IntelliJ IDEA本地启动调试Flink流计算时&#xff0c;报错一&#xff1a;加载DataStream报错java.lang.ClassNotFoundException。报错二&#xff1a;No ExecutorFactory found to execute the application。 版本&#xff1a;JDK 1.…...

对象及日期对象

对象 1.什么是对象 类是对象的抽象,对象是类的实例 程序算法数据结构 万物皆对象,对象是一个具体的事物,看到见摸得着,对象是一组无序相关属性和方法的集合(无序,所以对象没有length属性),所有事物都是对象,列如字符串,数值,数组,函数等. 属性:事物的特征,在对象中用属性表…...

鼠标滚轮编码器解析

文章目录 前言一、鼠标滚轮编码器逻辑&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 鼠标滚轮编码器为三脚接入&#xff0c;一个COM脚C&#xff08;一般是接地&#xff09;&#xff0c;两个脉冲波形输入脚A、B&#xff0c;转动滚轮编码器会在两个脉冲输入脚上产生脉冲…...

【PTA】攀拓(PAT)- 程序设计(甲级)2023年春季考试

个人学习记录&#xff0c;代码难免不尽人意。 今天又斥资买了今年春季的真题一试&#xff0c;呃&#xff0c;感觉尽力了&#xff0c;89分&#xff0c;在当年排名23&#xff0c;感觉还不错&#xff0c;没有出现读不懂的题目和没有思路的情况&#xff0c;扣的11分分别是第二题两个…...

Spring Cloud Gateway 实现原理

Spring Cloud Gateway是Spring Cloud生态系统中的一个组件&#xff0c;用于构建基于Spring Boot的微服务架构中的网关服务。它的主要目的是提供一种灵活的方式来路由、过滤和转换HTTP请求&#xff0c;从而允许您构建强大、高性能的微服务应用程序。 以下是Spring Cloud Gatewa…...

嘉泰实业:真实低门槛,安全有保障

在互联网金融大行其道的当下&#xff0c;无论用户是多么的青睐、喜爱这种便捷的理财方式&#xff0c;也一定得把资金安全放在心上。要投就投那些实力背景雄厚&#xff0c;诚信经营的平台&#xff0c;可以选择投资用户基数庞大的理财老品牌&#xff0c;也可以选择发展势头迅猛的…...

spring boot 2.7 -> 3.0升级指南

spring boot提供一个版本迁移指南 2.7 -> 3.0...

MQTT 连接优化指南

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

算法和数据结构学习中的一些小的工具函数

算法和数据结构学习中的一些小的工具函数 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;算法和数据结构学习中的一些小的工具函数 CSDN&#xff1a;算法和数据结构学习中的一些小的工具函数 提取一个数二进制最右侧的 1 比如二进制为&#xff1a;0100 0…...

解决2K/4K高分屏下Vmware等虚拟机下Kail Linux界面显示问题

问题现象 在我们日常使用VirtualBox、Vmware workstation、Hyper-V等虚拟机安装使用Kali系统&#xff0c;在2K/4K高分辨率电脑下Kali系统界面显示太小&#xff0c;包括各种软件及命令终端字体均无法很直观的看出&#xff0c;影响我们的正常测试及使用。 常规处理思路 很多人…...

【校招VIP】java语言考点之双亲委派模型

考点介绍&#xff1a; 双亲委派是校招面试中的高频考点之一。双亲委派机制定义: 当一个类加载器收到了类加载的请求的时候&#xff0c;他不会直接去加载指定的类&#xff0c;而是把这个请求委托给自己的父加载器去加载&#xff0c;只有父加载器无法加载这个类的时候&#xff0…...

2023年阿里云新用户云服务器价格表

阿里云&#xff0c;作为国内领先的云计算服务提供商&#xff0c;一直致力于为全球用户提供安全、稳定、高效的云计算服务。对于新用户来说&#xff0c;阿里云服务器是一个非常不错的选择。那么&#xff0c;阿里云新用户云服务器的价格是怎样的呢&#xff1f;本文将为大家详细介…...

信号相关名词概念汇总-采样周期、泄露、窗函数等

信号相关名词概念汇总-采样周期、泄露、窗函数等 以下为信号相关名词概念的汇总 1 名词解释 采样周期/间隔&#xff1a;采样频率的倒数&#xff0c;两次相邻采样之间的时间间隔采样时间&#xff1a;采样的总时长&#xff0c;即采样点数N和采样周期的乘积采样频率&#xff1a; …...

数字化新零售营销模式如何落地?数字化新零售营销功能推荐

​通过科技手段&#xff0c;针对对线下零售店面的客户进行消费行为、频次等的分析&#xff0c;并进一步整合线上线下资源&#xff0c;实现实体零售的效率充分化&#xff0c;便是目前很火的新零售营销模式&#xff0c;能够将实体门店与数字化技术进行有机结合&#xff0c;通过为…...

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、注释快捷键&#xff1a; Ctrl/ 可以注释掉光标所在的这一行&#xff0c;或者是选中的区域。 对于注释掉的这一行或者这一区域&#xff0c;按下ctrl/则会去掉注释。 2、多行注释 在写多行注释时&#xff0c;英文状态下写三个"&#xff0c;会自动变成六个"&…...

高精度(加减乘除)

高精度算法出现的原因 当参与运算的数的范围大大的超出了标准数据类型&#xff0c;如int&#xff08;-2147483648 ~ 2147483647&#xff09;或者long long的范围&#xff0c;就需要使用高精度算法来进行数的运算。高精度运算的特点是代码长度比较长&#xff0c;本质是对数学运算…...

java企业数据管理系统

项目介绍 此项目为企业数据管理系统的后端部分&#xff0c;前端部分请参考vue-admin&#xff0c;项目实现了菜单管理、用户管理、角色管理和权限管理四个基础模块&#xff0c;前端菜单管理结合动态路由可自由添加菜单。结合Shiro权限管理实现了菜单和按钮的权限控制。 ❝ 前端…...

【云原生进阶之PaaS中间件】第二章Zookeeper-3.1分布式架构介绍

1 分布式架构详解 1.1 分布式发展历程 1.1.1 单点集中式 特点&#xff1a;App、DB、FileServer都部署在一台机器上。并且访问请求量较少 1.1.2 应用服务和数据服务拆分 特点&#xff1a;App、DB、FileServer分别部署在独立服务器上。并且访问请求量较少 1.1.3 使用缓存改善…...

Aspose.Words避坑指南:Java实现Word转PDF时如何去除水印(2023最新版)

Aspose.Words商业应用实战&#xff1a;Java版Word转PDF无水印解决方案深度解析 在企业级文档处理系统中&#xff0c;Word到PDF的转换需求几乎无处不在——合同归档、报告生成、电子发票导出等场景都依赖这一基础功能。作为Java开发者&#xff0c;当我们选择Aspose.Words这一业界…...

别再只会发文本了!SpringBoot整合钉钉机器人,这5种高级消息模板让你的通知更专业

SpringBoot与钉钉机器人&#xff1a;五种高级消息模板实战指南 如果你还在用单调的文本消息推送系统通知&#xff0c;那么你的团队协作工具可能只发挥了50%的潜力。钉钉机器人提供的富文本消息类型&#xff0c;能够将枯燥的系统通知转化为直观、交互式的信息卡片&#xff0c;显…...

英伟达黄仁勋力荐!2026年AI Agent元年,掌握这5大关键技术,成为行业风口!

0****1 什么是AI Agent&#xff1f; 随着人工智能技术加速演进&#xff0c;AI Agent&#xff08;人工智能代理&#xff0c;常称智能体&#xff09;正悄然渗透到企业运营与日常生活的各个角落&#xff0c;从大家熟悉的虚拟助手&#xff08;如Siri、小爱同学、豆包&#xff09;&a…...

XUnity.AutoTranslator:Unity游戏翻译解决方案的创新方法 | 玩家与开发者实战指南

XUnity.AutoTranslator&#xff1a;Unity游戏翻译解决方案的创新方法 | 玩家与开发者实战指南 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 你是否曾因语言障碍错失优秀的外语游戏&#xff1f;是否在尝…...

EVA-01保姆级教程:Qwen2.5-VL-7B多模态大模型在EVA-01中的本地化安全部署

EVA-01保姆级教程&#xff1a;Qwen2.5-VL-7B多模态大模型在EVA-01中的本地化安全部署 1. 引言&#xff1a;欢迎来到NERV指挥中心 想象一下&#xff0c;你面前有一个能看懂图片、理解图表、甚至能和你讨论图片里发生了什么的智能助手。现在&#xff0c;我们把这个助手装进了一…...

QEMU监视器隐藏玩法:用TCP端口转发实现远程调试(2024最新版)

QEMU监视器隐藏玩法&#xff1a;用TCP端口转发实现远程调试&#xff08;2024最新版&#xff09; 在边缘计算和物联网设备调试中&#xff0c;经常需要跨越物理距离管理虚拟机。传统方式要求开发者必须物理接触设备或依赖图形界面&#xff0c;这在分布式场景中显得笨拙且低效。实…...

如何用Python零依赖快速获取百度搜索结果?python-baidusearch深度解析

如何用Python零依赖快速获取百度搜索结果&#xff1f;python-baidusearch深度解析 【免费下载链接】python-baidusearch 自己手写的百度搜索接口的封装&#xff0c;pip安装&#xff0c;支持命令行执行。Baidu Search unofficial API for Python with no external dependencies …...

破解B站评论区识人困境!B站成分检测器让用户画像识别效率飙升8倍

破解B站评论区识人困境&#xff01;B站成分检测器让用户画像识别效率飙升8倍 【免费下载链接】bilibili-comment-checker B站评论区自动标注成分&#xff0c;支持动态和关注识别以及手动输入 UID 识别 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-comment-checke…...

douyin-downloader:让每个人都能轻松获取无水印视频的技术利器

douyin-downloader&#xff1a;让每个人都能轻松获取无水印视频的技术利器 【免费下载链接】douyin-downloader 项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader 一、问题破局&#xff1a;揭开抖音内容获取的神秘面纱 1.1 内容获取的三大拦路虎 …...

宁波小程序公司提供专业的小程序开发服务

在宁波小程序公司的服务中&#xff0c;我们致力于为客户提供清晰的内容结构和流畅的表达。我们通过深入的需求分析&#xff0c;确保每个项目都能符合客户特定的期望和市场需求。设计阶段注重市场调研&#xff0c;力求在视觉和功能上都能满足用户的使用习惯和偏好。开发过程中&a…...