c++ - 为什么我可以使用 POSIX 创建一个比安装在/dev/shm 上的大小更大的共享内存?

标签 c++ c posix ipc shared-memory

我正在尝试在 Ubuntu 16.04 中使用共享内存 IPC 处理错误。 首先,我使用 df -h 检查/dev/shm 中的可用内存,有 500M 可用内存,所以我快速编写了一些代码,以检查如果我尝试创建一个大于安装大小的共享内存会发生什么。代码如下(修改了几次所以我知道不是很整洁):

#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <cstring>
#include <stdint.h>
#include <stddef.h>
#include <cerrno>

//static const size_t size = 4000000L;
static const size_t size = 701000000L;
//static const size_t size = 999999999L;

extern int errno;

static int open(const char* name, int oflag, mode_t mode)
{
   int shm = -1;

   /* Create/Open a shared memory zone */
    shm = shm_open(name, oflag, mode);
    if(shm == -1)
    {/* Print error */
        std::cout << "!!!Error getting file descriptor while opening!!!" << std::endl;
        std::cout << "ERROR:"<< strerror(errno) << std::endl;
    }
   return shm;
}

static void write_shm(void *addr, size_t size)
{
    size_t i = 0;
    uint32_t *shm_index = (uint32_t *)addr;

    /* 4 bytes to be written in memory */
    const char *test = "DEAD";

    /* Maximum allowed memory address*/
    ptrdiff_t max = (size+(ptrdiff_t)addr);

    for (i = 0; (ptrdiff_t)(shm_index + i) < max; i++)
    {
        std:memcpy(&shm_index[i], (uint32_t*)test, sizeof(uint32_t));
    }
}
static int adjust (int fd, size_t size)
{
     std::cout<<__func__<<": The size of the shared memory is: "<<size<< std::endl;
     int result = ftruncate(fd, size);
     std::cout<<__func__<< "ftruncate return: " <<result<< std::endl;
     errno = 0;
     std::cout << "errno: "<< std::strerror(errno) <<std::endl;
     if (result)
     {/* Print error */;
        std::cout << "FUNCION!!!Error in ftruncate!!!" << std::endl;
     }
     return result;
}

int main()
{
    const char *name = "vardb";
    int fd = -1;
    int oflag = O_CREAT | O_EXCL | O_RDWR;
    mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; // POSIX 1003.1 (Realtime Extensions)
    size_t sizeToUse = (size/sizeof(uint32_t)* sizeof(uint32_t));

    /* Let's try to get a file descriptor related to the shared memory*/
    fd = open(name, oflag, mode);
    if (fd == -1)
        return fd;

    /* Adjust the size of the shared memory to the expected one */
    int result = adjust(fd, sizeToUse);
    if (result)
        return -1;

    int prot = PROT_READ | PROT_WRITE;
    int flags = MAP_SHARED;

    /* Map the memory */
    void *addr = mmap(NULL, size, prot, flags, fd, 0);
    std::cout<<__func__<< "mmap return: " <<*(int *)addr<< std::endl;
    std::cout<<__func__<< "mmap mapped to this address: " <<addr<< std::endl;
    errno = 0;
    std::cout << "mmap errno: "<< std::strerror(errno) <<std::endl;

    struct stat fileStat;
    if(fstat(fd, &fileStat) < 0)    
        return 1;

    std::cout<<__func__<< "File Size: " <<fileStat.st_size<<" bytes"<<std::endl;

    /* Write all the shared memory previously reserved for the test */
    write_shm(addr, sizeToUse);


    /* Release the memory */
    munmap(addr, size);

    return 0;
}

我不会取消链接共享内存以对其进行十六进制转储,因此这需要在重新启动程序之前手动将其删除。

好吧,我要发布的是,当我创建一个比/dev/shm 安装大小更大的共享内存时,我没有收到任何错误...显然,当我尝试写出可用内存范围,但我需要控制共享内存的创建...我无法理解系统如何让我创建这样的东西而不向我报告任何错误。

提前致谢。

最好的问候,

伊万。

最佳答案

简短(且不令人满意的答案):如果 /dev/shm 上的空间不足,您不能强制 shm_open 失败。 (您可以通过使用 setrlimit 修改 RLIMIT_FSIZE 显式设置进程文件大小限制来强制它失败,但这是一个全局设置,不适用于单个文件系统,因此几乎当然不是你想让它做什么。)

当 Posix 标准化共享内存时,考虑了各种实现选项,并尝试在不使接口(interface)复杂化的情况下为实现提供相当大的灵 active 。特别是,许多 Unix 实现已经具有将文件对象直接映射到进程内存的机制,对于基于内存的文件系统,这种组合非常适合共享内存的实现:

Simple shared memory is clearly a special case of the more general file mapping capability. In addition, there is relatively widespread agreement and implementation of the file mapping interface. In these systems, many different types of objects can be mapped (for example, files, memory, devices, and so on) using the same mapping interfaces. This approach both minimizes interface proliferation and maximizes the generality of programs using the mapping interfaces. (from Posix rationale: Mapped file functions)

特别是,“……上述要求不排除:[t]使用实际文件系统上的实际文件实现可共享内存对象。” (Posix rationale: Shared memory objects)。虽然我不相信 Linux 库会这样做,但 Posix 甚至允许将 shm_open() 实现为包装普通 open() 调用的宏;对于简单地将共享内存映射到文件系统的实现(如 Linux),不需要对 ftruncate() 进行特殊处理。系统界面。

重要的是要强调 ftruncate() 调用的一个方面(强调已添加):

If the file size is increased, the extended area shall appear as if it were zero-filled.

许多文件系统允许“稀疏文件”。在稀疏文件中,完全由零填充的文件 block 根本不会映射到物理存储;如果应用程序读取其中一个 block ,它会收到一页零。如果一个 block 被修改并提交到磁盘,那么——并且只有在那时——文件系统才会为这个 block 分配存储空间。 [注1]

零填充 block 的延迟分配意味着在 ftruncate() 扩展文件的情况下,它只需要更新文件的元数据,从而可以非常快速地返回。除非所需的大小超过进程的文件大小限制(或者在文件系统没有使用足够大的整数类型作为文件大小的情况下的文件系统限制),ftruncate() 不会产生错误.当无法分配物理存储(或专用内存缓冲区,在内存映射文件的情况下)时,将发生错误。

这完全符合 Linux 乐观的内存分配方法:mmap 总是成功(只要地址空间可用)并且只有在实际使用内存时才会注意到错误。 (但这种实现共享内存的特殊方法并不限于那些使用乐观内存分配的方法。)


注意事项

  1. 我曾经通过在软盘上存储一个 2GB 的文件来演示这一点,但我想今天的许多读者甚至都不知道软盘是什么,更不用说它的实际容量了。

关于c++ - 为什么我可以使用 POSIX 创建一个比安装在/dev/shm 上的大小更大的共享内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50642571/

相关文章:

c - POSIX 中的消息队列

c++ - 使用 std::vector 和 .at() 检查二维数组边界

c++ - 注册表配置单元问题

c - 在C程序中使用cp命令获取要复制的文件列表

c - 如何根据数组的大小重新分配?

c - 如何正确检查 off_t 值在转换为 C 中的 size_t 时不会溢出?

c - 将数据提供给需要文件名的 C API

c++ - 如何在 C++ 中定义一个制作精良的消息类

java - 如何使用 C++ 打开 Java 程序

c++ - C++ 中的 FPS 相机可能使用 glm :lookat