✨个人主页:熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】
目录
1、共享内存的概念
共享内存是指多个进程共享同一块物理内存区域的技术。在Linux操作系统中,通过系统调用和内核支持,不同的进程可以映射到同一块物理内存,并直接读写这块内存区域,避免了数据在进程间的拷贝和传递,提高了数据访问的效率。
1、OS创建共享内存空间。
2、通过页表建立物理内存与虚拟内存的映射。
如何理解上面的这个过程呢?
1、前面做的操作都是OS做的。
2、OS提供上面1,2的步骤的系统调用,供用户进程A,B来进行调用。
3、AB进程可以共享,那么CD,EF都可以共享内存 ==> 共享内存在系统中可以存在多份,供不同个数,不同对进程同时进行通信。
4、OS注定要对共享内存进行管理! ==> 先描述在组织 ==> 共享内存不是简单的一段内存空间,也要有描述并管理的数据结构和匹配的算法。
5、共享内存 = 内存空间(数据) + 共享内存的属性(结构体封装)
共享内存结构体中一定要有标识内存唯一性的字段,但是又不能让OS自己生成,那么应该怎么生成呢?
其实我们只要设计一套规则,让用户自己设置唯一的标识符编号即可,但是通常使用ftok()函数获取共享内存唯一标识符。
2、创建共享内存
会用到的头文件及全局变量
bash
#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/root/linux-learning/Shm"; # 此路径自己设置
const int gproj_id = 0x66;
const int gShmSize = 4096;
2.1、函数介绍
创建共享内存系统调用函数
bash
shmget - allocates a System V shared memory segment
# 分配 System V 共享内存段
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
参数:
key:用户生成的共享内存唯一标识符
size:共享内存大小
shmflg:一个标志位集合,用于控制共享内存段的访问权限和创建行为
IPC_CREAT:如果共享内存段不存在,则创建一个新的共享内存段;如果已存在,获取该共享内存并返回
IPC_EXCL:单独使用没有意义,只有跟IPC_CREAT组合才有意义
IPC_CREAT | IPC_EXCL:如果共享内存段不存在,则创建一个新的共享内存段;如果已存在,则返回错误
RETURN VALUE
On success, a valid shared memory identifier is returned. On error,
-1 is returned, and er‐rno is set to indicate the error.
返回值:
成功时,shmget函数返回一个非负整数,表示共享内存段的标识符(ID)
失败时,返回-1,并设置相应的errno以指示错误原因
ftok()
bash
ftok函数用于生成一个唯一的键值(key),这个键值随后可以用作shmget等函数的参数来创建或访问共享内存。
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
参数:
pathname:一个已存在的文件路径名。这个函数通过结合这个路径名和proj_id来生成一个唯一的键值。
proj_id:一个项目标识符,通常是一个小的非负整数。
RETURN VALUE
On success, the generated key_t value is returned.
On failure -1 is returned, with errno indicating the error as for the stat(2) system call.
返回值:
成功时,返回一个唯一的键值(key_t类型)。
失败时,返回-1,并设置errno以指示错误原因。
2.2、代码测试
获取唯一标识符
cpp
key_t GetComKey(const std::string& pathname,int proj_id)
{
key_t k = ftok(pathname.c_str(),proj_id);
if(k < 0)
{
perror("ftok");
}
return k;
}
唯一标识符转为16进制数
cpp
std::string ToHex(key_t key)
{
char buffer[128];
snprintf(buffer,sizeof(buffer),"0x%x",key);
return buffer;
}
创建共享内存
cpp
int GetShm(key_t key,size_t size)
{
// 不存在则创建,存在则返回错误
int shmid = shmget(key,size,IPC_CREAT | IPC_EXCL);
if(shmid < 0)
{
perror("shmget");
}
return shmid;
}
测试key值
cpp
int main()
{
key_t key = GetComKey(gpathname,gproj_id);
std::cout << "key: " << ToHex(key) << std::endl;
return 0;
}
cpp
int main()
{
key_t key = GetComKey(gpathname,gproj_id);
std::cout << "key: " << ToHex(key) << std::endl;
return 0;
}
两个文件创建的key值都相同!!!
测试共享内存创建
IPC_CREAT | IPC_EXCL:不存在则创建,存在则返回错误
**IPC_CREAT :**不存在则创建,存在则获取并返回
补充命令:
ipcs -m # 查看共享内存信息
ipcrm -m shmid # 删除共享内存
key vs shmid
**key :**属于用户形成,内核使用的一个字段,用户不能使用key来管理shm。内核来区分shm的唯一性(类比struct file*)。
**shmid :**内核给用户返回的一个标识符,用来进行用户级对共享内存进行管理的id值(类比fd)。
- 在技术角度可以实现用key管理共享内存,但是为了能够更好的解耦,选择各自管理各自的。
3、封装成类
构造函数:创建共享内存。
析构函数:删除共享内存。
3.1、基本框架
cpp
class Shm
{
private:
key_t GetComKey(); // 获取key值
int GetShmHelper(key_t key, size_t size, int flag); // 创建内存空间
std::string RoleToString(int who); // 用户名转为字符串
void *AttachShm(); // 连接进程
void DetachShm(void *shmaddr); // 脱离进程
public:
Shm(const std::string &pathname, int proj_id, int who)
: _pathname(pathname), _proj_id(proj_id), _who(who),, _addrshm(nullptr)
{}
std::string ToHex(key_t key); // 将10进制数转化为16进制数
bool GetShmUseCreate(); // Creater创建共享内存
bool GetShmForUser(); // User创建共享内存
void Zero(); // 清空共享内存数据
void *Addr(); // 获取地址
void DebugShm(); // 访问共享内存属性
~Shm()
{}
private:
key_t _key; // key值
int _shmid; // 共享内存编号
std::string _pathname; // 路径
int _proj_id;
int _who; // 用户名
};
删除共享内存
cpp
shmctl函数用于控制共享内存段的某些操作,包括删除共享内存段。
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
shmid:共享内存段的标识符,由shmget函数返回。
cmd:要执行的操作命令。对于删除共享内存段,应使用IPC_RMID命令。
buf:指向shmid_ds结构的指针,用于传递或接收共享内存段的属性。在删除共享内存段时,这个参数通常设置为NULL,因为不需要获取或设置共享内存的属性。
返回值:
成功时,返回0。
失败时,返回-1,并设置errno以指示错误原因。
3.2、析构函数
创建者才需要删除共享内存!!!
cpp
~Shm()
{
sleep(5); // 休眠5秒测试删除效果
if (_who == gCreater)
{
int res = shmctl(_shmid, IPC_RMID, nullptr);
if (res < 0)
{
perror("shmctl");
}
}
std::cout << "shm remove done..." << std::endl;
}
3.3、私有成员函数
将获取key值和创建共享内存函数封装为私有
cpp
private:
key_t GetComKey()
{
key_t k = ftok(gpathname.c_str(), gproj_id);
if (k < 0)
{
perror("ftok");
}
return k;
}
int GetShmHelper(key_t key, size_t size, int flag)
{
int shmid = shmget(key, size, flag);
if (shmid < 0)
{
perror("shmget");
}
return shmid;
}
3.4、创建共享内存函数
根据用户创建共享内存
cpp
// Creater创建共享内存
bool GetShmUseCreate()
{
if (_who == gCreater)
{
// 不存在则创建,存在则返回错误
_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
if (_shmid >= 0)
return true;
std::cout << "shm create done..." << std::endl;
}
return false;
}
// USer创建共享内存
bool GetShmForUser()
{
if (_who == gUser)
{
// 不存在则创建,存在则获取并返回
_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
if (_shmid >= 0)
return true;
std::cout << "shm get done..." << std::endl;
}
return false;
}
3.5、构造函数
cpp
Shm(const std::string &pathname, int proj_id, int who)
: _pathname(pathname), _proj_id(proj_id), _who(who)
{
_key = GetComKey();
if (_who == gCreater)
GetShmUseCreate();
else if (_who == gUser)
GetShmForUser();
std::cout << "shmid: " << _shmid << std::endl;
std::cout << "_key: " << ToHex(_key) << std::endl;
}
3.6、测试
cpp
while :; do ipcs -m;sleep 1;done # 每隔一秒查看一次共享内存属性信息
cpp
int main()
{
Shm shm(gpathname,gproj_id,gCreater);
return 0;
}
运行结果
cpp
int main()
{
Shm shm(gpathname,gproj_id,gUser);
return 0;
}
运行结果
1、共享内存不随进程结束而自动释放,即共享内存声明周期随内核==> 一直存在,直到系统重启
2、共享内存手动释放(指令或者其他系统调用)
运行结果
先执行服务端程序,再执行客户端程序,会看到一个现象,共享内存挂接进程数从没有变成1,再从1变成2,然后从2变成1,且状态变为dest(被删除但还有用户在使用),最后没有挂接数。
3.7、进行通信
shmat()
cpp
shmat : 将共享内存区对象映射到调用进程的地址空间,使得进程能够像访问本地空间一样访问共享内存
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid:共享内存标识符,由shmget函数返回。该标识符用于唯一标识一块共享内存。
shmaddr:指定共享内存出现在进程内存地址的什么位置。如果此参数为NULL,则内核会自动选择一个合适的地址位置进行映射。
如果shmaddr不为NULL,且没有指定SHM_RND标志,则尝试将共享内存映射到shmaddr指定的地址。
如果指定了SHM_RND标志,则映射地址会向下调整为SHMLBA(低边界地址的倍数,总是2的乘方)的整数倍。
shmflg:权限标志位。如果shmflg为0,则默认为读写模式;如果指定SHM_RDONLY,则以只读模式连接共享内存。
返回值
成功时,shmat函数返回映射好的共享内存地址。
失败时,返回-1,并设置errno以指示错误原因。
shmdt()
cpp
shmdt : 将当前进程地址空间中附加的共享内存段分离
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmaddr:这是一个指向共享内存段起始地址的指针。这个地址通常是之前通过调用shmat函数获得的,
表示当前进程地址空间中共享内存段的起始位置。
返回值
成功:函数执行成功时,返回0。
失败:如果函数执行失败,则返回-1,并设置相应的errno以指示错误原因。
此处需要获取地址,因此可以增加一个存储地址的成员变量。
cpp
class Shm
{
private:
key_t _key; // key值
int _shmid; // 共享内存编号
std::string _pathname; // 路径
int _proj_id;
int _who; // 用户名
void* _addrshm; // 内存地址
};
分离地址
因为挂接地址需要先判断是否需要分离地址,因此先实现分离地址。
// 分离地址
void DetachShm(void* shmaddr)
{
// 为空无需分离地址
if(shmaddr == nullptr)
return;
shmdt(shmaddr);
std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;
}
挂接地址
void* AttachShm()
{
// 当前地址不为空先分离地址
if(_addrshm != nullptr)
{
DetachShm(_addrshm);
}
// 为空直接挂接地址
void* addrshm = shmat(_shmid,nullptr,0);
if(addrshm == nullptr)
{
perror("AttachShm");
}
std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;
return addrshm;
}
构造函数
构造函数通过初始化列表将地址初始化为空,然后在函数内部挂接地址。
Shm(const std::string &pathname, int proj_id, int who)
: _pathname(pathname), _proj_id(proj_id), _who(who),_addrshm(nullptr)
{
_key = GetComKey();
if (_who == gCreater)
GetShmUseCreate();
else if (_who == gUser)
GetShmForUser();
std::cout << "shmid: " << _shmid << std::endl;
std::cout << "_key: " << ToHex(_key) << std::endl;
_addrshm = AttachShm(); // 挂接地址
}
清理共享内存数据
// 清理数据
void Zero()
{
if (_addrshm)
{
memset(_addrshm, 0, gShmSize);
}
}
测试
客户端写数据,服务端读数据。
int main()
{
// 1.获取共享内存
Shm shm(gpathname,gproj_id,gUser);
// 2.清理内存数据
shm.Zero();
// 3.写数据
char* shmaddr = (char*)shm.Addr(); // 获取地址
char ch = 'A';
while(ch <= 'Z')
{
shmaddr[ch - 'A'] = ch;
sleep(2); // 两秒写一次数据
ch++;
}
return 0;
}
int main()
{
// 1.创建共享内存
Shm shm(gpathname,gproj_id,gCreater);
// 2.获取共享内存地址
char* shmaddr = (char*)shm.Addr();
// 3.读取数据
while(true)
{
std::cout << "shm memory content: " << shmaddr << std::endl;
}
return 0;
}
只启用服务端,会一直在读。
写端两秒写一条数据,读端一秒写一条数据,会发现出现重复数据。
- 可以看出共享内存的一个优点:我们在访问共享内存的时候,没有使用任何系统调用,因此共享内存是所有IPC速度最快的,因为共享内存大大减少了数据的拷贝次数。
- 通过上面测试可以看出共享内存一个缺点:共享内存不提供任何的保护机制 --- 数据不一致问题!!!
怎么解决这个缺点呢?
使用命名管道,因为命名管道是同步通信的!!!
int main()
{
// 1.创建共享内存
Shm shm(gpathname,gproj_id,gCreater);
// 2.获取共享内存地址
char* shmaddr = (char*)shm.Addr();
// 3.创建管道
NamePiped fifo(comm_path,Creater);
fifo.OpenForRead();
// 4.读取数据
while(true)
{
std::string temp;
fifo.ReadNamedPipe(&temp);
std::cout << "shm memory content: " << shmaddr << std::endl;
}
return 0;
}
int main()
{
// 1.获取共享内存
Shm shm(gpathname,gproj_id,gUser);
// 2.清理内存数据
shm.Zero();
// 3.创建管道
NamePiped fifo(comm_path,User);
fifo.OpenForWrite();
// 4.写数据
char* shmaddr = (char*)shm.Addr(); // 获取地址
char ch = 'A';
while(ch <= 'Z')
{
shmaddr[ch - 'A'] = ch;
std::string temp = "wakeup";
std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;
fifo.WriteNamedPipe(temp);
sleep(2); // 两秒写一次数据
ch++;
}
return 0;
}
使用命名管道之后,执行服务端但是没有执行客户端会阻塞(根据管道原理)。
执行服务端之后,再执行客户端,会正常进行通信,且不出现数据不一致问题!!!
注意:我们使用ctrl + c关闭服务端进程都需要手动删除共享内存,否则会出问题!!!
运行结果
3.8、查看共享内存属性
struct shmid_ds ds;
shmctl(_shmid, IPC_STAT, &ds);
IPC_STAT:
从关联的内核数据结构中复制信息与 shmid 一起进入 buf 指向的 shmid_ds 结构。
调用方必须对共享内存具有读取权限段。
测试函数
void DebugShm()
{
struct shmid_ds ds;
int n = shmctl(_shmid, IPC_STAT, &ds);
if (n < 0)
return;
std::cout << "ds.shm_perm.__key : " << ToHex(ds.shm_perm.__key) << std::endl;
std::cout << "ds.shm_nattch: " << ds.shm_nattch << std::endl;
}
主函数
int main()
{
// 1. 创建共享内存
Shm shm(gpathname, gproj_id, gCreater);
char *shmaddr = (char*)shm.Addr();
shm.DebugShm();
sleep(5);
return 0;
}
运行结果
4、完整代码
4.1、Makefile
.PHONY:all
all:client server
client:client.cc
g++ -o $@ $^ -std=c++11
server:server.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -rf client server
4.2、Shm.hpp
#ifndef __SHM__HPP
#define __SHM_HPP
#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>
const std::string gpathname = "/root/linux-learning/Shm";
const int gproj_id = 0x66;
const int gShmSize = 4096;
const int gCreater = 1;
const int gUser = 2;
class Shm
{
private:
key_t GetComKey()
{
key_t k = ftok(gpathname.c_str(), gproj_id);
if (k < 0)
{
perror("ftok");
}
return k;
}
int GetShmHelper(key_t key, size_t size, int flag)
{
int shmid = shmget(key, size, flag);
if (shmid < 0)
{
perror("shmget");
}
return shmid;
}
std::string RoleToString(int who)
{
if (who == gCreater)
return "gCreater";
else if (who == gUser)
return "gUser";
return "None";
}
// 挂接地址
void *AttachShm()
{
// 当前地址不为空先分离地址
if (_addrshm != nullptr)
{
DetachShm(_addrshm);
}
// 为空直接挂接地址
void *addrshm = shmat(_shmid, nullptr, 0);
if (addrshm == nullptr)
{
perror("AttachShm");
}
std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;
return addrshm;
}
// 分离地址
void DetachShm(void *shmaddr)
{
// 为空无需分离地址
if (shmaddr == nullptr)
return;
shmdt(shmaddr);
std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;
}
public:
Shm(const std::string &pathname, int proj_id, int who)
: _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
{
_key = GetComKey(); // 初始化_key值
if (_who == gCreater)
GetShmUseCreate();
else if (_who == gUser)
GetShmForUser();
std::cout << "shmid: " << _shmid << std::endl;
std::cout << "_key: " << ToHex(_key) << std::endl;
_addrshm = AttachShm(); // 挂接地址
}
std::string ToHex(key_t key)
{
char buffer[128];
snprintf(buffer, sizeof(buffer), "0x%x", key);
return buffer;
}
bool GetShmUseCreate()
{
if (_who == gCreater)
{
// 不存在则创建,存在则返回错误
_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
if (_shmid >= 0)
return true;
std::cout << "shm create done..." << std::endl;
}
return false;
}
bool GetShmForUser()
{
if (_who == gUser)
{
// 不存在则创建,存在则获取并返回
_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
// _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT);
if (_shmid >= 0)
return true;
std::cout << "shm get done..." << std::endl;
}
return false;
}
// 获取地址
void *Addr()
{
return _addrshm;
}
// 清理数据
void Zero()
{
if (_addrshm)
{
memset(_addrshm, 0, gShmSize);
}
}
void DebugShm()
{
struct shmid_ds ds;
int n = shmctl(_shmid, IPC_STAT, &ds);
if (n < 0)
return;
std::cout << "ds.shm_perm.__key : " << ToHex(ds.shm_perm.__key) << std::endl;
std::cout << "ds.shm_nattch: " << ds.shm_nattch << std::endl;
}
~Shm()
{
sleep(5);
if (_who == gCreater)
{
int res = shmctl(_shmid, IPC_RMID, nullptr);
if (res < 0)
{
perror("shmctl");
}
}
std::cout << "shm remove done..." << std::endl;
}
private:
key_t _key; // key值
int _shmid; // 共享内存编号
std::string _pathname; // 路径
int _proj_id;
int _who; // 用户名
void *_addrshm; // 内存地址
};
key_t GetComKey(const std::string &pathname, int proj_id)
{
key_t k = ftok(pathname.c_str(), proj_id);
if (k < 0)
{
perror("ftok");
}
return k;
}
int GetShm(key_t key, size_t size)
{
// 不存在则创建,存在则返回错误
// int shmid = shmget(key,size,IPC_CREAT | IPC_EXCL);
// 不存在则创建,存在则获取并返回
int shmid = shmget(key, size, IPC_CREAT);
if (shmid < 0)
{
perror("shmget");
}
return shmid;
}
std::string ToHex(key_t key)
{
char buffer[128];
snprintf(buffer, sizeof(buffer), "0x%x", key);
return buffer;
}
#endif
4.3、namedPipe.hpp
#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cerrno>
#include <cstdio>
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096
const std::string comm_path = "./myfifo";
class NamePiped
{
private:
bool OpenNamedPipe(int mode)
{
// 返回值是文件描述符,打开失败返回-1,并设置错误码
_fd = open(_fifo_path.c_str(), mode);
if (_fd < 0)
return false;
return true;
}
public:
// 构造函数创建管道
NamePiped(const std::string &path, int who)
: _fifo_path(path), _id(who), _fd(DefaultFd)
{
// 创建着才需要创建管道
if (_id == Creater)
{
int res = mkfifo(_fifo_path.c_str(), 0666);
// 返回值不等于0则创建失败
if (res != 0)
{
perror("mkfifo");
}
std::cout << "creater create named pipe" << std::endl;
}
}
// 以读方式打开文件
bool OpenForRead()
{
return OpenNamedPipe(Read);
}
// 以写方式打开文件
bool OpenForWrite()
{
return OpenNamedPipe(Write);
}
// 读文件
int ReadNamedPipe(std::string *out)
{
char buffer[BaseSize];
int n = read(_fd, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n] = 0;
*out = buffer;
}
return n;
}
// 写文件
int WriteNamedPipe(const std::string& in)
{
return write(_fd,in.c_str(),in.size());
}
// 析构函数删除管道
~NamePiped()
{
// 析构之前休眠5秒,看删除效果
sleep(5);
// 创建者才需要删除管道
if (_id == Creater)
{
int res = unlink(_fifo_path.c_str());
if (res != 0)
{
perror("umlink");
}
std::cout << "creater free named pipe" << std::endl;
}
// 创建了文件则关闭文件描述符
if(_fd != DefaultFd) close(_fd);
}
private:
const std::string _fifo_path; // 文件路径
int _id; // 用户名
int _fd; // 文件描述符
};
4.4、server.cc
#include "Shm.hpp"
#include "namedPipe.hpp"
// int main()
// {
// key_t key = GetComKey(gpathname,gproj_id);
// std::cout << "key: " << ToHex(key) << std::endl;
// int shmid = GetShm(key,gShmSize);
// std::cout << "shmid: " << shmid << std::endl;
// return 0;
// }
// int main()
// {
// // 1.创建共享内存
// Shm shm(gpathname,gproj_id,gCreater);
// // 2.获取共享内存地址
// char* shmaddr = (char*)shm.Addr();
// // 3.读取数据
// while(true)
// {
// sleep(1); // 每隔一秒读取一次
// std::cout << "shm memory content: " << shmaddr << std::endl;
// }
// return 0;
// }
int main()
{
// 1.创建共享内存
Shm shm(gpathname,gproj_id,gCreater);
// 2.获取共享内存地址
char* shmaddr = (char*)shm.Addr();
// 3.创建管道
NamePiped fifo(comm_path,Creater);
fifo.OpenForRead();
// 4.读取数据
while(true)
{
std::string temp;
fifo.ReadNamedPipe(&temp);
std::cout << "shm memory content: " << shmaddr << std::endl;
}
return 0;
}
4.5、client.cc
#include "Shm.hpp"
#include "namedPipe.hpp"
// int main()
// {
// key_t key = GetComKey(gpathname,gproj_id);
// std::cout << "key: " << ToHex(key) << std::endl;
// return 0;
// }
// int main()
// {
// // 1.获取共享内存
// Shm shm(gpathname,gproj_id,gUser);
// // 2.清理内存数据
// shm.Zero();
// // 3.写数据
// char* shmaddr = (char*)shm.Addr(); // 获取地址
// char ch = 'A';
// while(ch <= 'Z')
// {
// shmaddr[ch - 'A'] = ch;
// sleep(2); // 两秒写一次数据
// ch++;
// }
// return 0;
// }
// int main()
// {
// // 1.获取共享内存
// Shm shm(gpathname,gproj_id,gUser);
// // 2.清理内存数据
// shm.Zero();
// // 3.创建管道
// NamePiped fifo(comm_path,User);
// fifo.OpenForWrite();
// // 4.写数据
// char* shmaddr = (char*)shm.Addr(); // 获取地址
// char ch = 'A';
// while(ch <= 'Z')
// {
// shmaddr[ch - 'A'] = ch;
// std::string temp = "wakeup";
// std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;
// fifo.WriteNamedPipe(temp);
// sleep(2); // 两秒写一次数据
// ch++;
// }
// return 0;
// }
int main()
{
// 1. 创建共享内存
Shm shm(gpathname, gproj_id, gCreater);
char *shmaddr = (char*)shm.Addr();
shm.DebugShm();
sleep(5);
return 0;
}