Распределитель C++ STL в общей памяти — Windows

Прежде чем я начну, я хотел бы прояснить две вещи:

  1. Я не могу использовать Boost IPC для этого проекта и не могу использовать ничего, кроме оригинальных библиотек C/C++.

  2. Я посмотрел здесь и здесь для ответов и до сих пор не смог получить ответ, который хотел.

Я пытаюсь сохранить vector в общей памяти. Я сопоставил страницу общей памяти и получил ее указатель. Я также создал сам vector в общей памяти, используя placement new и присвоив ему LPVOID вида карты:

hMapFile = CreateFileMapping(
    INVALID_HANDLE_VALUE,    // use paging file
    NULL,                    // default security
    PAGE_READWRITE,          // read/write access
    0,                       // maximum object size (high-order DWORD)
    BUF_SIZE,                // maximum object size (low-order DWORD)
    szName);                 // name of mapping object


if (hMapFile == NULL)
{
    _tprintf(TEXT("Could not create file mapping object (%d).\n"),
        GetLastError());
    return 1;
}

pBuf = MapViewOfFile(hMapFile,   // handle to map object
    FILE_MAP_ALL_ACCESS, // read/write permission
    0,
    0,
    BUF_SIZE);

if (pBuf == NULL)
{
    _tprintf(TEXT("Could not map view of file (%d).\n"),
        GetLastError());

    CloseHandle(hMapFile);

    return 1;
}

std::vector<int, shared_allocator<int>>* vec = new(pBuf) std::vector<int, shared_allocator<int>>;

Теперь, когда я пытаюсь получить доступ к памяти из второго процесса, я могу получить сам vector и даже распечатать его size(). Очевидно, я не могу получить доступ ни к одному из его данных, поскольку все они хранятся в куче первого процесса.

Из кода видно, что я использую свой собственный распределитель, который выглядит так:

template <class T>
struct shared_allocator
{
    typedef T value_type;
    shared_allocator() noexcept {} //default ctor not required by C++ Standard Library    

                                                                 // A converting copy constructor:    
    template<class U> shared_allocator(const shared_allocator<U>&) noexcept {}
    template<class U> bool operator==(const shared_allocator<U>&) const noexcept
    {
        return true;
    }
    template<class U> bool operator!=(const shared_allocator<U>&) const noexcept
    {
        return false;
    }
    T* allocate(const size_t n) const;
    void deallocate(T* const p, size_t) const noexcept;
};

template <class T>
T* shared_allocator<T>::allocate(const size_t n) const
{
    if (n == 0)
    {
        return nullptr;
    }
    if (n > static_cast<size_t>(-1) / sizeof(T))
    {
        throw std::bad_array_new_length();
    }
    //void* const pv = malloc(n * sizeof(T));
    //if (!pv) { throw std::bad_alloc(); }
    //return static_cast<T*>(pv);

    return (T*) ::operator new(n*sizeof(T));
}

template<class T>
void shared_allocator<T>::deallocate(T * const p, size_t) const noexcept
{
    // free(p);
    delete(p);
}

Этот код был выполнен в соответствии с Microsoft. документация.

Я заменил malloc и free на C++ new и delete. Приведенный выше код работает хорошо, и первый процесс может получить доступ ко всему.

Теперь я пытался изменить распределитель, чтобы он размещал вновь созданные объекты в общей памяти. Поэтому я изменил функцию allocate на:

template <class T>
T* shared_allocator<T>::allocate(const size_t n) const
{
    if (n == 0)
    {
        return nullptr;
    }
    if (n > static_cast<size_t>(-1) / sizeof(T))
    {
        throw std::bad_array_new_length();
    }
    //void* const pv = malloc(n * sizeof(T));
    //if (!pv) { throw std::bad_alloc(); }
    //return static_cast<T*>(pv);

    cnt += n*sizeof(T);
    void* p = (void*) (static_cast<char*>(pBuf) + cnt);

    return (T*) ::operator new(n*sizeof(T), p);
}

cnt — это глобальная переменная, которая начинается с 0.

Это (очевидно) не работает. Он вылетает с некоторыми исключениями нарушения памяти и всем остальным. Я попытался отследить изменение cnt, и в первый раз он увеличился на 16, а затем на 4 с.

vector не заполняется правильными данными, так как каждый раз _MyFirst() внутри vector заменяется адресом в разделяемой памяти.

Я знаю, что делаю это неправильно, но я смотрел во многих местах и, кажется, не могу понять, как это сделать правильно. Как изменить allocator для правильного размещения в разделяемой памяти? Если он один не может, то должен ли я воссоздать свой собственный vector?

Я следил за этой статьей, и она показывает, что Мне нужно только написать allocator, чтобы это заработало.


person Everyone    schedule 27.10.2017    source источник
comment
разве это не должно быть (char*)pBuf + sizeof(vector<int, shared_allocator<int> >) + cnt? Поскольку вы построили вектор на месте в начале pBuf?   -  person PeterT    schedule 27.10.2017
comment
@PeterT Совершенно верно. Я изменил его, по крайней мере, теперь мое первое введенное значение правильное (раньше я использовал push_back(0) и получал случайное значение как vec[0]). . В любом случае, vec[0] по-прежнему хранится в куче процесса, а не в разделяемой памяти. Мой второй процесс все еще не может видеть vec[0].   -  person Everyone    schedule 27.10.2017
comment
если вы не хотите переписывать указатели или использовать какой-либо тип оболочки относительного указателя, вам придется использовать MapViewOfFileEx с одним и тем же базовым адресом в обоих процессах. Не уверен, что это возможно в Windows, никогда не пробовал. Но теоретически использование одного и того же виртуального адреса в двух разных процессах должно быть в порядке.   -  person PeterT    schedule 27.10.2017
comment
@PeterT Указание базового адреса не очень практично и может сломаться в любой момент. Я хотел бы надежное решение. Как вы упомянули, мне нужно работать с указателями. Мой вопрос в том, с чего мне начать и как мне их изменить?   -  person Everyone    schedule 27.10.2017
comment
Ваша общая память занимает один и тот же виртуальный адрес в обоих процессах?   -  person Richard Critten    schedule 27.10.2017
comment
@RichardCritten Не обязательно.   -  person Everyone    schedule 27.10.2017
comment
Тогда указатель (для выделенной памяти), хранящийся в векторе, будет указывать на неверный виртуальный адрес в одном из процессов.   -  person Richard Critten    schedule 27.10.2017
comment
Я так понимаю, как я могу сделать это относительно?   -  person Everyone    schedule 27.10.2017
comment
@Everyone Я не думаю, что это действительно возможно с std::vector   -  person PeterT    schedule 27.10.2017
comment
Это часть моего вопроса, когда я спросил, нужно ли мне воссоздавать свой собственный вектор.   -  person Everyone    schedule 27.10.2017
comment
Попробуйте использовать MapViewOfFile2, это позволит вам выбрать базовый адрес (хотя как вы выбираете действительный базовый адрес, я понятия не имею).   -  person Richard Critten    schedule 27.10.2017
comment
@RichardCritten Большинство операционных систем рандомизируют адресное пространство гостевых программ в целях безопасности. Даже если бы я смог правильно выбрать один из них и хорошо запустить программы, позже в будущем у меня могут возникнуть проблемы с этим.   -  person Everyone    schedule 27.10.2017
comment
@ Всем удалось найти решение?   -  person Norbert Boros    schedule 12.12.2019
comment
@NorbertBoros Боюсь, простого выхода нет. Вам нужно повторно реализовать распределитель особым образом, чтобы он размещался в общей памяти. Это непростая задача. Лично я сделал гораздо более простую структуру данных и вместо этого сохранил ее.   -  person Everyone    schedule 12.12.2019
comment
@Все хорошо.... :(. Можете ли вы поделиться дизайном конструкции?   -  person Norbert Boros    schedule 12.12.2019
comment
@NorbertBoros это не очень связано с вопросом. По сути, это массив с элементами фиксированного размера и фиксированным количеством записей.   -  person Everyone    schedule 13.12.2019
comment
@Всем спасибо, я так же сделал.   -  person Norbert Boros    schedule 13.12.2019