linux共享内存通信

Aki 发布于 2023-02-17 256 次阅读


介绍、

在 Linux 系统中,进程间通信方式有很多种,其中共享内存是一种高效的进程间通信方式。使用共享内存可以避免进程之间频繁地进行数据复制和数据传输,从而提高了进程间通信的效率。以下是使用共享内存进行进程间通信的一般步骤:

  1. 创建共享内存段:在进程中使用 shmget 函数创建一个新的共享内存段,或者连接到一个现有的共享内存段。
  2. 连接到共享内存段:在进程中使用 shmat 函数将共享内存段连接到进程的地址空间中。
  3. 写入/读取数据:通过共享内存段的地址可以在不同的进程之间进行数据读写。
  4. 分离共享内存段:使用 shmdt 函数将共享内存段从进程的地址空间中分离。
  5. 销毁共享内存段:最后,可以使用 shmctl 函数销毁共享内存段。

五个最重要的api函数、

(1)ftok函数:

key_t ftok(const char *pathname, int proj_id);

函数用于将路径名和一个整数标识符转换为一个 System V IPC 键值(key_t 类型)。ftok() 函数可以用于创建共享内存、消息队列、信号量等进程间通信方式的标识符。该函数并不会打开和修改路径名的文件,只是参考该文件和一个整数值生成唯一的标识符。成功返回大于0的标识符,失败返回-1。

(2)shmgt函数:

int shmget(key_t key, size_t size, int shmflg);

//key是ftok生成的标识符

//size是表明申请的字节大小

//shmflg有下面几种取值
//(1):IPC_CREAT | IPC_EXCL | 0666
//如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则
//报错,错误存在于errno中为EEXIST。0666标识对共享内存的访问权限,类似于-wr-wr-wr。

该函数作用是得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符。成功返回大于0的标识符,失败返回-1,错误原因存于errno中。

(3)shmat函数:

void *shmat(int shmid, const void *shmaddr, int shmflg);

//shmid是shmget返回的共享内存标识符

//指定共享内存出现在进程内存地址的什么位置,直接指定为nullptr让内核自己决定一个合适的地址位置

//shmflg是指定读写模式,设置为0是可读可写

函数作用是连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。失败返回-1,错误原因存于errno中。

(4)shmctl函数:

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


//shmid是shmget返回的共享内存标识符

//cmd是操作,有下面这些
//IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中
//IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内
//IPC_RMID:删除这片共享内存

//buf共享内存管理结构体。具体说明参见共享内存内核结构定义部分

作用是完成对共享内存的控制,成功返回0,失败返回-1,错误存在于errno中。

(5)shmdt函数:

int shmdt(const void *shmaddr);

//shmaddr:连接的共享内存的起始地址

与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存。失败返回-1,错误原因存于errno中,成功返回0。

#include<iostream>
#include<thread>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<mutex>
#include<sys/ipc.h>
#include<vector>
#include<sys/shm.h>
#include<errno.h>
#include<cstring>
#include<vector>
using std::mutex,std::cout,std::cin,std::endl,std::lock_guard,std::vector;


//共享内存结构体
struct sm_memory
{
	mutex sm_mutex;
};


//出现错误函数
void unix_error(const char* msg) noexcept
{
	cout << msg;
	exit(0);
}


//创建共享内存
static int shmid = 0;
static key_t key = 0;
sm_memory* create_shared_memory() noexcept
{

	key = ftok("./makefile",114514);
	if(key < 0)
	{
		unix_error("create key error\n");
	}

	shmid = shmget(key,sizeof(sm_memory),IPC_CREAT | IPC_EXCL | 0666);
	if(shmid < 0)
	{
                //这个是共享内存已经存在的情况,直接连接上就行
		if(errno == EEXIST)
		{
			shmid = shmget(key,sizeof(sm_memory),0666);
		}
		else
		{
			unix_error("create shared memory error\n");
		}
	}

	sm_memory* sm = static_cast<sm_memory*>(shmat(shmid,nullptr,0));
	if(reinterpret_cast<int64_t>(sm) == -1)
	{
		shmctl(shmid,IPC_RMID,nullptr);
		unix_error("map shared memory error\n");
	}

	memset(sm,0,sizeof(sm_memory));
	new(sm)sm_memory;

	return sm;
}



//删除共享内存
void delete_shared_memory(sm_memory* sm,int id) noexcept
{
	if(shmdt(sm) < 0)
	{
		cout << "disconnect shared memory error" << endl;
	}

	if(shmctl(id,IPC_RMID,nullptr) < 0)
	{
		cout << "delete shared memory error" << endl;
	}
}

int main()
{
        sm_memory* sm = create_shared_memory();
        delete_shared_memory(sm,shmid);
        return 0;
}