您的位置:首页 > 教育 > 培训 > 花桥网站建设公司_公众号二次开发_举例说明seo_常见的网站推广方法

花桥网站建设公司_公众号二次开发_举例说明seo_常见的网站推广方法

2025/4/4 0:21:54 来源:https://blog.csdn.net/2301_79927432/article/details/146798703  浏览:    关键词:花桥网站建设公司_公众号二次开发_举例说明seo_常见的网站推广方法
花桥网站建设公司_公众号二次开发_举例说明seo_常见的网站推广方法

目录

一 前言

二 共享内存概念

 三 共享内存创建 

四 查看共享内存 

五 共享内存的删除

六 共享内存的关联 

七 共享内存去关联 

八 共享内存的使用(通信)

 九 共享内存的特点


一 前言

共享内存区是最快的IPC形式(进程间通信:IPC,InterProcess Communication) 一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用write()和read()来传递彼此的数据。


二 共享内存概念

 在上一篇进程间的管道通信中我们提到过,在进程间进行通信的时候,由于程序地址空间的存在,进程间的独立性使得他们之间的通信很麻烦,如果想要通信则需要两个进程看到同一份资源,上篇通过系统调用创建管道文件,使得进程之间看到共享资源(内存级文件),而本节进程间通信时进程之间看到的同一份资源是  共享内存。

🚀什么是共享内存呢? 

实际上,我们在学习程序地址空间的时候,如上图所示,我们已经看到了有一个区域的名字是共享区,在之前我们学习动静态库的时候,就说过动态库是在进程运行的时候加载到程序地址空间中的共享区的。当程序需要的时候,就会来到这部分读取数据。这一块内存就可以看作是一块只读共享区,共享内存进程通信实际上就是这个原理。

             共享内存进程通信就是在物理内存中开辟一块可以让所有进程都看到的内存空间,然后多个进程只需要向这块空间中读取或者写入数据,这样就达到了多个进程间一起通信的目的

也就是说,共享内存进程间的通信就是在物理内存中开辟一块空间当作共享内存,然后通信的进程们通过各自的页表将这块物理内存(共享内存)映射到各自的程序地址空间中 


 三 共享内存创建 

shmget()    (share memory  get)

这个接口的参数一共有三个 

  1. key_t key :  这是一个键值,key_t是一个整型,此参数其实是传入的是一个整数。通常这个键是通过  ftok() 函数从一个文件路径和一个项目ID生成的. 这个key值其实就是共享内存段在操作系统层面的唯一标识符。共享内存是Linux系统的一种进程通信的手段, 而操作系统中共享内存段一定是有许多的, 为了管理这些共享内存段, 操作系统一定会描述共享内存段的各种属性。类似其他管理方式,操作系统也会为共享内存维护一个结构体,在这个结构体内会维护一个key值,表示此共享内存在系统层面的唯一标识符,其一般由用户传入,为了区别每一块的共享内存,key的获取也是有一定的方法。

    ftok()函数的作用是将 一个文件 和 项目id 转换为一个System V IPC key值。用户就是使用这个函数来生成key值。

    他有两个参数,第一个参数显而易见是文件的路径,第二个参数则是随意的8bite位的数值。ftok()函数执行成功则会返回一个key值,这个key值是该函数通过传入文件的inode值和传入的proi_id值通过一定的算法计算出来的。由于每一个文件的inode值是唯一的,所以我们不用担心key值得重复。

  2. size_t size:  该参数传入的是想要开辟的共享内存的大小,单位是 byte字节。值得注意的是系统是按照4KB大小为单位开辟空间的,因为我们在磁盘一篇中学到系统I/O的单位大小就是4KB,也就是说无论这个参数传的是1、1024还是4096时,系统都会开辟4KB,但是虽然系统是按照4KB为单位开辟的空间,但是实际上用户能使用的空间的大小还是传入的size字节大小。

  3. int shmflg: 这里传入的是一组标识位,可以控制shemget的行为,它包括权限标志(类似0666)和命令标志,就像我们使用文件接口open时的O_WRONLY、O_RDONLY一样。共享内存接口标识符的两个最重要的宏是:IPC_CREAT、IPC_EXCL

    IPC_CREAT:传入该宏,表示创建一个新的共享内存段,若共享内存段已经存在,则获取此内存段;若不存在就创建一个新的内存段。

    IPC_EXCL:该宏需要和IPC_CREAT一起使用。表示如果创建的内存段不存在,则正常创建,若存在则返回错误。使用该宏保证的是此次使用shmget()接口创建成功时,创建出来的共享内存是全新的。

  4. shmget()函数的返回值,如果创建共享内存成功或者找到共享内存则返回共享内存的id,该id的作用是可以让通信的进程找到同一份块的资源。此id 是给上层用户使用,是为了标识共享内存。而key也是为了标识共享内存,但是是从系统层面来说。

🚍:接下来我们来学习和认识共享内存的创建

///comm.hpp/
#ifndef _COMM_HPP_
#define COMM_HPP_#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>#define PATHNAME "."
#define PROJ_ID 0x66           //目标标识符 int proj_id 就是一个整型
#define MAX_SIZE 4096
key_t getKey()
{key_t k = ftok(PATHNAME,PROJ_ID);//可以获取同一个keyif(k < 0){//cin ,cout,cerr -------->stdin stdout stderr--->0 1 2std::cerr<<errno<<":"<<strerror(errno)<<std::endl;//strerror(errno) 打印错误码对应的错误信息exit(1);}return k;
}
/shm_client.cpp///
#include "comm.hpp"
int main()
{key_t k=getKey();printf("key: 0x%x\n",k); return 0;
}
///shm_server.cpp
#include "comm.hpp"int main()
{key_t k=getKey();printf("key: 0x%x\n",k);return 0;
}

 运行结果:

 🚋:key的值是什么并不重要,重要的是能进行唯一性标识。

有了key之后,我们就可以用唯一的key进行共享内存的创建

//comm.hpp
//将一些函数进行封装到comm.hpp,然后client和server进行调用
int getShmHelper(key_t k,int flags)
{int shmid=shmget(k,MAX_SIZE,flags);//创建共享内存函数shmgetif(shmid < 0){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(2);}return shmid;
}//获取共享内存
int getShm(key_t k)
{return getShmHelper(k,IPC_CREAT); //如果存在就获取,所以在客户端client,我们可以通过这个函数,来获取共享内存
}
////创建共享内存
int createShm(key_t k)//创建共享内存的工作,有服务端server来做
{return getShmHelper(k,IPC_CREAT | IPC_EXCL | 0600);//如果存在就创建失败,保证了我们创建的一定是新的共享内存,0600 代表创建的共享内存可读可写
}
//server.cpp
#include "comm.hpp"int main()
{key_t k=getKey();printf("key: 0x%x\n",k);int shmid=createShm(k);//服务端进行创建共享内存printf("shmid: %d\n",shmid);return 0;
}
///client.cpp
#include "comm.hpp"
int main()
{key_t k=getKey();printf("key: 0x%x\n",k);int shmid=getShm(k);//客户端进行获取共享内存printf("shmid: %d\n",shmid);return 0;
}

 运行结果:

 🍉这里的 key shmid 有什么区别呢?

key:是系统层面的,系统通过key来创建共享内存。

shmid:是上层用户层面,用户通过shmid来找到共享内存。


四 查看共享内存 

可是当我们再次运行服务端的时候,会发现出现如下问题:文件已存在

 这是什么原因呢?事实上,共享内存并不会随着进程的退出而退出,在创建共享内存的进程退出之后,共享内存是依旧存在于操作系统中的。而我们的服务端用key创建共享内存的时候,必须要求创建一个新的,如果当前的key对应的共享内存已经存在,则报错。

我们可以通过命令查看共享内存资源: ipcs -m  

这表明共享内存的生命周期是随着OS的,并不会因为进程的退出,而把共享内存删除。


五 共享内存的删除

我们可以使用 ipcrm -m (InterProcess Communication Remove Memory) 命令来删除。

那我们是通过 key 删除共享内存还是 shmid呢? 前面我们也说了key是内核层面,操作系统使用key,而删除共享内存,是指令操作,属于用户层面,所以我们通过shmid删除共享内存。

 我们还可以通过调用系统函数  shmctl 来对共享内存进行删除

我们在comm.hpp中对 shmctl进行封装成删除共享内存的函数。

/comm.hpp
void delShm(int shmid)
{if(shmctl(shmid,IPC_RMID,nullptr)==-1)//返回-1代表调用失败{std::cerr<<"shmctl"<<errno<<":"<<strerror(errno)<<std::endl;}
}
/sever.cpp
#include "comm.hpp"int main()
{key_t k=getKey();printf("key: 0x%x\n",k);int shmid=createShm(k);//服务端进行创建共享内存printf("shmid: %d\n",shmid);sleep(5);delShm(shmid);//调用删除共享内存函数}

 再次测试:


六 共享内存的关联 

🌏:前面我们讲述了服务端对共享内存的创建以及删除,但是要想使得两个进程进行通信,我们还需要进行共享内存对两个进程关联起来。

 系统调用函数 shmat (attach)

 我们在comm.hpp中对 shmat进行封装成关联共享内存的函数。

/comm.hpp//
//关联共享内存
void* attachShm(int shmid)
{void* mem =shmat(shmid,nullptr,0);if((long long)mem==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(3);}return mem;
}
///server.cpp
#include "comm.hpp"int main()
{key_t k=getKey();printf("key: 0x%x\n",k);int shmid=createShm(k);//服务端进行创建共享内存printf("shmid: %d\n",shmid);sleep(5);//关联共享内存char* start=(char*)attachShm(shmid);//返回值是共享内存地址printf("attach success,address start:%p\n",start);//删除sleep(5);delShm(shmid);return 0;
}

测试结果


七 共享内存去关联 

既然共享内存可以关联,自然也可以去关联,去关联并不是删除共享内存,而是去除进程与共享内存的联系。

系统调用函数 shmdt()    (detach)

我们在comm.hpp中对 shmdt进行封装成去关联共享内存的函数。

//comm.cpp////去关联共享内存
void detachShm(void* start)
{if(shmdt(start)==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;}
}

八 共享内存的使用(通信)

🌿,在前面我们做了以下工作

共享内存的创建------------->关联共享内存---------->(这里我们将进行共享内存的通信)---------------->关联共享内存------------------------------------>删除共享内存 

//client.cpp
// 4.使用即通信const char* message="hello server, 我是另外一个进程正在和你通信";pid_t id=getpid();int count =0;while(true){sleep(1);//向共享内存输入消息snprintf(start,MAX_SIZE,"%s[pid:%d][消息编号:%d]",message,id,count++);//pid count message}
/server.cpp///
//4.使用while(true){printf("client say: %s\n",start);//打印由客户端发来的消息,直接打印共享内存地址即可sleep(1);}

测试结果:

 

 完整测试如下

/comm.hpp
#ifndef _COMM_HPP_
#define COMM_HPP_#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>#define PATHNAME "."
#define PROJ_ID 0x66           //目标标识符 int proj_id 就是一个整型
#define MAX_SIZE 4096
key_t getKey()
{key_t k = ftok(PATHNAME,PROJ_ID);//可以获取同一个keyif(k < 0){//cin ,cout,cerr -------->stdin stdout stderr--->0 1 2std::cerr<<errno<<":"<<strerror(errno)<<std::endl;//strerror(errno) 打印错误码对应的错误信息exit(1);}return k;
}int getShmHelper(key_t k,int flags)
{int shmid=shmget(k,MAX_SIZE,flags);//创建共享内存函数shmgetif(shmid < 0){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(2);}return shmid;
}//获取共享内存
int getShm(key_t k)
{return getShmHelper(k,IPC_CREAT); //如果存在就获取,所以在客户端client,我们可以通过这个函数,来获取共享内存
}
////创建共享内存
int createShm(key_t k)//创建共享内存的工作,有服务端server来做
{return getShmHelper(k,IPC_CREAT | IPC_EXCL | 0666);//如果存在就创建失败,保证了我们创建的一定是新的共享内存
}//关联共享内存
void* attachShm(int shmid)
{void* mem =shmat(shmid,nullptr,0);if((long long)mem==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(3);}return mem;
}//去关联共享内存
void detachShm(void* start)
{if(shmdt(start)==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;}
}
//删除共享内存
void delShm(int shmid)
{if(shmctl(shmid,IPC_RMID,nullptr)==-1)//返回-1代表调用失败{std::cerr<<"shmctl"<<errno<<":"<<strerror(errno)<<std::endl;}
}#endif
client.cpp///
#include "comm.hpp"
int main()
{//1.获取keykey_t k=getKey();printf("key: 0x%x\n",k);//2.获取共享内存int shmid=getShm(k);//客户端进行获取共享内存printf("shmid: %d\n",shmid);sleep(5);//3.进行关联char* start=(char*)attachShm(shmid);printf("attach success,address start:%p\n",start);sleep(5);// 4.使用即通信const char* message="hello server, 我是另外一个进程正在和你通信";pid_t id=getpid();int count =0;while(true){sleep(1);//向共享内存输入消息snprintf(start,MAX_SIZE,"%s[pid:%d][消息编号:%d]",message,id,count++);//pid count message}// sleep(5);//5.去关联detachShm(start);return 0;
}
/server.cpp
#include "comm.hpp"int main()
{//1.创建keykey_t k=getKey();printf("key: 0x%x\n",k);//2.创建共享内存int shmid=createShm(k);//服务端进行创建共享内存printf("shmid: %d\n",shmid);sleep(5);//3. 关联共享内存char* start=(char*)attachShm(shmid);//返回值是共享内存地址printf("attach success,address start:%p\n",start);//4.使用while(true){printf("client say: %s\n",start);//打印由客户端发来的消息,直接打印共享内存地址即可sleep(1);}// // 5.去关联detachShm(start);sleep(5);//删除sleep(10);delShm(shmid);return 0;
}

 九 共享内存的特点

共享内存的优点:所以进程间通信,速度是最快的,能大大减少拷贝次数。

同样的代码,考虑到键盘输入和显示器输出 ,如果用管道来实现,会对数据进行几次拷贝?

共享内存的缺点:不给我们进行同步和互斥的操作,没有对数据做任何保护。

即客户端不进行写,服务端也一直进行读取,服务端不进行读取,客户端依然进行写入。 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com