Ошибка сегментации при записи в разделяемую память

#c #segmentation-fault #shared-memory

#c #ошибка сегментации #разделяемая память #c

Вопрос:

Я сделал проект в Netbeans, и он там отлично работает. Но когда я пытаюсь скомпилировать с помощью makefile, я получаю ошибку сегментации. Почему я не могу получить доступ к общей памяти? Должен ли я использовать char *?

 #include <fstream>
#include <string>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

static std::string *shmem;  //shared memory

int main(int argc, char** argv)
{    
    //open file and copy content
    std::ifstream ifs("example.txt");
    std::string content( (std::istreambuf_iterator<char>(ifs) ),
                     (std::istreambuf_iterator<char>()    ) );
    size_t size = content.size();

    //create shared memory page and copy content
    shmem = static_cast<std::string*>(mmap(NULL, size, PROT_READ | PROT_WRITE, 
                MAP_SHARED | MAP_ANONYMOUS, -1, 0));
    *shmem = content;

    return 0;
}
  

Вот мой Makefile

 CC      = g  
CFLAGS  = -c -Wall -std=c  11 -pthread
LDFLAGS = -pthread
SOURCES = main.cpp
OBJECTS =$(SOURCES:.cpp=.o)
EXECUTABLE=MapReduce

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS) 
    $(CC) $(LDFLAGS) $(OBJECTS) -o $@

.cpp.o:
    $(CC) $(CFLAGS) $< -o $@
  

Ответ №1:

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

Если вы хотите сохранить строковый буфер в общей памяти, вам следует просто использовать символ * или посмотреть на библиотеки, такие как Boost .Межпроцесс. Библиотека является кроссплатформенной и значительно упростит размещение всех типов типов в разделяемой памяти, конечно, за счет использования Boost.

Ответ №2:

Первое правило таких проблем: всегда проверяйте возвращаемое значение системных вызовов. Второе правило таких проблем: запускайте в отладчике и учитесь использовать посмертный анализ.

В вашем конкретном случае произошел сбой mmap, но вы не смогли проверить его код возврата, что означает, что вы пытались разыменовать указатель -1 .

Я не уверен точно, почему произошел сбой mmap, но когда я действительно создаю example.txt , он перестает работать на моей машине. Мне кажется, что вам также не удалось проверить код возврата из кода, подготавливающего буфер для сопоставления.

Наконец, std::string работает не так, как вы думаете. Выделение памяти для нее с помощью mmap просто не будет правильным. std::string выделяет свою собственную память, и если вы хотите, чтобы она размещалась в явно распределенном пространстве, вам нужно посмотреть, предоставив ему пользовательский распределитель.

Отредактировано для добавления

Если вы настаиваете на выделении вашего std::string в mmap (в отличие от данных, которые должна содержать строка), тогда размер, который вы отправляете в mmap, должен sizeof(std::string) , а не string.length . Это все равно не было бы семантически корректным, так как вы бы вызывали std::string.operator= без предварительного создания объекта. Поэтому рабочая программа будет:

 std::string *func(const std::string amp;str)
{    
    //create shared memory page and copy content
    void *memory = mmap(NULL, sizeof(std::string), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
    if( memory==MAP_FAILED ) {
        throw std::bad_alloc();
    }

    std::string *allocated_string = new(memory) std::string();
    *allocated_string = str;

    return allocated_string;
}
  

Это правильно помещает инициализированное std::string в mmaped-память. Для его инициализации используется размещение new (вам нужно будет включить <new> ). Опять же, это, вероятно, не то, что вы хотите, поскольку std::string оно будет находиться в вашей mmaped памяти, но эта фактическая строка, скорее всего, нет.

Ответ №3:

Используйте размещение new для создания std::string в местоположении shmem

 new (shmem) std::string(content)
  

Но это, вероятно, все равно не будет работать для межпроцессного взаимодействия только из-за

  • макет std::string может отличаться из-за параметров компилятора (или типа компилятора)
  • внутренние поля std::string ссылаются на другую память, локальную для вашего процесса, как уже указал grepsedawk