之前提到了进程间通信的管道,消息队列,信号量,然后其中信号量是PV操作,操控的是一个共享资源。在我们提到的IPC模块中,消息队列针对的是数据单元的信息传送,管道不属于system V IPC的部分,所以按照一个操作系统的整体来说,他应该也有着一个关于字节流的消息传输,并且要比之前都要快,还要跟我们之前所说的信号量利用起来,所以就出现了共享内存的概念。

  什么是共享内存?

    共享内存是多个进程之间共享内存区域的一种进程间的通信方式,他是在多个进程之间对内存段进行映射的方式实现内存共享的,这是IPC最快捷的方式,因为共享内存的方式的通信没有中间过程,二管道,消息队列等方式则是需要将数据通过中间机制进行转换。

   共享内存方式直接将某段内存段进行映射,多个进程间的共享内存是统一快的物理空间,仅仅阿是地址不同而已,因此不需要进行复制,可以直接使用此段空间。

 下面我们就来了解Linux下的共享内存相关的函数:

    1.创建共享内存函数:      

#include<sys/ipc.h>
#include<sys/shm.h>
int shmget(key_t key,size_t size,int shmflg);

  第一个参数是关键字的值,然后,这个值将与内核中现有德1其他共享内存段的关键字值相比较,比较之后,依赖第3个参数,跟前面消息队列的一样。

     2.获取共享内存地址函数shmat()

       函数shmat()用来获取共享内存的地址,获取共享内存成功后,可以像使用通用内存一样对其进行读写操作,

#include<sys/ipc.h>
#include<sys/shm.h>
void* shmat(int shmid,const void *shmaddr,int shmflg);
int shmdt(const void *shmaddr);

    如果shmaddr参数值等于0,则内科将试着查找一个未映射的区域,用户可以指定一个地址,但通常改地址只用于访问所拥有的硬件,或者解决与其他应用程序的冲突。SHM_RND标志可以与标志参数进行OR操作,结果在置为标志参数。SHM_RDONLY标志参数进行OR操作,结果在置为标志参数,这样映射的共享内存端智能标记为只读方式。

    shmdt()用于删除一段共享内存,这个函数之中,成功完成了断开连接操作以后,相关的shmid_ds结构的shm_nattch成员的值将减去1。如果这个值减到0.则内核将真正删除这个共享内存。

4.共享内存控制函数shmctl()

#include<sys/ipc.h>
#include<sys/shm.h>
int shmctl(int shmid,int cmd,struct shmid_ds*buf);

    第二个参数为:IPC_SET,获取内存短的shmid_ds结构。并把它存储在buf参数所指定的地址中。设置内存段shmid_ds结构的ipc_pern成员的值,此命令是从buf参数中获得该值。

        IPC_RMID:标记某内存段,以被删除。并不是真正的把内存段从内存中删除,相反,它只是标记上该内存段,以备将来删除,最后一个断开,删除操作才会发生。


下面看看实际代码:

//shm.h
#pragma once
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define _PATH_ "."
#define _PROJECT_ 0x777
#define _SHM_SIZE_ 4*1024

int get_shm();
void* at_shn();
int delete_shm();
int rm_shm();

//shm.c
#include "shm.h"

int get_shm()
{
	key_t key = ftok(_PATH_,_PROJECT_);
	
	if(key < 0)
	{
		perror("ftok");
		return -1;
	}

	int flag =IPC_EXCL| IPC_CREAT |0666;
	int shm_id = shmget(key,_SHM_SIZE_,flag);
	if(shm_id < 0)
	{
		printf("shmid error");
	}
	else
	{
		printf("shmid success");
	}
	return shm_id;
}     

void* at_shm(int shm_id)
{
	return shmat(shm_id,NULL,0);
}

int delete_shm(char *addr)
{
	return shmdt(addr);
}

int rm_shmctl(int shm_id)
{
	return shmctl(shm_id,IPC_RMID,NULL);
}

//shm_test.c
#include "shm.h"

int main()
{
	int shm_id = get_shm();
	if(shm_id < 0)
	{
		printf("HHH");
	}
	pid_t id = fork();
	if(id <0)
	{
		printf("fork error\n");
		return 1;
	}
	else if(id == 0)
	{
		char *buf = (char *)at_shm(shm_id);

		printf("child");
		int i = 0;
		while(i < 4095)
		{
			buf[i] = 'A';
			++i;
			buf[i] = '\0';
			sleep(1);
		}
		buf[4095] = '\0';
		delete_shm(buf);
	}
	else
	{
		int i = 0;
		char *buf  = (char *)at_shm(shm_id);
		printf("father");
		while(i<4095)
		{
			printf("%s\n",buf);
			++i;
			sleep(1);
		}
		delete_shm(buf);
		waitpid(id,NULL,0);
		rm_shmctl(shm_id);
	}
	return 0;

}

运行结果:

wKioL1cXPpSRu_kZAAAhoVKHEKY607.png


总结:

 其实共享内存是在IPC进程间通信最快的一种方式,因为他是直接针对物理地址进行的操作,而且对于他的资源控制,我们需要配合信号量进行操作,来防止死锁的产生。