C++ Linux共享内存

来自泡泡学习笔记
跳到导航 跳到搜索

概念与原理

共享内存是一种高效的进程间通信机制,允许多个进程直接访问同一块物理内存。共享内存是所有IPC机制中最快的,因为数据不需要在内核和用户空间之间进行复制。


核心点

  • 创建共享内存:使用shmget系统调用创建。
  • 附加共享内存:使用shmat系统调用将共享内存附加到进程地址空间。
  • 分离共享内存:使用shmdt系统调用分离共享内存。
  • 删除共享内存:使用shmctl系统调用删除共享内存。


实现实例

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <cstring>
#include <unistd.h>

#define SHM_SIZE 1024  // 共享内存大小

int main() {
    key_t key = ftok("shmfile", 65);  // 生成唯一键
    int shmid = shmget(key, SHM_SIZE, 0666|IPC_CREAT);  // 创建共享内存段
    char *str = (char*) shmat(shmid, nullptr, 0);  // 附加到共享内存
 
    pid_t cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
 
    if (cpid == 0) {    // 子进程
        sleep(1);  // 确保父进程先写入
        std::cout << "子进程从共享内存读取数据:\n";
        std::cout << str << std::endl;
        shmdt(str);  // 分离共享内存
        _exit(EXIT_SUCCESS);
    } else {            // 父进程
        std::cout << "父进程写入共享内存:\n";
        const char* msg = "来自父进程的消息";
        strncpy(str, msg, SHM_SIZE);
        wait(NULL);  // 等待子进程
        shmdt(str);  // 分离共享内存
        shmctl(shmid, IPC_RMID, nullptr);  // 删除共享内存
        exit(EXIT_SUCCESS);
    }
}


解析:

ftok("shmfile", 65):生成一个唯一键。

shmget(key, SHM_SIZE, 0666 | IPC_CREAT):创建共享内存段。

shmat(shmid, NULL, 0):将共享内存段附加到进程地址空间。


子进程:等待父进程将数据写入共享内存,从共享内存读取数据并显示。

父进程:将数据写入共享内存,等待子进程结束,分离并删除共享内存段。


运行结果:

父进程写入共享内存:
子进程从共享内存读取数据:
来自父进程的消息


实现实例2

共享内存写入端

#include <iostream>
#include<unistd.h>//unix stand lib
#include<sys/types.h>
#include<sys/fcntl.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<dirent.h>//file dir
#include <sys/wait.h>//wait func
#include <stdlib.h>//ststem
#include <signal.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/shm.h>
using namespace std;
typedef struct
{
	int age;
	char name[10];
}STU;

#define SHM_ZIZE_MAX 4096


int main(int argc, char *argv[])
{
	
	int shm_id;
	shm_id = shmget((key_t)6677, SHM_ZIZE_MAX, IPC_CREAT | 0666);//666可读可写
	if (shm_id == -1)
	{
		perror("shm create error");
		return -1;
	}

	STU stu_info = { 19, "rabbit" };

	void *shm_addr = NULL;
	//shmat的第二个参数shm_addr:把共享内存连接到当前进程去的时候准备放置它的那个地址,为NULL为让系统自动选择
	//第三个参数是一组按位OR(或)在一起的标志,0表示可读可写
	shm_addr = shmat(shm_id, NULL, 0);
	//要对内存清空一下
	memset(shm_addr, 0, SHM_ZIZE_MAX);
	//写进内存:memcpy因为映射就看做是自己的内存

	memcpy(shm_addr, &stu_info, sizeof(STU));//地址,内容,大小
	cout << "memcpy(shm_addr, &stu_info, sizeof(STU))" << endl;
	while(1){sleep(1);}
	shmdt(shm_addr);//脱钩函数
	
	return 0;
}


共享内存读出端

#include <iostream>
#include<unistd.h>//unix stand lib
#include<sys/types.h>
#include<sys/fcntl.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<dirent.h>//file dir
#include <sys/wait.h>//wait func
#include <stdlib.h>//ststem
#include <signal.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/shm.h>
typedef struct
{
	int age;
	char name[10];
}STU;

#define SHM_ZIZE_MAX 4096
int main(int argc, char *argv[])
{
	int shm_id;
	shm_id = shmget((key_t)6677, SHM_ZIZE_MAX, IPC_CREAT | 0666);//666可读可写
	if (shm_id == -1)
	{
		perror("shm create error");
		return -1;
	}
	
	STU stu_info;
	STU *recv_stu_info;
	bzero(&stu_info, sizeof(STU));
	void *shm_addr = NULL;
	//shmat的第二个参数shm_addr:把共享内存连接到当前进程去的时候准备放置它的那个地址,为NULL为让系统自动选择
	//第三个参数是一组按位OR(或)在一起的标志,SHM_RDONLY表示只读
	shm_addr = shmat(shm_id, NULL, SHM_RDONLY);

	//法一:从内存中拿出来:memcpy因为映射就看做是自己的内存
	//memcpy(&stu_info, shm_addr,sizeof(STU));//地址,内容,大小
	//cout << "stu_info.name" << stu_info.name << endl;
	
	//法二:也可以直接把共享内存指针强制转换成STU*,这时要shmat的参数3改为0读写
	//这里也说明如果有进程把数据写入共享内存,如果没有其他进程去修改或清除
	//那么数据永远都在里面,消息队列和共享内存里面的数据生命周期和进程不同(进程消失了数据可以还在)
	recv_stu_info = (STU*)shm_addr;
	cout << "stu_info.name" << recv_stu_info->name << endl;

	
	
	
	shmdt(shm_addr);//脱钩函数
	
	return 0;
}