共享内存的学习以及使用笔记

什么是共享内存

在软件中,术语共享内存指可被多个进程存取的内存,一个进程是一段程序的单个运行实例。

共享内存有什么好处

采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

通过fork是否能够做到共享内存呢?

不能,fork是一种创建自身程序副本的操作。它通常是内核实现的一种系统调用。
引用man page中的信息:

fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.

The child process and the parent process run in separate memory spaces. At the time of fork() both memory spaces have the same content. Memory writes, file mappings (mmap(2)), and unmappings (munmap(2)) performed by one of the processes do not affect the other.

也就是说,父进程和子进程各种拥有自己的内存空间,各自对内存的操作不会影响到对方,而且fork后,二者的内存空间是一模一样的.
那么,写一段程序实验一下:

#include 
#include 
#include 
#include 

std::string name = "parent";

void show_the_name(pid_t);

int main()
{
    auto pid = fork();
    if(pid > 0)
    {
        show_the_name(getpid());
    }
    else if(pid == 0)
    {
        name = "son";
        show_the_name(getpid());
    }
    else
    {
        std::cerr << "error" << std::endl;
    }
    return 0;
}

void show_the_name(pid_t pid)
{
    std::cout << "pid: " << pid << " " << name << std::endl;
}

运行结果:

pid: 20806 parent
pid: 20807 son

所以,由上面code所述,fork后的父进程和子进程间不是公用同一段内存,而是一段相同内容的内存.

但是,如果把代码修改一下:

#include 
#include 
#include 
#include 

std::string name = "parent";

void show_the_name(pid_t);

int main()
{
    auto pid = fork();
    if(pid > 0)
    {
        show_the_name(getpid());
    }
    else if(pid == 0)
    {
        name = "son";
        show_the_name(getpid());
    }
    else
    {
        std::cerr << "error" << std::endl;
    }
    return 0;
}

void show_the_name(pid_t pid)
{
    std::cout << "pid: " << pid << " " << name << std::endl;
    std::cout << "pid: " << pid << " " << &name << std::endl;
}

运行结果:

pid: 21325 parent
pid: 21325 0x602300
pid: 21326 son
pid: 21326 0x602300

咦,对应的name地址怎么是一样的,不要担心,这里指针获取的是变量的在用户空间的虚拟地址,实际上pid不一样,我就可以从内核中获取到他们的基地址

共享内存的操作函数

详细信息查看man page

  1. void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

  2. int munmap(void *addr, size_t length);

  3. int msync ( void * addr , size_t len, int flags);

使用共享内存操作函数

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

struct People {
    char name[10];
    int age;
};

int main()
{
    int len = sizeof(People);
    int fd = open("people", O_CREAT|O_RDWR|O_TRUNC, 00666);
    lseek(fd, sizeof(People)-1,SEEK_SET);
    write(fd, "", 1);
    People *p = (People *)mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    char *siskinc = "siskinc";
    memcpy(p->name, siskinc, strlen(siskinc));
    p->age = 123;
    munmap(p, len);
    return 0;
}

运行程序后,查看people文件(在程序对应的目录):

siskinc{

具体二进制可以用vim查看

你可能感兴趣的:(共享内存的学习以及使用笔记)