IPC机制(三)--共享内存和信号灯

news2024/11/28 1:50:25

目录

前言

一、什么是共享内存

二、创建一个共享内存 

 三、信号灯/信号集

1、临界资源

2、同步互斥机制

  1、互斥机制:

2、同步:

3、信号灯的机制

4、信号灯的函数

四、信号灯控制进程对共享内存的访问 

进程1:

进程2:

   总结 


前言

        本节将介绍,进程间通信的最后一个内容,也就是共享内存和信号灯机制

一、什么是共享内存

        共享内存就是创建在内核内存中,将同一个内存空间地址分别映射到不同的进程中,进程只需要操作对应用户空间的虚拟地址空间们就可以操作共享内存。 

        创建的内存空间有起始地址,和结束地址

        共享内存通信方式:当进程1想要和进程2进行通信时,由于内核空间是公用的,我们在内核空间中创建一个共享内存,然后得到共享内存的地址,在进程1中将数据传输到共享内存当中,进程2在通过地址找到共享内存的地址,把进程1传入的数据拿到进程2中,就实现了进程间通信的过程。但是在这个过程中会有一个问题,我们对在进程操作共享内存空间时没有安排访问共享内存的顺序,会导致达不到我们想要传输一次,读取一次的目的,这时候就引入了信号灯中的同步互斥机制,本文也会重点讲述这个机制

共享内存的特点: 

        1、共享内存时最高效的进程间通信机制

        2、共享内存独立于进程的,共享内存在进程结束后不会消失。除非手动删除或计算机重启

二、创建一个共享内存 

        shmget():创建共享内存

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);
功能:创建key或在内核中找到key值的共享内存

参数:
参数1:
    key_t key:创建/打开 key 对应的共享内存
参数2:
    size_t size:指定共享内存的大小,以字节为单位
参数3:
    int shmflg:选项
        IPC_CREAT:如果key值对应的共享内存不存在,则创建共享内存
        IPC_CREAT | 0664 :创建的同时指定权限,如果共享内存已经存在,则会忽略这个权限

返回值:
成功,返回共享内存id
失败,返回-1,设置errno

        shmmat():将共享内存的地址映射到用户空间,也就是进程能读取的地址空间中

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:将共享内存映射到用户空间中

参数:
参数1:
    int shmid:共享内存id
参数2:
    const void *shmaddr:指定要将共享内存映射到用户空间哪个地址
        比如:要映射到0x00800000地址,参数 (void *)0x00800000
        填 NULL , 让系统自动映射
参数3:
    int shmflg:
        0:默认方式操作,对共享内存可读可写
        SHM_RDONLY:只读
        
返回值:指针
成功,返回共享内存映射到程序空间的地址位置
失败,返回(void *)-1 , 设置errno

        shmdt():断开解除共享内存地址的映射,也就是停止映射地址,这时进程找不到这个地址

#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void *shmaddr);
功能:断开解除共享内存的映射

参数:
   const void *shmaddr:将哪一块程序空间映射的首地址与共享内存断开映射
   
返回值:
成功,返回0
失败,返回-1,设置错误码

        shmctl():控制共享内存,可以获取内存空间的属性,还能将内存空间删除

#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:控制共享内存,常用于删除共享内存

参数:
参数1:
    int shmid:要控制的共享内存id
参数2:
    int cmd:控制命令
        IPC_STAT:获取共享内存属性,获取的属性值存储到 第三个参数 指针变量 对应的地址空间   
        IPC_SET:设置共享内存属性,把 第三个参数 指针变量对应空间的属性值 设置到共享内存中 
        IPC_RMID:删除共享内存, 第三个参数为 NULL  

 创建一个共享内存:

//创建共享内存
#include<sys/types.h>
#include<sys/shm.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main(int argc, const char *argv[])
{
	//1、创建共享内存shmget
	int shmid = shmget(20000,1024,IPC_CREAT|0664);
	//2、映射共享内存地址shmat
	void * shmaddr=shmat(shmid,NULL,0);
	if(shmaddr==(void*)-1)
	{
		perror("shmat failed:");
		return -1;
	}
	//3,对共享内存操作
	char *p=shmaddr;
	while(1)
	{
		//情况内存空间
		bzero(p,1024);
		//从终端获取数据
		scanf("%s",p);
		sleep(1);
	}
	//4、断开解除共享内存地址shmdt
	int ret=shmdt(shmaddr);//映射的地址于共享内存空间地址断开
	if( ret< 0)
	{
		perror("shmdat failed:");
		return -1;
	}
	//5、删除共享内存
	shmctl(shmid,IPC_RMID,NULL);
	

	return 0;
}

 三、信号灯/信号集

        在介绍信号灯之前,我们要知道为什么我们需要引入信号灯这个概念

        原因:就像上面我们说的在使用共享内存时,如果多个进程或线程同时访问共享内存,可能会出现竞态条件,导致程序行为不一致或错误。信号灯可以用来避免这些竞态条件,通过控制对共享内存的访问顺序来确保数据的一致性。

        信号灯还可以用于控制对共享资源的访问,比如在生产者-消费者模型中,信号灯可以帮助管理生产者和消费者之间的协调,确保缓冲区的正确使用。

        总之,信号灯作为一种同步机制,能有效地解决在并发环境中访问共享内存时可能出现的各种问题,确保程序的稳定性和正确性。

1、临界资源

        临界资源定义:

当多个任务并发执行,访问同一个资源,将这个资源称之为临界资源

在进行通信时,需要引入同步互斥机制,避免产生竟争状态。保证任意时刻,都只有一个进程/线程处理临界资源(共享资源)

        临界区:访问临界资源的代码,称之为临界区

例如:上面我们说到的共享内存就是一个临界资源,还有管道文件也是共享资源,如果多个进程或线程试图同时读写同一个管道,就需要进行适当的同步,以防止数据冲突或丢失。所以就引入了下面的同步互斥机制

2、同步互斥机制

  1、互斥机制:

 定义:保证临界区的完整性,临界区(对临界资源的访问)具有唯一性(同时只有一个进程/线程 进行操作,操作完之后另外的进程才能操作)。只能有一个进程访问共享资源(A访问,其他进程就不能访问;B访问,其他进程也不能访问),但是无法保证访问者的访问顺序

2、同步:

定义:在互斥的基础上,能够限制访问者的访问顺序

         了解了上面的基础概念之后,我们来正式学习信号灯,在学习的过程中,可以将其看作日常生活中所看到交通信号灯;

3、信号灯的机制

        定义:对要访问的共享资源的进程/线程,执行申请信号量的操作

信号灯三种情况:

        1、当信号量/信号灯的值 > 0 ,则表示申请信号量成功(资源可以访问),进入临界区执行操作临界资源的代码(使用共享资源),同时把信号量的值 -1(进行申请操作)

        2、当信号量的值 == 0,则申请信号量失败,当前进程/线程进入休眠,阻塞等待信号量的值 > 0

        对于使用完共享资源的进程/线程,执行释放信号量的操作

        3、将信号量的值+1,通知休眠阻塞的信号量的进程/线程(进行释放操作)

信号量的两种操作:

        PV操作:信号量实现多进程、多线程同步互斥的方式

        P:申请信号量,== 0 阻塞休眠,否则信号量-1操作

        V:释放信号量,信号量 + 1操作

         那么下面我们将来具体代码实现信号灯,首先要知道内核中,常用于创建和控制信号灯的函数;

4、信号灯的函数

        下面我们说的信号灯和信号灯集是两个概念,信号灯集是信号灯的集合,多个信号灯才能组成一个信号灯集

        semget():创建信号灯集

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
功能:通过key值,创建或打开一个信号灯集(多个信号量的集合)

参数:
参数1:
    key_t key:key值,也要创建打开的信号量集 key
参数2:
    int nsems:信号 灯集中有几个信号灯
参数3:
    int semflg:选项
        IPC_CREAT:如果key值对应的信号灯集不存在,则创建信号灯
        IPC_CREAT | 0664 :创建的同时指定权限,如果信号灯集已经存在,则会忽略这个权限       

返回值:
成功,返回信号灯集的id
失败,返回-1,设置errno                                           

         semop():控制某个信号灯的PV操作

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);
功能:操作信号灯集中信号灯值PV操作,P操作(申请信号量,-1,不够减阻塞),V操作(释放信号量,+1,唤醒)

参数:
参数1:
    int semid:指定要操作的信号灯集的id
参数2:
    struct sembuf *sops:P、V操作的选项,结构体指针,操作信息结构体的地址
        struct sembuf
        {
            unsigned short sem_num;指定要操作的信号灯集中哪个信号灯,编号从0开始
            short          sem_op;
                P操作:负整数,如 -2,信号灯的值 -2,不够阻塞等待,一般 : -1
                V操作:正整数,如 2,信号灯的值 +2
            short          sem_flg;
                0:阻塞方式运行,上述操作就会阻塞运行
                IPC_NOWAIT:非阻塞方式,p操作不够时,不会阻塞等待,通过返回值出错表示申请失败,不能操作共享资源                                     
        }                                   
参数3:
    size_t nsops:要控制的信号灯集中信号灯的个数    

返回值:
成功,返回0
失败,返回-1,设置errno        

        semctl():控制信号灯集

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, ...);
功能:控制信号灯集

参数:
参数1:
    int semid:指定要控制那个信号灯集
参数2:
    int semnum:指定要控制信号灯集中的哪个信号灯
参数3:
    int cmd:控制命令
        IPC_RMID:删除信号灯集,第二个参数无意义,最后一个参数不用填
        IPC_STAT:获取信号灯集的属性,存储到 第四个参数 指针地址对应空间,第四个参数为:struct semid_ds * buf
        IPC_SET:设置信号灯集的属性,把第四个参数 指针地址对应空间数据,设置到信号灯集中
        GETVAL:获取信号灯集中,指定的信号灯的值,最后一个参数不用填,返回值 就是获取到的 信号量的值
        SETVAL:设置信号灯集中,指定的信号灯的值,第四个参数,就是要设置的值 类型为:int
    
参数4:
    根据参数3设置
    
返回值:
成功,返回0(有少量cmd,返回值为cmd对应的结果)
失败,返回-1 

        仔细看上面各个函数的参数,返回值,实现的功能,下面我们具体实现信号灯控制进程对于共享内存的访问

四、信号灯控制进程对共享内存的访问 

        首先,创建进程1,和进程2 ;进程当中必须使用同一个共享内存,否则失败。

进程1:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/sem.h>


int main()
{
	//共享内存通信
	//1、创建打开共享内存
	
	int shmid = shmget(20000,1024,IPC_CREAT|0664);
	//if
	//2、映射共享内存
	
	void * shmaddr = shmat(shmid,NULL,0);

	if(shmaddr == (void *)-1)
	{
		perror("shmat failed:");
		return -1;
	}


	//信号量的通信
	//P、V操作
	//1、创建打开信号灯集
	int semid = semget(20001,1,IPC_CREAT | 0664);
	//if(semid < 0)
	
	//获取信号灯集中,信号灯的值
	if( semctl(semid,0,GETVAL) != 0 )
	{
		//设置信号灯的值为0
		semctl(semid,0,SETVAL,0);
	}


	//3、通过共享内存地址 实现 对 共享内存进行操作
//	int * p = shmaddr;
//	*p = 10;//共享内存前4个字节

//	*(p+1) = 20;
	
	char * p = shmaddr;
	while(1)
	{	
		//从终端输入字符串到 共享内存
		scanf("%s",p);
	
		//2、操作信号量
		//释放信号量 V 操作
		struct sembuf semops;
		semops.sem_num = 0;
		semops.sem_op = 1;// +1 V操作
		semops.sem_flg = 0;//阻塞方式执行
		if(semop(semid,&semops,1) == 0)//判断 < 0
		{
			printf("v operation success\n");
		}
		//bzero(p,1024);
		//把 hello world 写入 共享内存
		//strcpy(p,"hello world");//拷贝字符串到共享内存
		//sleep(1);

	}

	//4、在操作通信结束后,解除共享内存映射
	
	if( shmdt(shmaddr) < 0 )
	{
		perror("shmdt failed:");
		return -1;
	}

	
	//5、删除共享内存
	shmctl(shmid,IPC_RMID,NULL);
	
	//3、删除信号灯集
	semctl(shmid,0,IPC_RMID);

	return 0;
}

进程2:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/sem.h>


int main()
{
	//共享内存通信
	//1、创建打开共享内存
	
	int shmid = shmget(20000,1024,IPC_CREAT|0664);
	//if
	//2、映射共享内存
	
	void * shmaddr = shmat(shmid,NULL,0);

	if(shmaddr == (void *)-1)
	{
		perror("shmat failed:");
		return -1;
	}

        //信号量的通信
        //P、V操作
        //1、创建打开信号灯集
        int semid = semget(20001,1,IPC_CREAT | 0664);
        //if(semid < 0)


	//3、通过共享内存地址 实现 对 共享内存进行操作
	
	char * p = shmaddr;
	char buf[100];
	while(1)
	{
		//2、操作信号量
                //申请信号量 P 操作
                struct sembuf semops;
                semops.sem_num = 0;
                semops.sem_op = -1;// -1 P操作
                semops.sem_flg = 0;//阻塞方式执行
		//阻塞申请信号量 -1
                if(semop(semid,&semops,1) == 0)//判断 < 0
                {
                        printf("p operation success\n");
                }
		
		printf("%s\n",p);//打印共享内存数据

		bzero(p,1024);
	}	

	//4、在操作通信结束后,解除共享内存映射
	
	if( shmdt(shmaddr) < 0 )
	{
		perror("shmdt failed:");
		return -1;
	}

	
	//5、删除共享内存
	shmctl(shmid,IPC_RMID,NULL);

	//3、删除信号灯集
        semctl(shmid,0,IPC_RMID);
	

	return 0;
}

         注意:代码当中的函数使用不清楚的看上面的函数介绍,我们通过进程1来发送信号灯进行P操作,也就是将原本的信号灯值变为1,进程二调用的后,获取到信号灯值大于0,执行操作,将信号灯值变为0,参数中的-1是传递参数到函数中,将信号灯值-1变为0,而不是直接赋值;然后执行下面的打印输出,如果信号灯值为0,也就是在进程2中,信号灯值1-1=0之后,进程2进入休眠,等待进程1将信号灯值进行+1操作,否则继续休眠;

   总结 

        本文主要简述了信号灯如何对进程访问临界资源进行控制, 这里主要讲述的是对进程访问共享内存进行控制,读者也可以下去使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2099849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通过G2++利率模型来模拟短期和长期利率的随机路径

G2利率模型是一种用于描述和预测利率变化的数学模型&#xff0c;它属于随机利率模型的一种。随机利率模型主要用于研究一段时间内利率的随机波动。 G2模型的核心在于它能够通过两个因子来捕捉短期和长期利率的动态变化。这两个因子通常与宏观经济变量相关联&#xff0c;能够更…

网络安全宗旨和目标

网络安全涉及网络和相关数据及信息的保护与保障。它已从物理技术发展到防病毒和反网络钓鱼平台等软件方法。 在本章中&#xff0c;我们将详细讨论网络安全的主要目标和原则&#xff0c;并提供与之相关的具体示例。所以&#xff0c;让我们从网络安全的目标开始。 网络安全的目的…

二手Agilent N6705B+N6781A电源模块N6705C

二手Agilent N6705BN6781A电源模块N6705C 产品概述 AgilentN6705B是可集成多达4个具有数字万用表、示波器、任意波形发生器和数据记录器特性的xj电源&#xff0c;可显著提高产生和测量输送到被测件中的直流电压和电流的效率。可使您无需开发和调试程序来控制大量仪器&#xf…

BaseCTF高校联合新生赛Week1复现(Web)

目录 A Dark Room Aura 酱的礼物 HTTP 是什么呀 md5绕过欸 喵喵喵•&#xfecc;• A Dark Room 打开环境 先查看源码&#xff0c;拉到最后看到flag Aura 酱的礼物 第一层看见file_get_contents()函数&#xff0c;想到文件包含&#xff0c;可以用伪协议&#xff0c;一般是…

Spring框架的案例程序

Spring框架的案例程序通常涉及构建一个简单的应用程序&#xff0c;以展示Spring的核心功能&#xff0c;如依赖注入、面向切面编程、数据访问等。以下是一个基本的Spring案例程序&#xff0c;它包括一个Spring Boot应用程序&#xff0c;用于展示如何使用Spring构建RESTful服务。…

Elasticsearch 基于Windows环境安装

1、 Java 下载安装 1.1 下载地址 elasticsearch 8.x 版本需要jdk17及以上版本支持 JDK下载地址 2、下载和安装 ES 2.1 下载地址 Past Release 2.2 文件目录 3、启动服务 3.1 启动方式 Windows&#xff1a;双击 bin 目录下的 elasticsearch.bat 文件Mac&#xff1a;双击…

深度学习系列74:语音中的mel谱

1 mel谱介绍 一个人说一句话&#xff0c;其 waveform 可以很不一样&#xff0c;但是 spectrogram 基本上会相似&#xff0c;甚至有人可以通过 spectrogram 来判断说话的内容。语谱图的横坐标是时间&#xff0c;纵坐标是频率&#xff0c;坐标点值为语音数据能量。由于是采用二维…

计算机网络(一) —— 网络基础入门

目录 一&#xff0c;关于网络 二&#xff0c;协议 2.1 协议是什么&#xff0c;有什么用&#xff1f; 2.2 协议标准谁定的&#xff1f; 2.3 协议分层 2.4 OSI 七层模型 2.5 TCP/IP 四层模型 三&#xff0c;网络传输基本流程 3.1 局域网中两台主机通信* 3.2 报文的封装与…

智能化浪潮赋能工业制造与报废拆解,基于高精度YOLOv8全系列参数【n/s/m/l/x】模型开发构建工业生产场景下车辆不同部位智能化分割检测识别分系统

随着科技的飞速发展&#xff0c;数字化与智能化已成为推动社会进步的强大引擎。在工业生产制造与汽车报废处理这一传统领域中&#xff0c;这一变革尤为显著。曾经&#xff0c;流水线作业与人工拆解是这些行业的主要生产方式&#xff0c;它们不仅效率低下&#xff0c;且高度依赖…

如何从 AWS CodeCommit 迁移到极狐GitLab?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;可以私有化部署&#xff0c;对中文的支持非常友好&#xff0c;是专为中国程序员和企业推出的企业级一体化 DevOps 平台&#xff0c;一键就能安装成功。安装详情可以查看官网指南。 本文将分享如何从 AWS CodeCommit 服务无缝迁…

【无标题】XSS安全防护:responseBody (输入流可重复读) 配置

接上文:配置XSS过滤器 XXS 安全防护:拦截器+注解实现校验-CSDN博客XSS(跨站脚本)攻击是一种网络安全威胁,允许攻击者注入恶意脚本到看似安全的网站。当用户浏览这些被注入恶意代码的网页时,恶意脚本会在用户的浏览器环境中执行,这可能导致多种安全问题,如窃取敏感数据、…

链表算法题(上)

在之前单链表和双链表两个专题中我们学习了链表相关的概念和性质&#xff0c;同时了解了单链表和双链表各自的特征&#xff0c;那么接下来在本篇中我们就将使用这些链表的知识来解决链表相关的算法题&#xff0c;在本篇中这些算法题能强化我们的算法思想&#xff0c;会对我们之…

HTML5好看的花店商城源码2

文章目录 1.设计来源1.1 主界面1.2 界面效果11.3 界面效果21.4 界面效果31.5 界面效果41.6 界面效果51.7 界面效果61.8 界面效果71.9 界面效果8 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#…

postgresql数据库创建表分区和分区分区查询失效问题

postgresql数据库创建表分区和分区失效场景 一、前言二、分区表创建1、范围分区&#xff08;Range Partitioning&#xff09;2、列表分区&#xff08;List Partitioning&#xff09;3、hash分区&#xff08;hash Partitioning&#xff09; 三、表分区查询失效问题 一、前言 在…

AcWing算法基础课-785快速排序-Java题解

大家好&#xff0c;我是何未来&#xff0c;本篇文章给大家讲解《AcWing算法基础课》785 题——快速排序。这篇文章介绍了使用快速排序算法对整数数列进行排序的方法&#xff0c;包括选择基准元素、分区操作和递归排序子数组。通过详细的步骤和示例&#xff0c;解释了快速排序的…

Axure打造科技感数据可视化大屏原型

在数字化浪潮的推动下&#xff0c;数据已成为企业决策不可或缺的核心驱动力。面对海量且复杂的数据集&#xff0c;如何高效解读并转化为洞见&#xff0c;是企业面临的重大挑战。数据可视化&#xff0c;尤其是科技感十足的大屏展示&#xff0c;不仅为企业提供了直观的数据洞察&a…

9,sql 约束

创建表时添加非空约束 create table sys3(name int,age int not null) 建表后修改 alter table sys3 modify name int null 删除 创建表时添加默认值约束 关键字 default 默认值的意思 后面跟着默认的值create table sys_4(列名1 数据类型 default 默认值,列名2 数据类型 no…

网络安全售前入门09安全服务——安全加固服务

目录 1.服务概述 2.流程及工具 2.1服务流程 2.2服务工具 3.服务内容 ​​​​​​​4.服务方式 ​​​​​​​5.风险规避措施 ​​​​​​​6.服务输出 1.服务概述 安全加固服务是参照风险评估、等保测评、安全检查等工作的结果,基于科学的安全思维方式、长期的安全…

Apache CloudStack Official Document 翻译节选(十三)

快速部署一朵 Apache CloudStack 云 &#xff08;二&#xff09; 部署一朵pache CloudStack 云 安装Apache CloudStack的云内管理服务组件 本部分我们将安装Apache CloudStack的云内管理服务组件及周边工具。 关于数据库的安装与配置&#xff1a; 我们会安装和配置MySQL并配…

【机器学习入门】一文读懂非线性支持向量机SVM

前面已经分别介绍了基于硬间隔最大化的线性可分支持向量机、基于软间隔最大化的线性支持向量机&#xff0c;这次来总结下使用核函数来解决非线性可分问题的非线性支持向量机。 【机器学习入门】一文读懂线性可分支持向量机【机器学习入门】一文读懂线性支持向量机SVM 一 非线…