Linux IPC实践(8) --共享内存/内存映射

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

概述

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

 

共享内存 VS. 其他IPC形式

     管道/消息队列传递数据

 

共享内存传递数据

 

    共享内存生成之后,传递数据并不需要再走Linux内核,共享内存允许两个或多个进程共享一个给定的存储区域,数据并不需要在多个进程之间进行复制,因此,共享内存的传输速度更快!


mmap内存映射

将文件/设备空间映射到共享内存区

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);

参数:

    addr:  要映射的起始地址, 通常指定为NULL, 让内核自动选择;

    length: 映射到进程地址空间的字节数;

    prot: 映射区保护方式(见下);

    flags: 标志(见下);

    fd: 文件描述符;

    offset: 从文件头开始的偏移量;

 

prot

说明

PROT_READ

页面可读

PROT_WRITE

页面可写

PROC_EXEC

页面可执行

PROC_NONE

页面不可访问

 

flags

说明

MAP_SHARED

变动是共享的

MAP_PRIVATE

变动是私有的

MAP_FIXED

准确解释addr参数, 如果不指定该参数, 则会以4K大小的内存进行对齐

MAP_ANONYMOUS

建立匿名映射区, 不涉及文件

 

mmap返回值:

    成功: 返回映射到的内存区的起始地址;

    失败: 返回MAP_FAILED;

 

内存映射示意图:


(注意: 内存映射时, 是以页面(4K)作为单位)

/** 示例1: 写文件映射
将文件以可读,可写的方式映射到进程的地址空间, 然后向其中写入内容
**/
struct Student
{
    char name[4];
    int age;
};
int main(int argc,char **argv)
{
    if (argc != 2)
        err_quit("usage: ./main <file-name>");

    int fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);
    if (fd == -1)
        err_exit("file open error");

//为内存映射争取空间
    if (lseek(fd, sizeof(Student)*5-1, SEEK_SET) == (off_t)-1)
        err_exit("lseek error");
    write(fd, "", 1);

    Student *p = (Student *)mmap(NULL, sizeof(Student)*5,
                                 PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED)
        err_exit("mmap error");

    // 此时:操纵文件就可以如同操纵内存一样了
    char ch = 'a';
    for (int i = 0; i < 5; ++i)
    {
        memcpy((p+i)->name, &ch, 1);
        (p+i)->age = 20+i;
        ++ ch;
    }
    cout << "file initialized!" << endl;
    if (munmap(p, sizeof(Student)*5) == -1)
        err_exit("munmap error");
    cout << "process exit..." << endl;

    return 0;
}
/**示例2: 读文件映射
**/
int main(int argc,char **argv)
{
    if (argc != 2)
        err_quit("usage: ./main <file-name>");

    //以只读方式打开
    int fd = open(argv[1], O_RDONLY);
    if (fd == -1)
        err_exit("file open error");

    void *mmap(void *addr, size_t length, int prot, int flags,
               int fd, off_t offset);
    Student *p = (Student *)mmap(NULL, sizeof(Student)*5,
                                 PROT_READ, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED)
        err_exit("mmap error");

    // 从内存中读取数据(其实是从文件中读取)
    for (int i = 0; i < 5; ++i)
    {
        cout << "name: " << (p+i)->name << ", age: " << (p+i)->age << endl;
    }
    if (munmap(p, sizeof(Student)*5) == -1)
        err_exit("munmap error");
    cout << "process exit..." << endl;

    return 0;
}

map注意点:

    1. 内存映射不能(也不可能)改变文件的大小;

    2. 可用于进程间通信的有效地址空间不完全受限于映射文件的大小, 而应该以内存页面的大小为准(见下面测试);

    3. 文件一旦被映射之后, 所有对映射区域的访问实际上是对内存区域的访问; 映射区域内容写会文件时, 所写内容不能超过文件的大小.

/** 测试: 注意点2
文件以40K的内容进行创建, 而以120K的内容进行写回
**/
//程序1: 写文件映射
int main(int argc,char **argv)
{
    if (argc != 2)
        err_quit("usage: ./main <file-name>");

    int fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);
    if (fd == -1)
        err_exit("file open error");

    // 注意: 此处我们的文件其实只有40个字节
    if (lseek(fd, sizeof(Student)*5-1, SEEK_SET) == (off_t)-1)
        err_exit("lseek error");
    write(fd, "", 1);

    // 但是我们却是以120个字节的方式进行映射
    Student *p = (Student *)mmap(NULL, sizeof(Student)*15,
                                 PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED)
        err_exit("mmap error");

    // 以120个字节的方式进行写入
    char ch = 'a';
    for (int i = 0; i < 15; ++i)
    {
        memcpy((p+i)->name, &ch, 1);
        (p+i)->age = 20+i;
        ++ ch;
    }
    cout << "file initialized!" << endl;
    // 以120字节的方式卸载该内存区
    if (munmap(p, sizeof(Student)*15) == -1)
        err_exit("munmap error");

    // 注意: 要故意暂停一会儿, 以便让read程序读取该共享内存的内容
    sleep(20);
    cout << "process exit..." << endl;
}
//程序2: 读文件映射
int main(int argc,char **argv)
{
    if (argc != 2)
        err_quit("usage: ./main <file-name>");

    //以只读方式打开
    int fd = open(argv[1], O_RDONLY);
    if (fd == -1)
        err_exit("file open error");

    void *mmap(void *addr, size_t length, int prot, int flags,
               int fd, off_t offset);
    // 以120字节的方式映射
    Student *p = (Student *)mmap(NULL, sizeof(Student)*15,
                                 PROT_READ, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED)
        err_exit("mmap error");

    // 以120字节的方式读取
    for (int i = 0; i < 15; ++i)
    {
        cout << "name: " << (p+i)->name << ", age: " << (p+i)->age << endl;
    }
    if (munmap(p, sizeof(Student)*15) == -1)
        err_exit("munmap error");
    cout << "process exit..." << endl;
}

msync函数

int msync(void *addr, size_t length, int flags);

对映射的共享内存执行同步操作

参数:

    addr: 内存起始地址;

    length: 长度

    flags: 选项

flags

说明

MS_ASYNC

执行异步写

MS_SYNC

执行同步写, 直到内核将数据真正写入磁盘之后才返回

MS_INVALIDATE

使高速缓存的数据失效

返回值:

    成功: 返回0;

    失败: 返回-1;

目录
相关文章
|
3月前
|
监控 算法 Java
Java内存管理:垃圾收集器的工作原理与调优实践
在Java的世界里,内存管理是一块神秘的领域。它像是一位默默无闻的守护者,确保程序顺畅运行而不被无用对象所困扰。本文将带你一探究竟,了解垃圾收集器如何在后台无声地工作,以及如何通过调优来提升系统性能。让我们一起走进Java内存管理的迷宫,寻找提高应用性能的秘诀。
|
6天前
|
关系型数据库 MySQL Linux
Linux环境下MySQL数据库自动定时备份实践
数据库备份是确保数据安全的重要措施。在Linux环境下,实现MySQL数据库的自动定时备份可以通过多种方式完成。本文将介绍如何使用`cron`定时任务和`mysqldump`工具来实现MySQL数据库的每日自动备份。
19 3
|
7天前
|
存储 监控 Java
深入理解计算机内存管理:优化策略与实践
深入理解计算机内存管理:优化策略与实践
|
3月前
|
安全 Java 开发者
Java 内存模型解析与实践
在Java的世界中,理解内存模型对于编写高效、线程安全的代码至关重要。本文将深入探讨Java内存模型的核心概念,并通过实例分析其对并发编程的影响,旨在为读者提供一套实用的策略和思考方式来优化多线程应用的性能与安全性。
64 0
|
13天前
|
消息中间件 存储 Linux
|
25天前
|
监控 Linux 云计算
Linux操作系统在云计算环境中的实践与优化###
【10月更文挑战第16天】 本文探讨了Linux操作系统在云计算环境中的应用实践,重点分析了其在稳定性、安全性和高效性方面的优势。通过具体案例,阐述了Linux如何支持虚拟化技术、实现资源高效分配以及与其他开源技术的无缝集成。文章还提供了针对Linux系统在云计算中的优化建议,包括内核参数调整、文件系统选择和性能监控工具的应用,旨在帮助读者更好地理解和应用Linux于云计算场景。 ###
31 3
|
1月前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
29 0
Linux c/c++之IPC进程间通信
|
1月前
|
Ubuntu Linux
Linux实践|设置静态 IP 地址
Linux实践|设置静态 IP 地址
61 0
Linux实践|设置静态 IP 地址
|
1月前
|
SQL 安全 算法
ChatGPT高效提问—prompt实践(漏洞风险分析-重构建议-识别内存泄漏)
ChatGPT高效提问—prompt实践(漏洞风险分析-重构建议-识别内存泄漏)
|
1月前
|
程序员 编译器 数据处理
【C语言】深度解析:动态内存管理的机制与实践
【C语言】深度解析:动态内存管理的机制与实践