Многопроцессорный С++ (11) с указателем связанного списка в качестве глобальной переменной

У меня есть классическая проблема, как указано здесь, здесь и здесь, а также здесь,

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

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

Возможно, я задаю слишком широкий вопрос, но лично я не продвинулся дальше ветвления и вставки элемента в конец списка. Таким образом, на самом деле у меня нет ни одной строчки кода, которая привела бы меня туда, куда я хочу. Я понятия не имею, как использовать shm() в этом сценарии.

Пожалуйста помоги.


person Sean    schedule 11.01.2014    source источник
comment
потоки будут совместно использовать адресное пространство, поэтому им не нужен shm() (убедитесь, что вы знаете, создаете ли вы новый процесс или поток), а остальное зависит от вашей конкретной ситуации - все ли они читают или пишут или и то, и другое? , так далее   -  person user3125280    schedule 11.01.2014
comment
Использование разделяемой памяти не всегда достаточно, особенно если указатели в вашем shm указывают на неразделяемую память. Однако похоже, что вы вообще не должны использовать процессы - скорее вы должны использовать потоки (pthreads или что-то еще, что поддерживает ваша среда) в одном и том же процессе. Одна из причин заключается в том, что общее глобальное пространство является общим только до тех пор, пока не произойдет запись... тогда это два отдельных буфера памяти.   -  person mah    schedule 11.01.2014
comment
Я использовал fork для ветвления, несущего входные данные use, затем на основе этих входных данных обновил globals , затем запустил внешний процесс, затем убил дочерний процесс, чтобы до того, как придет следующий пользовательский ввод, основной процесс знал, что именно происходит на последнем звонке. Я понимаю, что жестоким решением было бы выполнить обновление ДО разветвления, или, поскольку пользовательские вводы также копируются, пусть ребенок вызывает только внешнюю программу, в то время как основная обновляет переменные. Но могу ли я действительно сделать обновления в детях вместо брутального решения?   -  person Sean    schedule 11.01.2014
comment
Если потомок является потоком того же процесса, а не другого процесса, то да. Если это разные процессы, дочерний процесс начинается с копии глобального пространства, поэтому записи дочернего процесса не будут видны родительскому процессу. Поскольку потоки находятся в одном и том же процессе, у них не просто копия, у них точно такой же буфер. (Путаница в реализации: дочерний элемент форка сначала имеет доступ только для чтения к исходному глобальному пространству... при первой записи он получает копию, а затем доступ для чтения и записи. Это просто для эффективности во время выполнения, не влияет вы.) С помощью shm любой может обновиться, но использовать синхронизацию!   -  person mah    schedule 11.01.2014
comment
разве fork() на самом деле не создает новый процесс? Итак, я предполагаю, что после разветвления у меня будет два отдельных процесса... И, ну, мне бы очень понравился пример shm() с синхронизацией со связанными списками. также мне нужно передать каждый элемент списка в shm?   -  person Sean    schedule 11.01.2014
comment
Взгляните на boost::interprocess.   -  person Casey    schedule 11.01.2014


Ответы (2)


Вы можете попробовать это.. Я просто написал это с нуля.. Это кросс-платформенный, так что это всегда плюс. Распределители и пулы можно повторно использовать с чем угодно. Например, вы можете разместить любой stl-контейнер в стеке или где угодно.

SharedMemory.hpp:

#ifndef SHAREDMEMORY_HPP_INCLUDED
#define SHAREDMEMORY_HPP_INCLUDED

#if defined _WIN32 || defined _WIN64
    #include <windows.h>
#else
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <dlfcn.h>
    #include <fcntl.h>
    #include <unistd.h>
#endif

#include <string>
#include <cstdint>

class SharedMemory
{
    private:
        std::string name;
        std::size_t size;
        void* data;
        void* hFileMap;

    public:
        SharedMemory(std::string name, std::size_t size) : name(name), size(size), data(nullptr), hFileMap(nullptr) {};
        ~SharedMemory();

        bool Open();
        bool Create();

        std::size_t GetSize() const {return this->size;}
        void* GetPointer() const {return this->data;}
};

#endif // SHAREDMEMORY_HPP_INCLUDED

SharedMemory.cpp:

#include "SharedMemory.hpp"

SharedMemory::~SharedMemory()
{
    if (data)
    {
        #if defined _WIN32 || defined _WIN64
        UnmapViewOfFile(data);
        data = nullptr;

        if (hFileMap)
        {
            if (CloseHandle(hFileMap))
            {
                hFileMap = nullptr;
            }
        }

        #else

        if (data)
        {
            munmap(data, size);
            data = nullptr;
        }

        if (hFileMap)
        {
            if (!close(hFileMap))
            {
                hFileMap = nullptr;
            }
        }
        #endif
    }
}

bool SharedMemory::Open()
{
    #if defined _WIN32 || defined _WIN64
        if ((hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, name.c_str())) == nullptr)
        {
            return false;
        }

        if ((data = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == nullptr)
        {
            CloseHandle(hFileMap);
            return false;
        }
    #else

        if ((hFileMap = open(MapName.c_str(), O_RDWR | O_CREAT, 438)) == -1)
        {
            return false;
        }

        if ((data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
        {
            close(hFileMap);
            return false;
        }
    #endif
    return true;
}

bool SharedMemory::Create()
{
    #if defined _WIN32 || defined _WIN64
        if ((hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, size, name.c_str())) == nullptr)
        {
            return false;
        }

        if ((data = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == nullptr)
        {
            CloseHandle(hFileMap);
            return false;
        }

    #else

        if ((hFileMap = open(MapName.c_str(), O_RDWR | O_CREAT, 438)) == -1)
        {
            return false;
        }

        if ((data = mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
        {
            close(hFileMap);
            return false;
        }
    #endif
    return true;
}

Пулы.hpp:

#ifndef POOLS_HPP_INCLUDED
#define POOLS_HPP_INCLUDED

#include <stdexcept>
#include <cstdint>
#include "SharedMemory.hpp"

template<typename T>
class SharedPool
{
    private:
        T* data;
        SharedMemory* shm;
        std::size_t size;

    public:
        SharedPool(SharedMemory* shm) : data(reinterpret_cast<T*>(shm->GetPointer())), shm(shm), size(shm->GetSize()) {};

        template<typename U = T>
        void* allocate(std::size_t n, const void* hint = 0) {return &data[0];}

        template<typename U = T>
        void deallocate(U* ptr, std::size_t n) {}

        template<typename U = T>
        std::size_t max_size() const {return size;}
};

#endif // POOLS_HPP_INCLUDED

main.cpp (добавление значений в разделяемую память из первого процесса):

#include "SharedMemory.hpp"
#include "Allocators.hpp"
#include "Pools.hpp"

#include <vector>
#include <iostream>

int main()
{
    SharedMemory mem = SharedMemory("Local\\Test_Shared_Memory", 1024);
    if (!mem.Open() && !mem.Create())
    {
        throw std::runtime_error("Error Mapping Shared Memory!");
    }

    auto pool = PoolAllocator<int, SharedPool<int>>(SharedPool<int>(&mem));
    std::vector<int, decltype(pool)> v(pool);

    int* ptr = reinterpret_cast<int*>(mem.GetPointer());
    std::cout<<"Pushing 3 values to: "<<ptr<<"\n";

    v.push_back(100);
    v.push_back(200);
    v.push_back(700);

    std::cin.get();
}

main.cpp (Чтение значений из разделяемой памяти, второй процесс):

#include "SharedMemory.hpp"
#include "Allocators.hpp"
#include "Pools.hpp"

#include <vector>
#include <iostream>

int main()
{
    SharedMemory mem = SharedMemory("Local\\Test_Shared_Memory", 1024);
    if (!mem.Open() && !mem.Create())
    {
        throw std::runtime_error("Error Mapping Shared Memory!");
    }

    auto pool = PoolAllocator<int, SharedPool<int>>(SharedPool<int>(&mem));
    std::vector<int, decltype(pool)> v(pool);

    int* ptr = reinterpret_cast<int*>(mem.GetPointer());
    std::cout<<"Reading 3 values from: "<<ptr<<"\n";

    v.reserve(3);
    std::cout<<v[0]<<"\n";
    std::cout<<v[1]<<"\n";
    std::cout<<v[2]<<"\n";

    std::cin.get();
}
person Brandon    schedule 11.01.2014
comment
Успех, это сработало, спасибо. но я могу задать еще несколько вопросов в более поздние часы. - person Sean; 12.01.2014
comment
Я не против. Спрашивай. Я не очень много комментирую свой код, поэтому надеюсь, что вы его поняли. Если нет, не стесняйтесь спрашивать что угодно. Я или кто-то другой (кто увидит) поможет. - person Brandon; 12.01.2014
comment
на данный момент, да, я вижу, как это работает. но вы должны иметь в виду, что я метеоролог с математическим образованием (пытаюсь самостоятельно автоматизировать свои вещи, связанные с погодой - часто проще сделать это самостоятельно, чем объяснять, что именно нужно для удобства использования) - это может быть Дело в том, что я неправильно понял. Давайте посмотрим . - person Sean; 12.01.2014

Это трудная проблема.

Один из способов – использовать общую память, а при построении связанного списка назначить ему собственный распределитель, использовать общую память. Другой способ - реализовать свой собственный связанный список на основе общей памяти.

Вы также можете попробовать использовать boost - Boost interprocess, что, вероятно, является идеальным решением.

В частности, - interprocess с контейнерами

person user1708860    schedule 11.01.2014
comment
ОП заявил, что я понятия не имею, как использовать shm() в этом сценарии, поэтому просто предложить ему использовать общую память кажется недостаточным. - person mah; 11.01.2014