文章目录
- Linux共享内存:完整代码展示与剖析 🚀
- 共享内存:原理、接口与应用实践 🔍
- 引言 🌟
- 一、共享内存核心原理 ⚙️
- 1.1 共享内存的特点 📌
- 1.2 生命周期管理 ⏳
- 二、关键系统接口解析 🔧
- 2.1 生成唯一标识Key 🔑
- 2.2 创建/获取共享内存 🛠️
- 2.3 内存挂接与去关联 🔗
- 2.4 控制操作 🎮
- 完整代码展示 💻
- 1. 公用头文件 `common.hpp` 📜
- 2. 客户端代码 `client.cc` 👨💻
- 3. 服务端代码 `server.cc` 👩💻
- 编译与运行 🚀
- 编译命令 🔧
- 运行步骤 ▶️
- 代码工作原理 🧠

Linux共享内存:完整代码展示与剖析 🚀
共享内存(Shared Memory)是Linux系统中一种高效的进程间通信(IPC)机制,允许多个进程共享同一块物理内存区域,从而实现快速的数据交换。本文将提供完整的C++代码示例,包括头文件、客户端和服务端实现,详细展示共享内存的创建、使用、同步及管理过程。✨
共享内存:原理、接口与应用实践 🔍
引言 🌟
共享内存是Linux系统进程间通信(IPC)中最快的一种方式,其核心思想是让多个进程通过映射同一块物理内存来实现数据共享。
一、共享内存核心原理 ⚙️
1.1 共享内存的特点 📌
- 零拷贝:数据直接映射到进程地址空间,无需内核中转 🚫📦
- 高效性:适合传输大块数据(如视频流、大型矩阵) 💨
- 无同步机制:需结合其他IPC(如信号量、管道)实现同步 🔄
1.2 生命周期管理 ⏳
- 随内核持续:即使进程退出,共享内存仍存在 💾
- 主动释放:需调用
shmctl
或使用ipcrm
命令删除 🗑️
二、关键系统接口解析 🔧
2.1 生成唯一标识Key 🔑
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
- 参数:
pathname
:已存在的文件路径(如/home/user/config
📁)proj_id
:项目ID(0-255) 🔢
- 返回值:唯一Key,用于后续操作 🎯
2.2 创建/获取共享内存 🛠️
int shmget(key_t key, size_t size, int shmflg);
- 标志位:
IPC_CREAT
:不存在则创建 🆕IPC_EXCL
:配合IPC_CREAT
使用,存在则报错 ❌- 权限位(如
0666
🔒)
2.3 内存挂接与去关联 🔗
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
- 挂接策略:
shmaddr
设为NULL
由系统选择地址 🎯SHM_RDONLY
以只读方式挂接 📖
2.4 控制操作 🎮
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 控制命令:
IPC_STAT
:获取状态信息 📊IPC_RMID
:标记删除 🗑️
完整代码展示 💻
以下是实现共享内存通信的完整代码,分为三个文件:common.hpp
(公用头文件)、client.cc
(客户端)和server.cc
(服务端)。
1. 公用头文件 common.hpp
📜
#ifndef __COMMON_HPP__
#define __COMMON_HPP__#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>using namespace std;const string pathname = "/home"; // ftok使用的路径名 🏠
const int proj_id = 0x666; // ftok的项目ID 🆔
const string FIFO_FILE = "./myfifo"; // 命名管道文件路径 🚇
const mode_t MODE = 0664; // 命名管道权限 🔒enum Mistake
{FTOK_ERROR = 1,SHMGET_ERROR,SHMAT_ERROR,FIFO_CREATE_ERR,FIFO_DELETE_ERR
};// 生成唯一的key_t键值 🔑
key_t GetKey()
{key_t key = ftok(pathname.c_str(), proj_id);if (key < 0){perror("ftok error");exit(FTOK_ERROR);}return key;
}// 共享内存辅助函数 🛠️
int GetShareMemHelper(int flag)
{key_t key = GetKey();int shmid = shmget(key, 4096, flag); // 创建或获取4096字节的共享内存 💾if (shmid < 0){perror("shmget error");exit(SHMGET_ERROR);}return shmid;
}// 创建共享内存 🆕
int CreateShm()
{return GetShareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}// 获取共享内存 🔍
int GetShm()
{return GetShareMemHelper(IPC_CREAT);
}// 初始化类:创建和销毁命名管道 🚧
class Init
{
public:Init(){int n = mkfifo(FIFO_FILE.c_str(), MODE);if (n == -1 && errno != EEXIST) // 如果管道已存在则忽略错误 ⚠️{perror("mkfifo");exit(FIFO_CREATE_ERR);}}~Init(){int m = unlink(FIFO_FILE.c_str());if (m == -1){perror("unlink");exit(FIFO_DELETE_ERR);}}
};#endif
2. 客户端代码 client.cc
👨💻
#include "common.hpp"int main()
{// 获取共享内存 💾int shmid = GetShm();char *shmaddr = (char *)shmat(shmid, nullptr, 0);if (shmaddr == (char *)-1){perror("shmat error");exit(SHMAT_ERROR);}// 打开命名管道并发送信号 📤int fd = open(FIFO_FILE.c_str(), O_WRONLY);if (fd < 0){perror("open");exit(1);}cout << "Please Enter :" << endl;// 从标准输入读取数据并写入共享内存 ⌨️while (true){fgets(shmaddr, 4096, stdin);write(fd, "c", 1); // 通知服务端数据已写入 📨}// 分离共享内存 🔗shmdt(shmaddr);close(fd);return 0;
}
3. 服务端代码 server.cc
👩💻
#include "common.hpp"int main()
{// 创建命名管道 🚇Init init;// 创建共享内存 🆕int shmid = CreateShm();char *shmaddr = (char *)shmat(shmid, nullptr, 0);if (shmaddr == (char *)-1){perror("shmat error");exit(SHMAT_ERROR);}// 打开命名管道,等待客户端信号 📭int fd = open(FIFO_FILE.c_str(), O_RDONLY);if (fd < 0){perror("open");exit(1);}std::cout << "server start sucess" << endl;while (true){char c;ssize_t s = read(fd, &c, 1); // 读取信号 📨if (s <= 0)break; // 当s小于0说明写端关闭跳出循环关闭共享内存else if (s > 0){cout << "client say@ " << shmaddr; // 输出共享内存内容 💬 // 获取共享内存状态 📊struct shmid_ds shmds;shmctl(shmid, IPC_STAT, &shmds);cout << "shm size: " << shmds.shm_segsz << endl;cout << "shm nattch: " << shmds.shm_nattch << endl;printf("shm __key: 0x%x\n", shmds.shm_perm.__key);cout << "shm shm_perm.mode: " << shmds.shm_perm.mode << endl;cout << "---------------------" << endl;}}// 分离并删除共享内存 🗑️shmdt(shmaddr);shmctl(shmid, IPC_RMID, nullptr);close(fd);return 0;
}
编译与运行 🚀
编译命令 🔧
g++ -o client client.cc
g++ -o server server.cc
运行步骤 ▶️
-
在一个终端运行服务端:
./server
-
在另一个终端运行客户端:
./client
-
在客户端终端输入消息(例如
"Hello, shared memory!"
)并按回车 ↵
代码工作原理 🧠
-
共享内存创建与附加 🔗
- 服务端使用
shmget
创建共享内存,客户端通过相同的键值获取。 shmat
将共享内存映射到进程地址空间,返回指针供直接操作。
- 服务端使用
-
数据通信 📡
- 客户端将输入写入共享内存,服务端直接读取该内存区域的内容。
-
同步机制 ⚡
- 使用命名管道(FIFO)实现同步,客户端写入数据后通过管道发送信号,服务端接收信号后读取数据。
-
资源管理 🛠️
- 服务端在结束时使用
shmctl(IPC_RMID)
删除共享内存,Init
类的析构函数清理命名管道。
- 服务端在结束时使用