当前位置: 首页 > 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 使用缓存改善…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...

leetcode73-矩阵置零

leetcode 73 思路 记录 0 元素的位置&#xff1a;遍历整个矩阵&#xff0c;找出所有值为 0 的元素&#xff0c;并将它们的坐标记录在数组zeroPosition中置零操作&#xff1a;遍历记录的所有 0 元素位置&#xff0c;将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...

CSS 工具对比:UnoCSS vs Tailwind CSS,谁是你的菜?

在现代前端开发中&#xff0c;Utility-First (功能优先) CSS 框架已经成为主流。其中&#xff0c;Tailwind CSS 无疑是市场的领导者和标杆。然而&#xff0c;一个名为 UnoCSS 的新星正以其惊人的性能和极致的灵活性迅速崛起。 这篇文章将深入探讨这两款工具的核心理念、技术差…...