【Linux】进程间通讯之共享内存

共享内存

一、共享内存的概念
进程的地址空间都是独立的,受保护的。
假设我们现在有两个进程:A进程和B进程

//A
int a = 10;
printf("%d\n",a);
//B
int a = 10;
printf("%d\n",a);

这两个进程中的a是没有关系的。那么共享内存就是内存上开辟的一块区域,能使得ptra和ptrb都指向这个空间,这样ptra可以在这个空间写东西,ptrb去查看的时候会看到A所写的东西。如下图所示:
在这里插入图片描述

在这里插入图片描述

共享内存必须有一个内核对象去指向它,不然它无法保存所写的东西。
二、相关函数
1、int shmget():用于创建或者获取共享内存

  • int shmget((key_t)key, int size, int flag);

shmget()成功返回共享内存的ID,失败返回-1
key:不同的进程使用相同的key值可以获取到同一个共享内存
size:创建共享内存时,指定要申请的共享内存空间大小
flag:IPC_CREAT IPC_EXCL

2、void* shmat():将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上

  • void * shmat(int shmid, void * addr, int shmflag);

shmat():成功返回共享内存的首地址,失败返回NULL
addr:一般给NULL,由系统自动选择映射的虚拟地址空间
shmflag:一般给0,可以给SHM_RDONLY为只读模式,其他的为读写

3、int shmdt():断开当前进程的shmaddr指向的共享内存映射,并没有删除这个共享内存

  • int shmdt( void * shmaddr)

shmdt():成功返回0,失败返回-1

4、int shmctl():控制共享内存

  • int shmctl(int shmid, int cmd, struct shmid_ds * buf)

shmctl():成功返回0,失败返回-1
cmd:IPC_RMID(删除共享内存空间,并不会立即删除共享空间)

注意:不能使用shmat方法与共享存储段建立映射关系
三、系统上模拟实现
我们在系统上创建两个进程A和B
示例:进程a从键盘循环获取数据并拷贝到共享内存中,进程b从共享内存中读取并打印数据,要求进程a输入一次,进程b输出一次。进程a不输入,进程b也不输出。

//shmA.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/shm.h>
#include "sem.h"
int main()
{
	int shmid = shmget((key_t)1234,128,IPC_CREAT | 0664);
	assert(shmid != -1);
	char *ptr = (char *)shmat(shmid,NULL,0);
	assert(ptr != (char*)-1);
    //0:sem1 1:sem2
	int init_val[2] = {0, 1};
	int semid = CreateSem(1234, init_val,2);
	assert(semid != -1);
	while(1)
	{
		SemP(semid,1); //B--->A 此处的1为下标
		printf("input: ");
		fgets(ptr, 127, stdin);
		SemV(semid,0); //A---->B
		if(strncmp(ptr,"end",3) == 0)
		{
			break;
		}
		shmdt(ptr);
		shmctl(shmid,IPC_RMID,NULL);
	}
}
//shmB.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/shm.h>
#include "sem.h"
#include <time.h>
int main()
{
	srand((unsigned int)(time(NULL)*time(NULL)));
	int shmid = shmget((key_t)1234,128,IPC_CREAT | 0664);
	assert(shmid != -1);
	char *ptr = (char *)shmat(shmid,NULL,0);
	assert(ptr != (char*)-1);
	int init_val[2] = {0, 1};
	int semid = CreateSem(1234, init_val,2);
	assert(semid != -1);
	while(1)
	{	
		SemP(semid,0); //A---->B
		if(strncmp(ptr,"end",3)==0)
		{
			break;
		}
		printf("B process: %s",ptr);
		int n = rand() % 3+1;
		sleep(n);
		printf("B Deal Over\n");
		memset(ptr, 0, 128);
		SemV(semid,1); //B--->A
	}
		shmdt(ptr);
		shmctl(shmid,IPC_RMID,NULL);
		DeleteSem(semid);
}

1、这有一个过程是这样,一个是A对B的信号量,一个是B对A的信号量。初始时,A对B中B是阻塞的,A是正常执行的。这块就需要两个信号量一个是sem1一个是sem2。系统对sem1和sem2进行P操作,如下图所示:
在这里插入图片描述

2、如果先执行B我们发现B是阻塞的。
在这里插入图片描述

3、如果执行A我们发现A中输入什么B中就输出什么,但是A不能接着输入,因为得等B处理完成之后才可以继续输入。
在这里插入图片描述
4、查看和删除共享内存
(1)命令删除

ipcs -s:查看信号量
ipcs -m:查看共享内存

在这里插入图片描述
执行命令:

ipcrm -s 98306

这样就可以删除这个信号量
(2)代码删除
我们可以在刚才的代码中调用:
DeleteSem(semid);
这样就不用手动删除,只要在A中输入end结束后,就可以删除所有东西。
四、共享内存的特点

  • 它是最快的IPC,因为少了两次拷贝。
  • 共享内存属于临界资源,所有AB两个进程去使用这个临界资源的时候需要进行同步控制
  • B进程必须在A进程获取数据之后才能打印,A进程必须在B进程将上一次数据处理后才能再次获取

五、进程间通信的总结
1、类别

  • 有名管道:通过文件描述符的方式做进程间通信,有阻塞非阻塞机制在其中,效率低。 、
  • 无名管道:父子进程间的通讯
  • 消息队列:进程可以根据消息类型获取不同的消息---->有个中间件MQ
  • 信号量:做同步控制的,对临界资源临界区的访问或两个进程的同步,协作关系做同步控制
  • 共享内存:最快的IPC

2、进程间通讯的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
  • 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知 父进程)。
  • 资源共享:多个进程之间共享同样的资源。为了作到这一点,需要 内核提供锁和同步机制。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
  • 进程通过与内核及其它进程之间的互相通信来协调它们的行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值