Почему я могу создать разделяемую память больше, чем размер, смонтированный в / dev / shm, с помощью POSIX? - PullRequest
0 голосов
/ 01 июня 2018

Я пытаюсь обрабатывать ошибки, используя разделяемую память IPC в Ubuntu 16.04.Во-первых, я проверил доступную память в / dev / shm, используя df -h, имея 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 ... Очевидно, я получаюОшибка шины, когда я пытаюсь записать из доступного диапазона памяти, но мне нужно контролировать создание общей памяти ... Я не могу понять, как система позволяла мне создавать что-то подобное, не сообщая мне ни о какой ошибке.

Заранее спасибо.

С уважением,

Иван.

1 Ответ

0 голосов
/ 01 июня 2018

Короткий (и неудовлетворительный ответ): вы не можете заставить shm_open потерпеть неудачу, если на /dev/shm недостаточно места.(Вы можете принудительно заставить его завершиться с ошибкой, явно установив предел размера файла процесса, используя setrlimit, чтобы изменить RLIMIT_FSIZE, но это глобальный параметр, неприменимый к отдельной файловой системе, так что это почти наверняка не то, что вы хотите.)

Когда Posix стандартизировал разделяемую память, были рассмотрены различные варианты реализации и была предпринята попытка обеспечить значительную гибкость реализации, если это не усложняет интерфейс.В частности, во многих реализациях Unix уже есть механизмы для сопоставления файловых объектов непосредственно с памятью процесса, а для файловых систем на основе памяти - комбинация, идеально подходящая для реализации разделяемой памяти:

Простая разделяемая память явноособый случай более общей возможности сопоставления файлов.Кроме того, существует довольно распространенное соглашение и реализация интерфейса сопоставления файлов.В этих системах много разных типов объектов могут отображаться (например, файлы, память, устройства и т. Д.) С использованием одних и тех же интерфейсов сопоставления.Такой подход минимизирует распространение интерфейса и максимизирует универсальность программ, использующих интерфейсы отображения. (из Обоснование Posix: функции сопоставленного файла )

В частности, «… вышеприведенные требования не исключают: [t] объект разделяемой памяти изреализуется с использованием реальных файлов в реальной файловой системе. " ( Обоснование Posix: объекты общей памяти ) .Хотя я не верю, что библиотека Linux делает это, Posix даже позволяет реализовывать shm_open() как макрос, заключающий в себе обычный вызов open();в случае реализаций (таких как Linux), которые просто отображают общую память в файловую систему, ничто не требует специальной обработки системного интерфейса ftruncate().

Важно выделить один аспектftruncate() вызов (выделение добавлено):

Если размер файла увеличивается, расширенная область должна выглядеть так, как если бы была заполнена нулями.

Многие файловые системы допускают "разреженные файлы".В разреженном файле файловые блоки, полностью заполненные нулями, просто не отображаются на физическое хранилище;если приложение читает один из этих блоков, оно получает страницу с нулями.Если блок модифицируется и фиксируется на диске, то и только тогда файловая система выделяет хранилище для блока.[Примечание 1]

Ленивое выделение заполненных нулями блоков означает, что в случае, когда ftruncate() расширяет файл, ему нужно только обновить метаданные файла, что позволяет ему очень быстро возвращаться.Если требуемый размер не превышает ограничение размера файла процесса (или ограничение файловой системы в случае файловых систем, которые не используют достаточно большие целочисленные типы для размеров файлов), ошибка ftruncate() не будет выдана.Ошибка возникает, когда становится невозможно выделить физическое хранилище (или выделенный буфер памяти, в случае файла с отображением в памяти).

Это полностью соответствует оптимистическому подходу Linux к распределению памяти: mmap всегда завершается успешно (пока доступно адресное пространство), а ошибка отмечается только при фактическом использовании памяти.(Но этот конкретный подход к реализации совместно используемой памяти не ограничивается теми, которые используют оптимистичное распределение памяти.)


Примечания

  1. Я использовал это для демонстрации, сохраняя файл 2 ГБ вдискета, но я полагаю, что многие читатели сегодня даже не будут знать, что такое дискета, тем более, какова была ее фактическая емкость.
...