推荐一个简单的网站制作,策划方案免费网站,网站设计经典案例,wap网站的未来什么是共享内存 共享内存是一种计算机编程中的技术#xff0c;它允许多个进程访问同一块内存区域#xff0c;以此作为进程间通信#xff08;IPC, Inter-Process Communication#xff09;的一种方式。这种方式相对于管道、套接字等通信手段#xff0c;具有更高的效率…什么是共享内存 共享内存是一种计算机编程中的技术它允许多个进程访问同一块内存区域以此作为进程间通信IPC, Inter-Process Communication的一种方式。这种方式相对于管道、套接字等通信手段具有更高的效率因为数据不需要在用户空间和内核空间之间进行复制也不需要经过序列化和反序列化的复杂过程。
特点
高速度由于省去了数据复制和上下文切换的开销共享内存提供了非常高的数据交换速度。低延迟适用于需要快速响应和大数据量传输的场景。同步需求虽然高效但多个进程同时访问同一块内存可能会导致数据不一致。因此需要使用如互斥锁、信号量等同步工具来确保数据的正确性和完整性。生命周期管理共享内存段需要显式创建、映射到进程地址空间、使用后断开连接并在不再需要时销毁以避免资源泄露。共享内存在系统中可以存在多个供不同进程之间进行通信
共享内存的原理
每一个进程都有属于自己的进程地址空间假设操作系统在物理内存开辟了一段空间该进程可以创建一段虚拟内存将这段虚拟内存的起始与结束地址通过页表与物理内存的空间构建联系 如果另一个进程也通过上述方式通过页表映射到同一段物理内存那就实现了让多个进程看到同一段空间这样当一个进程向这段物理空间写入数据另一个进程就可以马上从这段空间读取数据了就可以实现进程间的通信了 共享内存的使用
一创建共享内存
#原型int shmget(key_t key, size_t size, int shmflg);
#参数key:用户自定义共享内存的标识size:共享内存大小shmflg:由九个权限标志构成它们的用法和创建文件时使用的mode模式标志是一样的
#返回值成功返回一个非负整数即该共享内存段的标识码失败返回-1【解释】
# key由于进程间具有独立性所以共享内存一定不是某一个进程自己创建的而是进程通过函数调用让操作系统创建的而为了使另一个进程可以找到该共享内存每一个共享内存一定有一个唯一性的标识但是这个标识一定不能是操作系统自己独立生成的因为这样只有要创建共享内存的那个进程能找到该共享内存。所以用户可以通过key自己设定唯一的标识key一般使用函数调用生成
#include sys/types.h
#include sys/ipc.hkey_t ftok(const char *pathname, int proj_id);pathname为文件路径proj_id为项目id这两个都是由用户自己设定的该函数会通过特定的算法将参数形成唯一的key值
#size共享内存的大小建议设置为4096个字节的倍数例如当我们将共享内存设置为4097个字节OS会申请4096*2大小的空间由于我们申请的是4097个字节剩下的4095个字节我们不能使用就会浪费掉
#shmflg: 标志位参数有两种IPC_CREAT、IPC_EXCL,常用的反方式有两种
IPC_CREAT: 如果要创建的共享内存不存在那就创建如果存在就返回该共享内存IPC_CREAT | IPC_EXCL如果创建的共享内存不存在那就创建如果存在就报错在使用时后面一般还要加上权限防止进程无法与共享内存联系注意
ps第二种使用方法可以保证每次创建的共享内存都是新创建的所以在使用上IPC_CREAT | IPC_EXCL一般用于创建共享内存IPC_CREAT一般用于获取共享内存
#返回值共享内存创建成功就返回该共享内存的shmid失败就返回-1
key和shmid都是标识共享内存的唯一性字段不过key是用户自定义的用于让内核区分shm唯一性的用户不能通过key进行对shm管理而shmid是有内核返回的一个值是让用户对共享内存进行管理的id值
二删除共享内存
查看所有的共享内存
ipcs -m利用指令删除共享内存
ipcrm -m shmid 代码删除共享内存
shmctl函数
#功能用于控制共享内存
#原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
#参数shmid:由shmget返回的共享内存标识码cmd:将要采取的动作有三个可取值buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
#返回值成功返回0失败返回-1
命令说明IPC_STAT把shmid_ds数据结构中的数据设置为共享内存当前关联值IPC_SET在进程有足够权限的前提下把共享内存当前关联值设置为shmid_ds数据结构中给出的值IPC_RMID删除共享内存段
ps.删除共享内存关心第三个参数可以直接把他设置为nullptr
三将共享内存连接到进程地址空间
shmat函数
#功能将共享内存段连接到进程地址空间
#原型void *shmat(int shmid, const void *shmaddr, int shmflg);
#参数shmid: 共享内存标识shmaddr:指定连接的地址shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
#返回值成功返回一个指针指向共享内存第一个节失败返回-1
四将共享内存从进程地址空间脱离
#功能将共享内存段与当前进程脱离
#原型int shmdt(const void *shmaddr);
#参数shmaddr: 由shmat所返回的指针
#返回值成功返回0失败返回-1注意将共享内存段与当前进程脱离不等于删除共享内存段 补充共享内存不具有进程之间的同步机制假设一个进程负责读一个进程负责写就算共享内存中没有写入数据读进程还是会一直读这就可能会发生写进程才写了一半的数据就被读走了造成数据不一致问题。我们可以利用管道解决这个问题因为管道具有同步机制我们让写端写完以后通过管道传输信号只有读端通过管道接受到信号以后才会进行对共享内存的读取
(向管道中写的信号是什么不重要只要向共享内存中写后向管道中发送信息在接收到管道信号后才读取共享内存的内容这样就可以让共享内存也存在向管道一样的同步机制写端写一条读端读一条)
代码完整使用
shm.hpp
#include iostream
#include sys/types.h
#include sys/ipc.h
#include sys/shm.h
#include unistd.h#define Creater 1
#define User 2
const char *pathname /home/zyq/mydir/dir/Shm;
int proj_id 0x666;class Shm
{
private:key_t GetComkey(){key_t key ftok(_pathname, proj_id);if (key 0){perror(ftok);return -1;}return key;}int GetShmid(key_t key, int size, int flag){int shmid shmget(_key, size, flag);if (shmid 0)perror(shmget);return shmid;}void AttachShm(){_addrshm shmat(_shmid, nullptr, 0);if (_addrshm nullptr){perror(shmat);}}void DetachShm(){if (_addrshm ! nullptr){int n shmdt(_addrshm);if (n 0)perror(shmdt);}}public:Shm(const char *pathname, int proj_id, int who): _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr){_key GetComkey();if (who Creater){GetCreatershmid();}else{GetUsershmid();}//将共享内存连接到进程地址空间AttachShm();std::cout _key: _key std::endl;std::cout _shmid: _shmid std::endl;}~Shm(){//删除进程地址空间shmctl(_shmid,IPC_RMID,nullptr);//将共享内存脱离进程地址空间DetachShm();}bool GetCreatershmid(){_shmid GetShmid(_key, 4096, IPC_CREAT | IPC_EXCL|0666 );if (_shmid 0)return false;else{std::cout Create shm done! std::endl;return true;}}bool GetUsershmid(){_shmid GetShmid(_key, 4096, IPC_CREAT|0666);if (_shmid 0)return false;else{std::cout Get shm done! std::endl;return true;}}void* Addr(){return _addrshm;}
private:key_t _key;int _shmid;const char *_pathname;int _proj_id;int _who;void *_addrshm;
};server.cc
#include shm.hpp
#include namedpipe.hpp
int main()
{// 创建共享内存并连接Shm shm(pathname, proj_id, Creater);char *shmaddr (char *)shm.Addr();// 创建管道NamedPipe fifo(path, Creater);fifo.OpenforRead();while (true){//读共享内存前先获取唤醒信号std::string str;fifo.ReadNamedPipe(str);std::cout shm content: shmaddr std::endl;sleep(1);}sleep(10);return 0;
}
client.cc
#includeshm.hpp
#includenamedpipe.hpp
int main()
{//获取共享内并连接Shm shm(pathname,proj_id,User);char* shmaddr(char*)shm.Addr();//获取管道NamedPipe fifo(path,User);fifo.OpenforWrite();char chA;while(chZ){shmaddr[ch-A]ch;//写完以后向管道发送唤醒信息//向管道中写的内容不重要主要是利用管道的同步机制std::coutadd ch into shmstd::endl;std::string strWakeupRead;fifo.WriteNamedPipe(str);ch;sleep(2);}sleep(10);return 0;
}