共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
共享内存=共享内存块+对应的共享内存的内核数据结构!
说明:
shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以 shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式: shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
命令 | 说明 |
---|---|
IPC_STAT | 把shmid_ds结构中的数据设置为共享内存的当前关联值 |
IPC_SET | 在进程有足够的权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值 |
IPC_RMID | 删除共享内存段 |
ipcs -m
ipcrm -m shmid
shmid就是上图的22
int n = shmctl(shmid, IPC_RMID, nullptr);
#pragma once
#include
#include
enum e
{
Debug,
Notice,
Warning,
Error
};
const std::string msg[]{
"Debug",
"Notice",
"Warning",
"Error"};
std::ostream &log(std::string message, int level)
{
std::cout << " | " << (unsigned)time(nullptr) << " | " << msg[level] << " | " << message;
return std::cout;
}
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "log.hpp"
using namespace std;
#define PATH_NAME "/home/Ding"
#define PROJ_ID 0X66
#define SHM_SIZE 4096 //共享内存的大小(512字节的整数倍)
#define FIFO_NAME "./fifo"
//命名管道---控制不让通信的时候它疯狂的读取数据;解决时序性问题
class Init
{
public:
Init()
{
umask(0);
int n = mkfifo(FIFO_NAME, 0666);
assert(n == 0);
(void)n;
log("创建管道文件成功", Notice) << "\n";
}
~Init()
{
unlink(FIFO_NAME);
log("删除管道文件成功", Notice) << "\n";
}
};
#define READ O_RDONLY
#define WRITE O_WRONLY
int OpenFIFO(string pathname, int flags)
{
int fd = open(pathname.c_str(), flags);
assert(fd >= 0);
(void)fd;
return fd;
}
void Wait(int fd)
{
log("等待中...", Notice) << "\n";
uint32_t temp = 0;
ssize_t s = read(fd, &temp, sizeof(uint32_t));
assert(s == sizeof(uint32_t));
(void)s;
}
void Signl(int fd)
{
uint32_t temp = 1;
ssize_t s = write(fd, &temp, sizeof(uint32_t));
assert(s == sizeof(uint32_t));
(void)s;
log("唤醒中...", Notice) << "\n";
}
void CloseFIfo(int fd)
{
close(fd);
}
PATH_NAME 路径只有你有权限进入的目录都可以
key_t k = ftok(PATH_NAME, PROJ_ID);
assert(k != -1);
log("创建公共key值", Debug) << "server key: " << k << endl;
// 2. 创建一个全新的共享内存---通信的发起者
int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
if (shmid == -1)
{
perror("shmget");
exit(1);
}
log("创建共享内存", Debug) << "shmid: " << shmid << endl;
// 3. 将指定的共享内存,挂接到自己的地址空间
char *shmaddr = (char *)shmat(shmid, nullptr, 0);
log("挂接共享内存成功", Debug) << endl;
sleep(10);
// 这里就是通信的逻辑了
int fd = OpenFIFO(FIFO_NAME, READ);
for (;;)
{
Wait(fd);
printf("%s\n", shmaddr);
if (strcmp(shmaddr, "quit") == 0)
break;
// sleep(1);
}
// 4. 将指定的共享内存,从自己的地址空间中去关联
int n = shmdt(shmaddr);
assert(n != -1);
(void)n;
log("去挂接共享内存成功", Debug) << "shmdt: " << n << endl;
// 5. 删除共享内存, IPC_RMID即便是当前进程与shm挂接,仍删除。
n = shmctl(shmid, IPC_RMID, nullptr);
assert(n != -1);
(void)n;
log("删除共享内存成功", Debug) << "shmctl: " << n << endl;
#include "comm.hpp"
//在加载的时候,会自动构建全局变量,就要调用该类的构造函数 -- 创建管道文件
// 程序退出的时候,全局变量会被析构,自动调用析构函数,会自动删除管道文件
Init init;
string TransToHex(key_t k)
{
char buffer[32];
snprintf(buffer, sizeof buffer, "0x%x", k);
return buffer;
}
int main()
{
// 1.创建公共的key值
key_t k = ftok(PATH_NAME, PROJ_ID);
assert(k != -1);
log("创建公共key值", Debug) << "server key: " << TransToHex(k) << endl;
// 2. 创建一个全新的共享内存---通信的发起者
int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
if (shmid == -1)
{
perror("shmget");
exit(1);
}
log("创建共享内存", Debug) << "shmid: " << shmid << endl;
// sleep(10);
// 3. 将指定的共享内存,挂接到自己的地址空间
char *shmaddr = (char *)shmat(shmid, nullptr, 0);
log("挂接共享内存成功", Debug) << endl;
// sleep(10);
// 这里就是通信的逻辑了
int fd = OpenFIFO(FIFO_NAME, READ);
for (;;)
{
Wait(fd);
printf("%s\n", shmaddr);
if (strcmp(shmaddr, "quit") == 0)
break;
// sleep(1);
}
// 4. 将指定的共享内存,从自己的地址空间中去关联
int n = shmdt(shmaddr);
assert(n != -1);
(void)n;
log("去挂接共享内存成功", Debug) << "shmdt: " << n << endl;
// sleep(10);
// 5. 删除共享内存, IPC_RMID即便是当前进程与shm挂接,仍删除。
n = shmctl(shmid, IPC_RMID, nullptr);
assert(n != -1);
(void)n;
log("删除共享内存成功", Debug) << "shmctl: " << n << endl;
CloseFIfo(fd);
return 0;
}
#include "comm.hpp"
int main()
{
// 1.创建公共的key值
key_t k = ftok(PATH_NAME, PROJ_ID);
if (k < 0)
{
log("创建公共key值失败", Debug) << "server key: " << k << endl;
exit(1);
}
log("创建公共key值", Debug) << "clicent key" << k << endl;
// 2. 获取共享内存
int shmid = shmget(k, SHM_SIZE, 0);
if (shmid < 0)
{
log("获取共享内存失败", Debug) << "shmid: " << shmid << endl;
exit(2);
}
log("获取共享内存", Debug) << "shmid: " << shmid << endl;
// sleep(10);
// 3. 挂接共享内存
char *shmaddr = (char *)shmat(shmid, nullptr, 0);
if (shmaddr == nullptr)
{
log("挂接共享内存失败", Debug) << endl;
exit(3);
}
log("挂接共享内存成功", Debug) << endl;
// sleep(10);
//使用
int fd = OpenFIFO(FIFO_NAME, WRITE);
while (true)
{
ssize_t s = read(0, shmaddr, SHM_SIZE - 1);
if (s > 0)
{
shmaddr[s - 1] = 0;
Signl(fd);
if (strcmp(shmaddr, "quit") == 0)
break;
}
}
// 4. 去关联
int n = shmdt(shmaddr);
assert(n != -1);
(void)n;
log("去挂接共享内存成功", Debug) << "shmdt: " << n << endl;
//不需要chmctl删除
return 0;
}
将共享内存当成一个大字符串,通信的时候可以像malloc一样使用 char buffer[SHM_SIZE];