合肥建设学校网站,文创产品设计方案模板,凡科做的网站不能被收录,盐城网站建设与网页制作目录 共享内存概念 模拟实现共享内存 创建key阶段 编辑创建共享内存阶段 删除共享内存阶段 查看共享内存属性阶段 挂接共享内存到进程阶段 取消共享内存与进程挂接阶段 进程通信阶段 添加管道改进版 共享内存函数 shmget函数 shmat函数 shmdt函数 shmctl函数 共享内存概念 共… 目录 共享内存概念 模拟实现共享内存 创建key阶段 编辑创建共享内存阶段 删除共享内存阶段 查看共享内存属性阶段 挂接共享内存到进程阶段 取消共享内存与进程挂接阶段 进程通信阶段 添加管道改进版 共享内存函数 shmget函数 shmat函数 shmdt函数 shmctl函数 共享内存概念 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间这些进程间数据传递不再涉及到内核换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。 之前所学的管道通信本质上是通过内核的文件作为媒介进行通信的而共享内存则是脱离创建管道的范畴转而到物理内存中让两进程通信~ 然而共享内存可以存在很多个因为不仅仅只有进程A,B需要利用共享内存通信其他进程之间也需要所以OS必须管理所有的共享内存~如何管理呢——先描述再组织~ 那么核心问题来了如何让不同进程之间看到同一份共享内存呢之前的管道通信是通过创建管道这种特殊文件让不同进程在内核中通过打开同一个文件来建立通信~而在共享内存中进程之间应该如何做到呢总不能共享内存被创建起那进程AB就自然而然可以通信了吧 如果key由OS生成它哪里知道我们的心思是无法保证让指定进程看到同一份共享内存的这样倒不如我们自己给一个参数(ftok)来让进程A,B作为同一个参数形成key这样就可以看到同一份内存了~
模拟实现共享内存
创建key阶段
//设置好生成key的两个参数
const char* pathname /home/LJZ;
const int proj_id 0x66;//转16进制
std::string ToHex(key_t k)
{char buffer[1024];//将k以16进制的格式写入buffersnprintf(buffer,sizeof(buffer),%x,k);return buffer;
}//获取key函数
key_t GetShmKey()
{//生成keykey_t k ftok(pathname,proj_id);if (k 0){std::cerr ftok error, errno : errno , error string: strerror(errno) std::endl;exit(1);}return k;
}
#include Comm.hppint main()
{key_t key GetShmKey();std::cout key: ToHex(key) std::endl;return 0;
}成功让客户端与服务端都拿到了我们自定义生成的key~ 创建共享内存阶段
//创建共享内存函数
int CreatShm(key_t key,int size,int flag)
{//shmget创建共享内存的返回值类似我们创建文件的文件描述符fdint shmid shmget(key,size,flag);if (shmid 0){std::cerr shmget error, errno : errno , error string: strerror(errno) std::endl;exit(2);}return shmid;
}//为服务端使用由服务端来创建共享内存
int SSCreateShm(key_t key, int size)
{// IPC_CREAT: 不存在就创建存在就获取// IPC_EXCL: 没有意义// IPC_CREAT | IPC_EXCL: 不存在就创建存在就出错返回return CreatShm(key, size, IPC_CREAT | IPC_EXCL | 0666);
}//为客户端使用在共享内存存在的情况下获取它
int SCGetShm(key_t key, int size)
{return CreatShm(key, size, IPC_CREAT);
}
int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//获取共享内存int shmid SCGetShm(key,defaultsize);std::cout shmid: shmid std::endl;return 0;
}int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//创建共享内存int shmid SSCreateShm(key,defaultsize);std::cout shmid: shmid std::endl;return 0;
}以上说明在进程结束时如果我们没有主动释放掉共享内存它会一直存在~ ps:ipcrm -m shmid 手动删除共享内存
删除共享内存阶段 //删除共享内存函数
void DeleteShm(int shmid)
{//删除共享内存int n shmctl(shmid,IPC_RMID,nullptr);if (n 0){std::cerr shmctl error std::endl;}else{std::cout shmctl delete shm success, shmid: shmid std::endl;}
} int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//创建共享内存int shmid SSCreateShm(key,defaultsize);std::cout shmid: shmid std::endl;sleep(10);//10s后删除共享内存DeleteShm(shmid);return 0;
}查看共享内存属性阶段 //查看共享内存属性函数
void ShmDebug(int shmid)
{//shmid_ds里面存储着其信息struct shmid_ds shmds;//将与标识符为 shmid 的共享内存段相关联的内核数据结构中的信息复制到由 buf 所指向的 shmid_ds 结构中int n shmctl(shmid, IPC_STAT, shmds);if (n 0){std::cerr shmctl error std::endl;return;}std::cout shmds.shm_segsz: shmds.shm_segsz std::endl;std::cout shmds.shm_nattch: shmds.shm_nattch std::endl;std::cout shmds.shm_ctime: shmds.shm_ctime std::endl;std::cout shmds.shm_perm.__key: ToHex(shmds.shm_perm.__key) std::endl;
} int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//创建共享内存int shmid SSCreateShm(key,defaultsize);std::cout shmid: shmid std::endl;//查看共享内存信息ShmDebug(shmid);sleep(10);//10s后删除共享内存DeleteShm(shmid);return 0;
}以上只是为了演示shmctl中模式的多样化选择~
挂接共享内存到进程阶段 //挂接共享内心到进程函数
void *ShmAttach(int shmid)
{//挂接进程成功则返回已连接的共享内存段的地址。void *addr shmat(shmid, nullptr, 0);if ((long long int)addr -1){std::cerr shmat error std::endl;return nullptr;}return addr;
} int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//获取共享内存int shmid SCGetShm(key,defaultsize);std::cout shmid: shmid std::endl;sleep(2);//挂接共享内存到客户端char * addr (char*)ShmAttach(shmid);std::cout Attach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(5);return 0;
}int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//创建共享内存int shmid SSCreateShm(key,defaultsize);std::cout shmid: shmid std::endl;//挂接共享内存到服务端char * addr (char*)ShmAttach(shmid);std::cout Attach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(10);//查看共享内存信息//ShmDebug(shmid);sleep(100);//10s后删除共享内存DeleteShm(shmid);return 0;
}有时候我们不想让某一个进程挂接共享内存了总不能直接把共享内存删掉吧可以取消它与共享内存的挂接~
取消共享内存与进程挂接阶段
//取消共享内存与进程挂接函数
void ShmDetach(void *addr)
{//通过进程的虚拟地址取消挂接int n shmdt(addr);if (n 0){std::cerr shmdt error std::endl;}
}int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//获取共享内存int shmid SCGetShm(key,defaultsize);std::cout shmid: shmid std::endl;sleep(2);//挂接共享内存到客户端char * addr (char*)ShmAttach(shmid);std::cout Attach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(5);//客户端取消挂接ShmDetach(addr);std::cout Detach shm success, addr: ToHex((uint64_t)addr) std::endl;return 0;
}int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//创建共享内存int shmid SSCreateShm(key,defaultsize);std::cout shmid: shmid std::endl;//挂接共享内存到服务端char * addr (char*)ShmAttach(shmid);std::cout Attach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(10);//查看共享内存信息//ShmDebug(shmid);//服务端取消挂接ShmDetach(addr);std::cout Detach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(20);//10s后删除共享内存DeleteShm(shmid);return 0;
}所有工作准备完毕进入通信阶段~ 进程通信阶段
int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//创建共享内存int shmid SSCreateShm(key,defaultsize);std::cout shmid: shmid std::endl;//挂接共享内存到服务端char * addr (char*)ShmAttach(shmid);std::cout Attach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(10);//查看共享内存信息//ShmDebug(shmid);std::cout Detach shm success, addr: ToHex((uint64_t)addr) std::endl;//通信阶段服务端读取数据//直接打印在共享内存中的数据for(;;){cout shm content: addr std::endl;sleep(1);}sleep(100);//服务端取消挂接ShmDetach(addr);sleep(20);//10s后删除共享内存DeleteShm(shmid);return 0;
} int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//获取共享内存int shmid SCGetShm(key,defaultsize);std::cout shmid: shmid std::endl;sleep(2);//挂接共享内存到客户端char * addr (char*)ShmAttach(shmid);std::cout Attach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(5);//通信阶段客户端写入数据//客户端直接在共享内存中写数据memset(addr,0,defaultsize);for (char c A; c Z; c) {addr[c - A] c;sleep(1);}sleep(100);//客户端取消挂接ShmDetach(addr);std::cout Detach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(5);return 0;
}不过这种通信有个无法避免的缺陷在默认情况中作为shm读取方是不会去阻塞等待写入方的就一直读取不管有没有写入优点是共享内存这种方式是进程通信中速度最快的因为写入数据的同时另外一方直接在内存中就可以读取不需要管道传递的媒介但缺点也很明显无法提供进程间协同的任何机制如果我们想要发送一整段字符串时会被这种方式切割为一个个字符读入~
为了能控制想要发送与读取的大小我们利用管道来实现同步的机制~
添加管道改进版 #define Mode 0666
#define Path ./fifo//命名管道
class Fifo
{
public:Fifo(const string path Path) : _path(path){umask(0);int n mkfifo(_path.c_str(), Mode);if (n 0){cout mkfifo success endl;}else{cerr mkfifo failed, errno: errno , errstring: strerror(errno) endl;}}~Fifo(){int n unlink(_path.c_str());if (n 0){cout remove fifo file _path success endl;}else{cerr remove failed, errno: errno , errstring: strerror(errno) endl;}}private:string _path; // 文件路径文件名
};//利用管道进行同步数据
class Sync
{
public:Sync() : rfd(-1), wfd(-1){}//以读方式打开管道void OpenRead(){rfd open(Path, O_RDONLY);if (rfd 0)exit(1);}//以写方式打开管道void OpenWrite(){wfd open(Path, O_WRONLY);if (wfd 0)exit(1);}//让服务端等待管道中写端的写入到达一定量后再一次性读取bool Wait(){bool ret true;uint32_t c 0;ssize_t n read(rfd, c, sizeof(uint32_t));//读取的数据大小为uint32_t时提示准备唤醒服务端读取数据代替之前时时刻刻读if (n sizeof(uint32_t)){std::cout server wakeup, begin read shm... std::endl;}//代表管道写端关闭读端读取数据结束返回0else if (n 0){ret false;}else{return false;}return ret;}//往管道写入数据量够的时候作提示唤醒服务端void Wakeup(){uint32_t c 0;//往管道写入数据ssize_t n write(wfd, c, sizeof(c));//写够数据量的时候提示唤醒服务端可以读取了assert(n sizeof(uint32_t));std::cout wakeup server... std::endl;}~Sync() {}private:int rfd;int wfd;
};
#include Comm.hpp
#include unistd.h
#include Fifo.hpp
int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//获取共享内存int shmid SCGetShm(key,defaultsize);std::cout shmid: shmid std::endl;sleep(2);//挂接共享内存到客户端char * addr (char*)ShmAttach(shmid);std::cout Attach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(5);//通信阶段客户端写入数据//客户端直接在共享内存中写数据memset(addr,0,defaultsize);Sync syn;//写方式打开管道syn.OpenWrite();for (char c A; c Z; c) {addr[c - A] c;sleep(1);//往管道写入数据写够时发出提示可唤醒服务端读取数据syn.Wakeup();}sleep(100);//客户端取消挂接ShmDetach(addr);std::cout Detach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(5);return 0;
} int main()
{//获取keykey_t key GetShmKey();std::cout key: ToHex(key) std::endl;//创建共享内存int shmid SSCreateShm(key,defaultsize);std::cout shmid: shmid std::endl;//挂接共享内存到服务端char * addr (char*)ShmAttach(shmid);std::cout Attach shm success, addr: ToHex((uint64_t)addr) std::endl;sleep(10);//查看共享内存信息//ShmDebug(shmid);// 0. 先引入管道Fifo fifo;Sync syn;//读方式打开管道syn.OpenRead();std::cout Detach shm success, addr: ToHex((uint64_t)addr) std::endl;//通信阶段服务端读取数据//直接打印在共享内存中的数据for(;;){//读取数据量不够的时候退出不让打印读取出内容//读取数据量够的时候打印堆积的内容if(!syn.Wait()) break;cout shm content: addr std::endl;sleep(1);}sleep(100);//服务端取消挂接ShmDetach(addr);sleep(20);//10s后删除共享内存DeleteShm(shmid);return 0;
}管道意义就在于不要让服务端读那么快而是在管道内积累一定数据量后再一次性读取数据实现同步的机制。
共享内存函数
shmget函数 功能用来创建共享内存 原型 int shmget(key_t key, size_t size, int shmflg); 参数 key: 这个共享内存段名字 size: 共享内存大小 shmflg: 由九个权限标志构成它们的用法和创建文件时使用的 mode 模式标志是一样的 返回值成功返回一个非负整数即该共享内存段的标识码失败返回-1 shmat函数 功能将共享内存段连接到进程地址空间 原型 void *shmat(int shmid, const void *shmaddr, int shmflg); 参数 shmid: 共享内存标识 shmaddr: 指定连接的地址 shmflg: 它的两个可能取值是 SHM_RND 和 SHM_RDONLY 返回值成功返回一个指针指向共享内存第一个节失败返回 -1 shmaddr 为 NULL 核心自动选择一个地址 shmaddr 不为 NULL 且 shmflg 无 SHM_RND 标记则以 shmaddr 为连接地址。 shmaddr 不为 NULL 且 shmflg 设置了 SHM_RND 标记则连接的地址会自动向下调整为 SHMLBA 的整数倍。公式 shmaddr - (shmaddr % SHMLBA) shmflgSHM_RDONLY 表示连接操作用来只读共享内存 shmdt函数 功能将共享内存段与当前进程脱离 原型 int shmdt(const void *shmaddr); 参数 shmaddr: 由 shmat 所返回的指针 返回值成功返回 0 失败返回 -1 注意将共享内存段与当前进程脱离不等于删除共享内存段 shmctl函数 功能用于控制共享内存 原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf); 参数 shmid: 由 shmget 返回的共享内存标识码 cmd: 将要采取的动作有三个可取值 buf: 指向一个保存着共享内存的模式状态和访问权限的数据结构 返回值成功返回 0 失败返回 -1