当前位置: 首页 > news >正文

【Linux系统编程】第三十六弹---深入探索进程间通信:封装共享内存类并实现进程间数据共享

个人主页: 熬夜学编程的小林

💗系列专栏: 【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;
}


http://www.mrgr.cn/news/56680.html

相关文章:

  • 浅谈微前端【qiankun】的应用
  • 查询MySQL表以及表中数据条数
  • 【数据结构与算法】第3课—数据结构之单链表
  • UDP/TCP协议
  • Vue使用OnlyOffice预览文档方案
  • SHELL脚本之循环语句的for循环以及中断循环的语句
  • python安卓开发
  • 0160____declspec(noreturn)的用法
  • 【趣学C语言和数据结构100例】
  • C++网络编程之绑定
  • PCB生产制造商强达电路,公布网上申购情况及中签率
  • Transformer 天气数据进行时序预测
  • Github 2024-10-23C开源项目日报 Top10
  • 本地函数 lambda函数 回调函数(c#)
  • Redis内部数据结构ziplist详解
  • 文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《需求响应下计及高耗能工业负荷生产流程的经济调度模型》
  • PCDN 的激励机制对平台可持续发展的影响(贰)
  • 闯关leetcode——190. Reverse Bits
  • [论文笔记]ColPali: Efficient Document Retrieval with Vision Language Models
  • PCL SAC-IA 算法实现点云粗配准(永久免费版)
  • 【卡尔曼滤波】观测模型包含输入的线性卡尔曼滤波
  • 输出时间序列中的时区是什么Series.dt.tz_convert(tz)
  • 酒店智能轻触开关的类型及其应用
  • 过零检测比较器电路设计
  • 【数据结构与算法】Java中的基本数据结构:数组、链表、树、图、散列表等。
  • Java | Leetcode Java题解之第502题IPO