0
点赞
收藏
分享

微信扫一扫

linux系统编程:使用共享内存实现进程间的通信(附C++实现代码)

他说Python 2022-03-30 阅读 34

文章目录


共享内存基本概念

共享内存是目前比较主流的进程之间互相通信的方式,这种通信中读取数据的对象和写入数据的对象不一定是同一个,写入的所有数据放在一块共享的内存中,这样方式的通信十分方便快捷。但是这样就会有一些数据的同步问题,因为在这个内存上的读取时没有阻塞的,每个人都不知道自己读取的数据是不是最新的自己需要的数据,这也是我们在使用共享内存进行通信的时候需要注意的问题。

共享内存可以将将一个文件映射到一块内存上;这种不同于以往读取文件内容的方式,我们使用管道或者其他方式读写文件内容时,往往需要打开两次文件,一次读取文件,将文件内容读入一个buff中,然后在buff中进行修改,再将buff中的内容重新写回去。而共享内存可以直接在内存上进行修改最终映射回去,它相比而言效率更高。
在这里插入图片描述

内存映射文件

相关函数

函数功能函数格式
创建共享内存int shm_open(const char *name, int oflag, mode_t mode)
取消内存共享int shm_unlink(const char *name)
建立内存映射void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset)
关闭内存映射int munmap(void *start,size_t length)

利用文件映射的方式读文件内容

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <cstring>
using namespace std;

int main(int argc, char** argv){
    if(3!=argc){
        printf("Usage:%s filepath size\n",argv[0]);
        return 1;
    }
    int fd = open(argv[1],O_RDONLY);
    void* buff = mmap(NULL,stoi(argv[2]),PROT_READ,MAP_SHARED,fd,0);   // 进行映射
    if(buff == MAP_FAILED){   // 测试文件是否映射失败
        perror("mmap error");
        return 1;
    }
    cout << static_cast<char*>(buff) << endl;  // 显示被映射的文件内容
    munmap(buff,stoi(argv[2]));  // 将文件映射回去
    close(fd);
}

利用文件映射的方式写文件内容

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <cstring>
using namespace std;

// ./a.out filepath size
int main(int argc, char** argv){
    if(3!=argc){
        printf("Usage:%s filepath size\n",argv[0]);
        return 1;
    }
    int fd = open(argv[1],O_RDWR);
    void* buff = mmap(NULL,stoi(argv[2]),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(buff == NULL){
        perror("mmap error");
        return 1;
    }
    cout << static_cast<char*>(buff) << endl;
    int pos;
    string s;
    cin >> pos >> s;
    strcpy(static_cast<char*>(buff)+pos,s.c_str());   // 对映射的内存内容的pos位置增添内容s

    munmap(buff,stoi(argv[2]));
    close(fd);
}

利用共享内存直接创建文件

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <cstring>
using namespace std;

// ./a.out filepath size
int main(int argc, char** argv){
    if(3!=argc){
        printf("Usage:%s filepath size\n",argv[0]);
        return 1;
    }
    int fd = open(argv[1],O_RDWR|O_CREAT,0666);  // 如果文件存在就打开该文件,如果文件不存在就创建一个权限为0666的文件
    ftruncate(fd,stoi(argv[2]));   // 因为我们直接使用open函数创建的文件大小为0,所以在对该文件进行映射前需要使用该函数对文件大小进行扩充。
    void* buff = mmap(NULL,stoi(argv[2]),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(buff == NULL){
        perror("mmap error");
        return 1;
    }
   
    string s;
    cin >> s;
    strcpy(static_cast<char*>(buff),s.c_str());   // 对映射的内存内容增添内容s
    munmap(buff,stoi(argv[2]));
    close(fd);
}

使用共享内存进行进程间通信

我们首先创建两个进程,工作代码如下:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

using namespace std;

// 共享内存:让父子进程共享内存
int main(){
    int n = 0;
    if(fork() == 0){
        for(int i=0; i<10; i++)
        cout << getpid() << ":" << ++n << endl;
    }else{
        for(int i=0; i<10; i++)
        cout << getpid() << ":" << --n << endl;
    }
}

运行结果如下:
在这里插入图片描述

接下来,我们创建一个共享内容,让该父子进程同时访问该内存上的内容。

代码如下:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

using namespace std;

// 共享内存:让父子进程共享内存
int main(){

    int fd = open("/dev/zero",O_RDWR);  // /dev/zero创建共享内存的一个特
殊文件
    if(-1 == fd){  // 判断文件是否打开成功
        perror("open error");
        return 1;
    }
    void* buff = mmap(NULL,sizeof(int),PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
    if(MAP_FAILED == buff){  // 判断文件是否映射成功
        perror("mmap error");
        return 1;
    }
    int &n = *static_cast<int*>(buff);
    if(fork() == 0){
        for(int i=0; i<10; i++)
        cout << getpid() << ":" << ++n << endl;
    }else{
        for(int i=0; i<10; i++)
        cout << getpid() << ":" << --n << endl;
    }

    munmap(buff, sizeof(int));
    buff = NULL;
    close(fd);
}

运行结果如下:

在这里插入图片描述

除了以上这种创建共享内存之外,我们还可以使用另外一种风格,这两种方法效果相同,主要区别是不需要打开文件,直接创建一个匿名共享内存,创建方法相对简单,但是这种匿名共享内存只能在亲缘进程中使用。具体代码如下:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

using namespace std;

// 共享内存:让父子进程共享内存
int main(){
    void* buff = mmap(NULL,sizeof(int),PROT_WRITE|PROT_READ,MAP_SHARED|MAP_ANON,-1,0);   // 申请匿名共享内存
    if(MAP_FAILED == buff){  // 判断文件是否映射成功
        perror("mmap error");
        return 1;
    }
    int &n = *static_cast<int*>(buff);
    if(fork() == 0){
        for(int i=0; i<10; i++)
        cout << getpid() << ":" << ++n << endl;
    }else{
        for(int i=0; i<10; i++)
        cout << getpid() << ":" << --n << endl;
    }

    munmap(buff, sizeof(int));
    buff = NULL;
}

执行结果如下:

在这里插入图片描述

创建可命名的共享内存

我们知道匿名的共享内存只能父子进程才能使用,那么非亲缘进程该怎么使用共享内存呢?

这就用到了我们开头提及的shm_open函数,在这一节我们会写三个文档,分别用于创建共享内存、向共享内存中写数据以及读出共享内存中的数据。具体如下:

该文件内容如下:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

using namespace std;

int main(int argc, char* argv[]){
    if(3 != argc){
        printf("Usage:%s name size\n",argv[0]);
        return 1;
    }
    int fd = shm_open(argv[1],O_CREAT|O_RDWR,0666);  // 打开一个共享内存
 
    if(-1 == fd){
        perror("shm_open error");
        return 1;
    }
    ftruncate(fd,stoi(argv[2]));
    close(fd);
}

然后我们在终端链接库执行g++ create_shared_memory.cpp -lrt运行程序,然后再做以下命令:

在这里插入图片描述
我们发现该文件创建后默认在dev/shm文件夹下。

文件内容如下:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

using namespace std;

int main(int argc, char* argv[]){
    int fd = shm_open(argv[1], O_RDWR, 0);

    if(-1 == fd){
        perror("shm_open error");
        return 1;
    }
    void* buff = mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
    if(MAP_FAILED == buff){
        perror("mmap error");
        return 1;
    }
    cin >> *(int*)buff;
    munmap(buff, sizeof(int));
    close(fd);
}

我们在终端执行如下命令:

在这里插入图片描述需要注意的是,执行命令./a.out /abc中的/abc并不是指根目录下的abc文件,而是指使用shm_open创建的文件的默认路径下。

文件内容如下:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

using namespace std;

int main(int argc, char* argv[]){
    int fd = shm_open(argv[1], O_RDONLY, 0);

    if(-1 == fd){
        perror("shm_open error");
        return 1;
    }
    void* buff = mmap(NULL, sizeof(int), PROT_READ, MAP_SHARED, fd, 0);
    if(MAP_FAILED == buff){
        perror("mmap error");
        return 1;
    }
    cout << *(int*)buff << endl;
    munmap(buff, sizeof(int));
    close(fd);
}

终端执行命令如下:

在这里插入图片描述

举报

相关推荐

0 条评论