✨个人主页: 熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】
目录
1、共享内存的概念
2、创建共享内存
2.1、函数介绍
2.2、代码测试
3、封装成类
3.1、基本框架
3.2、析构函数
3.3、私有成员函数
3.4、创建共享内存函数
3.5、构造函数
3.6、测试
3.7、进行通信
3.8、查看共享内存属性
4、完整代码
4.1、Makefile
4.2、Shm.hpp
4.3、namedPipe.hpp
4.4、server.cc
4.5、client.cc
1、共享内存的概念
共享内存是指多个进程共享同一块物理内存区域的技术。在Linux操作系统中,通过系统调用和内核支持,不同的进程可以映射到同一块物理内存,并直接读写这块内存区域,避免了数据在进程间的拷贝和传递,提高了数据访问的效率。
1、OS创建共享内存空间。
2、通过页表建立物理内存与虚拟内存的映射。
如何理解上面的这个过程呢?
1、前面做的操作都是OS做的。
2、OS提供上面1,2的步骤的系统调用,供用户进程A,B来进行调用。
3、AB进程可以共享,那么CD,EF都可以共享内存 ==> 共享内存在系统中可以存在多份,供不同个数,不同对进程同时进行通信。
4、OS注定要对共享内存进行管理! ==> 先描述在组织 ==> 共享内存不是简单的一段内存空间,也要有描述并管理的数据结构和匹配的算法。
5、共享内存 = 内存空间(数据) + 共享内存的属性(结构体封装)
共享内存结构体中一定要有标识内存唯一性的字段,但是又不能让OS自己生成,那么应该怎么生成呢?
其实我们只要设计一套规则,让用户自己设置唯一的标识符编号即可,但是通常使用ftok()函数获取共享内存唯一标识符。
2、创建共享内存
会用到的头文件及全局变量
#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、函数介绍
创建共享内存系统调用函数
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 VALUEOn 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()
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 VALUEOn 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、代码测试
获取唯一标识符
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进制数
std::string ToHex(key_t key)
{char buffer[128];snprintf(buffer,sizeof(buffer),"0x%x",key);return buffer;
}
创建共享内存
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值
server.cc
int main()
{key_t key = GetComKey(gpathname,gproj_id);std::cout << "key: " << ToHex(key) << std::endl;return 0;
}
client.cc
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、基本框架
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; // 用户名
};
删除共享内存
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、析构函数
创建者才需要删除共享内存!!!
~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值和创建共享内存函数封装为私有
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、创建共享内存函数
根据用户创建共享内存
// 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、构造函数
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、测试
while :; do ipcs -m;sleep 1;done # 每隔一秒查看一次共享内存属性信息
server.cc
int main()
{Shm shm(gpathname,gproj_id,gCreater);return 0;
}
运行结果
client.cc
int main()
{Shm shm(gpathname,gproj_id,gUser);return 0;
}
运行结果
1、共享内存不随进程结束而自动释放,即共享内存声明周期随内核==> 一直存在,直到系统重启
2、共享内存手动释放(指令或者其他系统调用)
运行结果
先执行服务端程序,再执行客户端程序,会看到一个现象,共享内存挂接进程数从没有变成1,再从1变成2,然后从2变成1,且状态变为dest(被删除但还有用户在使用),最后没有挂接数。
3.7、进行通信
shmat()
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()
shmdt : 将当前进程地址空间中附加的共享内存段分离#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);参数shmaddr:这是一个指向共享内存段起始地址的指针。这个地址通常是之前通过调用shmat函数获得的,
表示当前进程地址空间中共享内存段的起始位置。
返回值成功:函数执行成功时,返回0。失败:如果函数执行失败,则返回-1,并设置相应的errno以指示错误原因。
此处需要获取地址,因此可以增加一个存储地址的成员变量。
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);}
}
测试
客户端写数据,服务端读数据。
client.cc
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;
}
server.cc
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速度最快的,因为共享内存大大减少了数据的拷贝次数。
- 通过上面测试可以看出共享内存一个缺点:共享内存不提供任何的保护机制 --- 数据不一致问题!!!
怎么解决这个缺点呢?
使用命名管道,因为命名管道是同步通信的!!!
server.cc
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;
}
client.cc
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.ccg++ -o $@ $^ -std=c++11
server:server.ccg++ -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 4096const 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;
}