Linux编程之共享内存

Linux 支持两种方式的共享内存:System V 和 POSIX 共享内存。

1. POSIX 共享内存

1.1 POSIX 共享内存的由来

System V 共享内存和共享文件映射的不足:

  • System V 共享内存模型使用的是键和标识符,这与标准的 UNIX I/O 模型使用文件名和描述符的做法是不一致的。这种差异意味着使用 System V 共享内存需要一整套全新的系统调用和命令。
  • 使用一个共享文件映射来进行 IPC 要求创建一个磁盘文件,即使无需对共享区域进行持久存储也需要这样做。除了因需要创建文件所带来的不便之处,这种技术还会带来一些文件 I/O 开销。

基于以上不足,POSIX.1b 定义了一组新的共享内存 API: POSIX 共享内存。

1.2 POSIX 共享内存概述

POSIX 共享内存能够让无关进程共享一个映射区域而无需创建一个相应的映射文件。Linux 从内核 2.4 起开始支持 POSIX 共享内存。

很多类 UNIX 实现采用了文件系统来标识共享内存对象。一些 UNIX 实现将共享内存对象名创建为标准文件系统上一个特殊位置处的文件。Linux 使用挂载于 /dev/shm 目录下的专用 tmpfs 文件系统. 这个文件系统具有内核持久性--它所包含的共享内存对象会一直持久,即使当前不存在任何进程打开它,但这些对象会在系统关闭之后丢失.

系统上 POSIX 共享内存区域占据的内存总量受限于底层的 tmpfs 文件系统的大小。这个文件系统通常会在启动时使用默认大小(如 256 MB)进行挂载。如果有必要的话,超级用户能够通过使用命令 mount -o remount,size=

使用 POSIX 共享内存对象的流程:

  • 使用 shm_open() 函数打开一个与指定的名字对应的对象。shm_open() 函数与 open() 系统调用类似,它会创建一个新共享对象或打开一个既有对象。作为函数结果,shm_open() 会返回一个引用该对象的文件描述符。
  • 将上一步中获得的文件描述符传入 mmap() 调用并在其 flags 参数中指定 MAP_SHARED。这会将共享内存对象映射进进程的虚拟地址空间。与 mmap() 的其他用法一样,一旦映射了对象之后就能够关闭该文件描述符而不会影响到这个映射。然后,有可能需要将这个文件描述符保持在打开状态以便后续的 fstat() 和 ftruncate() 调用使用这个文件描述符。

1.3 创建共享内存对象

shm_open() 函数创建和打开一个新的共享内存对象或打开一个既有对象。传入 shm_open() 的参数与传入 open()的参数类似。

#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */

int shm_open(const chart *name, int oflag, mode_t mode);
        Returns file descriptor on success, or -1 on error.

Link with -lrt.

name 参数标识出了待创建或待打开的共享内存对象。oflag 参数是一个改变调用行为的位掩码。

oflag 参数的位值:

  • O_CREAT: 对象不存在时创建对象,一个新的共享内存对象最初长度为 0,对象的大小可以使用 ftruncate 来设置。
  • O_EXCL: 与 O_CREAT 互斥地创建对象,即如果 O_CREAT 已经指定了,并且指定名字的共享内存对象已经存在,则会返回错误。
  • O_RDONLY: 打开只读访问。
  • O_RDWR: 打开读写访问。
  • O_TRUNC: 将对象长度截断为零。

在一个新共享内存对象被创建时,其所有权和组所有权将根据调用 shm_open() 的进程的有效用户和组 ID 来设定,对象权限将会根据 mode 参数中设置的掩码值来设定。mode 参数能取的位值与文件上的权限位值是一样的。与 open() 系统调用一样,mode 中的权限掩码将会根据进程的 umask 来取值。与 open() 不同的是,在调用 shm_open() 时总是需要 mode 参数,在不创建新对象时需要将这个参数值指定为 0.

shm_open() 返回的文件描述符会设置 close-on-exec 标记,因此当程序执行了一个 exec() 时文件描述符会被自动关闭。

一个新共享内存对象被创建时其初始长度被会设置为 0。这意味着在创建完一个新共享内存对象之后通常在调用 mmap() 之前需要调用 ftruncate() 来设置对象的大小。在调用完 mmap() 之后可能还需要使用 ftuncate() 来根据需求扩大或收缩共享内存对象。

在扩展一个共享内存对象时,新增加的字节会自动被初始化为 0。

在任何时候都可以在 shm_open() 返回的文件描述符上使用 fstat() 以获取一个 stat 结构,该结构的字段会包含与这个共享内存对象相关的信息,包括其大小(st_size)、权限(st_mode)、所有者(st_uid)以及组(st_gid)。

使用 fchmod() 和 fchown() 能够分别修改共享内存对象的权限和所有权。

示例程序: pshm_create.c

该程序创建了一个大小通过命令参数指定的共享内存对象并将该对象映射进进程的虚拟地址空间。

#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/stat.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int flags, opt, fd;
    mode_t perms;
    size_t size;
    void *addr;

    /* Create shared memory object and set its size */
    fd = shm_open(argv[1], O_RDWR|O_CREAT, 0777);
    if (fd == -1)
    {
        printf("shm_open failed");
        exit(-1);
    }

    if (ftruncate(fd, 10000) == -1)
    {
        printf("ftruncate failed");
        exit(-1);
    }

    /* Map shared memory object */
    addr = mmap(NULL, 10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED)
    {
        printf("mmap failed");
        exit(-1);
    }

    exit(EXIT_SUCCESS);
}

如上程序创建一各 10000 字节的共享内存对象:

# gcc pshm_create.c -o pshm_create -lrt
# ./pshm_create demo_shm
# ls -l /dev/shm/demo_shm
-rwxr-xr-x 1 root root 10000 Jun 16 03:34 /dev/shm/demo_shm

1.4 使用共享内存对象

pshm_write.c

将数据复制进一个 POSIX 共享内存对象.

#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/stat.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int fd;
    size_t len;
    char *addr;

    /* Open existing object */
    fd = shm_open(argv[1], O_RDWR, 0);
    if (fd == -1)
    {
        printf("shm_open failed");
        exit(-1);
    }

    len = strlen(argv[2]);
    /* Resize object to hold string */
    if (ftruncate(fd, len) == -1)
    {
        printf("ftruncate failed");
        exit(-1);
    }

    addr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED)
    {
        printf("mmap failed");
        exit(-1);
    }

    if (close(fd) == -1)
    {
        printf("close failed");
        exit(-1);
    }

    printf("copying %ld bytes\n", (long)len);
    /* Copy string to shared memory */
    memcpy(addr, argv[2], len);
    exit(EXIT_SUCCESS);
}

向 1.3 创建的共享内存对象 demo_shm 写入数据:

# gcc pshm_write.c -o pshm_write -lrt
# ls -l /dev/shm/demo_shm
-rwxr-xr-x 1 root root 10000 Jun 16 03:34 /dev/shm/demo_shm
# ./pshm_write demo_shm ‘hello‘
copying 5 bytes
# ls -l /dev/shm/demo_shm
-rwxr-xr-x 1 root root 5 Jun 16 03:46 /dev/shm/demo_shm

pshm_read.c

从一个 POSIX 共享内存对象中复制数据。

#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/stat.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int fd;
    char *addr;
    struct stat sb;

    /* Open existing object */
    fd = shm_open(argv[1], O_RDONLY, 0);
    if (fd == -1)
    {
        printf("shm_open failed");
        exit(-1);
    }

    /* Use shared memory object size as length argument for mmap()
     * and as number of bytes to write() */

    if (fstat(fd, &sb) == -1)
    {
        printf("fstat failed");
        exit(-1);
    }

    addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED)
    {
        printf("mmap failed");
        exit(-1);
    }

    if (close(fd) == -1)
    {
        printf("close failed");
        exit(-1);
    }

    write(STDOUT_FILENO, addr, sb.st_size);
    printf("\n");
    exit(EXIT_SUCCESS);
}

显示刚才写到共享内存对象 demo_shm 中的字符串:

# gcc pshm_read.c -o pshm_read -lrt
# ./pshm_read demo_shm
hello

1.5 删除共享内存对象

POSIX 共享内存对象具有内核持久性,即它们会持续存在直到被显示删除或系统重启。当不再需要一个共享内存对象时就应该使用 shm_unlink() 删除它。

#include <sys/mman.h>

int shm_unlink(const char *name);
        Returns 0 on success, or -1 on error

Link with -lrt.

shm_unlink() 函数会删除通过 name 指定的共享内存对象。删除一个共享内存对象不会影响对象的既有映射(它会保持有效直到相应的进程调用 munmap() 或终止),但会阻止后续的 shm_open() 调用打开这个对象。一旦所有进程都接触映射这个对象,对象就会删除,其中的内容会丢失。

pshm_unlink.c

#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    if (shm_unlink(argv[1]) == -1)
    {
        printf("shm_unlink failed");
        exit(-1);
    }

    exit(EXIT_SUCCESS);
}

删除上面创建的共享内存对象 demo_shm:

# gcc pshm_unlink.c -o pshm_unlink -lrt
# ./pshm_unlink demo_shm

原文地址:https://www.cnblogs.com/jimodetiantang/p/9190172.html

时间: 2024-10-08 19:10:30

Linux编程之共享内存的相关文章

Linux环境编程之共享内存区(一):共享内存区简介

Spark生态圈,也就是BDAS(伯克利数据分析栈),是伯克利APMLab实验室精心打造的,力图在算法(Algorithms).机器(Machines).人(People)之间通过大规模集成,来展现大数据应用的一个平台,其核心引擎就是Spark,其计算基础是弹性分布式数据集,也就是RDD.通过Spark生态圈,AMPLab运用大数据.云计算.通信等各种资源,以及各种灵活的技术方案,对海量不透明的数据进行甄别并转化为有用的信息,以供人们更好的理解世界.Spark生态圈已经涉及到机器学习.数据挖掘.

Linux环境编程之共享内存区(二):Posix共享内存区

现在将共享内存区的概念扩展到将无亲缘关系进程间共享的内存区包括在内.Posix提供了两种在无亲缘关系进程间共享内存区的方法: 1.内存映射文件:由open函数打开,由mmap函数把得到的描述符映射到当前进程地址空间中的一个文件.(上一节就是这种技术) 2.共享内存区对象:由shm_open打开一个Posix名字(也许是在文件系统中的一个路径名),所返回的描述符由mmap函数映射到当前进程的地址空间.(本节内容) Posix共享内存区涉及以下两个步骤要求: 1.指定一个名字参数调用shm_open

Linux IPC之共享内存C 事例

Linux IPC之共享内存 标签: linuxrandomnull工作 2011-08-25 11:52 4123人阅读 评论(0) 收藏 举报  分类: Linux(3)  读书札记(3)  版权声明:本文为博主原创文章,未经博主允许不得转载. 简介 共享内存(shared memory)是最简单的Linux进程间通信方式之一.使用共享内存,不同进程可以对同一块内存进行读写.由于所有进程对共享内存的访问就和访问自己的内存空间一样,而不需要进行额外系统调用或内核操作,同时还避免了多余的内存拷贝

Linux进程间通信—使用共享内存

Linux进程间通信-使用共享内存 转自: https://blog.csdn.net/ljianhui/article/details/10253345 下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物理内存.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像

Linux环境编程之共享内存区(一):共享内存区简单介绍

共享内存区是可用IPC形式中最快的.一旦内存区映射到共享它的进程的地址空间,进程间数据的传递就不再涉及内核.然而往该共享内存区存放信息或从中取走信息的进程间通常须要某种形式的同步.不再涉及内核是指:进程不再通过运行不论什么进入内核的系统调用来彼此传递数据.内核必须建立同意各个进程共享该内存区的内存映射关系.然后一直管理内存区. 默认情况下通过fork派生的子进程并不与其父进程共享内存区. mmap函数把一个文件或一个Posix共享内存区对象映射到调用进程的地址空间.使用该函数的目的有: 1.使用

Linux进程间通信之共享内存

一,共享内存  内核管理一片物理内存,允许不同的进程同时映射,多个进程可以映射同一块内存,被多个进程同时映射的物理内存,即共享内存.  映射物理内存叫挂接,用完以后解除映射叫脱接. 1,共享内存的特点: 优点:是最快的IPC. 缺点:要编程者自己实现对共享内存互斥访问.如何实现? 2,编程模型:具体函数的用法可以用man手册查看(强力推荐) 进程A: writeshm.c     1) 获得key, ftok()     2) 使用key来创建一个共享内存 shmget()      3) 映射

Linux进程间通信——使用共享内存

下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物理内存.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样.而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程. 特别提醒:共

Linux进程间通信--mmap共享内存(一)

共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进程A可以即时看到进程B对共享内存中数据的更新,反之亦然.由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以. 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝.对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入

Linux进程间通信--mmap()共享内存(二)

内核怎样保证各个进程寻址到同一个共享内存区域的内存页面 1.page cache及swap cache中页面的区分:一个被访问文件的物理页面都驻留在page cache或swap cache中,一个页面的所有信息由struct page来描述.struct page中有一个域为指针mapping ,它指向一个struct address_space类型结构.page cache或swap cache中的所有页面就是根据address_space结构以及一个偏移量来区分的. 2.文件与addres