2016-12-10 14 views
1

Я хотел бы зарезервировать область памяти, а затем картографировать файлы в зарезервированную память. Между сопоставлением файлов могут возникать большие промежутки времени, в течение которых другие функции могут выделять память из кучи. После сопоставления файл может не отображаться и сопоставляться с новой ячейкой памяти.Как зарезервировать память на Windows и более поздние файлы карты?

В Linux это будет что-то вроде:

#include <iostream> 
#include <sys/mman.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <cerrno> 

int main(){ 
    void *memory = mmap(nullptr, getpagesize() * 2, 
         PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0); // reserve memory 
    int handle1 = ::open("1", O_CREAT | O_RDWR, S_IRWXU); // open file1 
    int handle2 = ::open("2", O_CREAT | O_RDWR, S_IRWXU); // open file2 
    void *data = mmap(memory, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED |MAP_FIXED, handle1, 0); // map first file into reserved memory 
    void *data2 = mmap(static_cast<char *>(memory) + getpagesize(), getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, handle2, 0); // map second file into reserved memory 
} 

В Windows, однако я не могу найти правильный способ сделать это. Кто-нибудь знает, как это делается?

Редактировать: Поскольку моя цель, похоже, не так проста для понимания. Еще раз упрощен:

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

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

Проблема с решением с разреженными файлами и расширением существующего файла: Программа должна иметь возможность удалять файлы, если они больше не нужны. Поэтому важно, чтобы отображения всегда были новыми файлами.

Вы можете подумать об этом скорее как распределитель. Требуется память с отображением памяти. Библиотека отображает блок памяти и возвращает указатель на субблок. Память больше не нужна, она возвращается в распределитель. Если полное сопоставление больше не требуется, связанный файл будет удален (данные не должны быть записаны путем сопоставления, если он больше не нужен).

+0

google "отображенный файл с памятью окон" –

+0

Его не касается сопоставления файла, это просто. Его о сопоставлении файла в память, который ранее был зарезервирован, поэтому я могу сопоставить разные файлы в смежную память. – johnST

+0

@johnST - на окнах это невозможно - если вы попробуете файл карты в зарезервированную память, вы получили сообщение об ошибке 'STATUS_CONFLICTING_ADDRESSES' – RbMm

ответ

2

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

В Windows, обработка возможного многопоточного состояния гонки, вы можете сделать что-то вроде:

while(1) { 
    char *memory = VirtualAlloc(NULL, page_size * 2, MEM_RESERVE, 0); 
    VirtualFree(memory); 
    if (MapViewOfFileEx(handle1, FILE_MAP_WRITE, 0, 0, page_size, memory) == NULL 
     && GetLastError() == ERROR_INVALID_ADDRESS) { 
     continue; 
    } 
    if (MapViewOfFileEx(handle2, FILE_MAP_WRITE, 0, 0, page_size, memory + page_size) == NULL 
     && GetLastError() == ERROR_INVALID_ADDRESS) { 
     UnMapViewOfFile(memory); 
     continue; 
    } 
    break; 
} 
+0

Это всего лишь минимальный пример. Файлы не будут отображаться сразу после друг друга. В промежутках между ними существуют большие промежутки времени, в течение которых другие функции могут выделять память из кучи. – johnST

+0

@johnST. Здесь нет принципиальной проблемы, вам просто нужно выбрать другой адрес, когда обнаружите конфликт, но я предполагаю, что есть другие детали, которые вы оставляете out –

+0

после сопоставления файла не может быть отменен – johnST

1

моего решения используется недокументированных апите

NTSYSAPI NTSTATUS NTAPI ZwExtendSection (HANDLE SectionHaqndle, PLARGE_INTEGER SectionSize); 

без аналоговых win32 для этой функции, но это является ключевым моментом решения. также мы должны использовать ZwMapViewOfSection но не MapViewOfFileEx (win32 оболочки над ZwMapViewOfSection), потому что MapViewOfFileEx имеют меньше параметров, чем ZwMapViewOfSection - мы не можем установить ULONG AllocationType к MEM_RESERVE - но это тоже ключевой момент. для других задач мы можем использовать аналог win32, но для единообразия и стиля я буду использовать NT api.

Конечно, многие просто говорят, что это недокументированные, неподдерживаемые и т. Д. Используют ntdll api direct - но на самом деле это работает, и здесь я не рассматриваю решение, основанное только на win32. поэтому кто хочет, может использовать, кто не хочет, не может использовать.как

идея - мы просто оставляем необходимую большую область с вызовом ZwMapViewOfSection (это невозможно сделать, MapViewOfFileEx), а затем, когда необходимо, мы можем, насколько этот регион по вызову ZwExtendSection

раствора протестирована и работал ,

class SECTION_EX 
{ 
    LARGE_INTEGER _CurrentSize, _MaximumSize; 
    HANDLE _hSection; 
    PVOID _BaseAdress; 

public: 
    NTSTATUS Create(POBJECT_ATTRIBUTES poa, SIZE_T InitialSize, SIZE_T MaximumSize); 
    NTSTATUS Extend(SIZE_T NewSize); 

    SECTION_EX() 
    { 
     _BaseAdress = 0; 
     _hSection = 0; 
    } 

    ~SECTION_EX() 
    { 
     if (_hSection) 
     { 
      if (_BaseAdress) ZwUnmapViewOfSection(NtCurrentProcess(), _BaseAdress); 
      ZwClose(_hSection); 
     } 
    } 
}; 

NTSTATUS SECTION_EX::Extend(SIZE_T NewSize) 
{ 
    LARGE_INTEGER Size; 
    Size.QuadPart = NewSize; 

    if (Size.QuadPart <= _CurrentSize.QuadPart) 
    { 
     return STATUS_SUCCESS; 
    } 

    if (Size.QuadPart > _MaximumSize.QuadPart) 
    { 
     return STATUS_SECTION_TOO_BIG; 
    } 

    NTSTATUS status = ZwExtendSection(_hSection, &Size); 

    if (0 <= status) 
    { 
     _CurrentSize = Size; 
    } 

    return status; 
} 

NTSTATUS SECTION_EX::Create(POBJECT_ATTRIBUTES poa, SIZE_T InitialSize, SIZE_T MaximumSize) 
{ 
    HANDLE hFile; 
    IO_STATUS_BLOCK iosb; 

    NTSTATUS status = ZwCreateFile(&hFile, FILE_GENERIC_READ|FILE_GENERIC_WRITE, poa, 
     &iosb, 0, 0, FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_ALERT, 0, 0); 

    if (0 <= status) 
    { 
     _MaximumSize.QuadPart = MaximumSize; 

     LARGE_INTEGER Size, *pSize = &Size; 
     Size.QuadPart = InitialSize; 

     if (iosb.Information == FILE_OPENED) 
     { 
      FILE_STANDARD_INFORMATION fsi; 

      if (0 <= (status = ZwQueryInformationFile(hFile, &iosb, &fsi, sizeof(fsi), FileStandardInformation))) 
      { 
       if (fsi.EndOfFile.QuadPart) 
       { 
        pSize = 0;// in case file already exist with not zero size - use it 
       } 
      } 
     } 

     if (0 <= status) 
     { 
      status = ZwCreateSection(&_hSection, SECTION_ALL_ACCESS, 0, pSize, 
       PAGE_READWRITE, SEC_COMMIT, hFile); 
     } 

     ZwClose(hFile); 

     if (0 <= status) 
     { 
      SECTION_BASIC_INFORMATION sbi; 

      if (0 <= ZwQuerySection(_hSection, SectionBasicInformation, &sbi, sizeof(sbi), 0)) 
      { 
       _CurrentSize = sbi.Size;// real file size in bytes, without align 

       // !!! use MEM_RESERVE !!! 
       // MaximumSize - will be reserved, but not all commited 

       status = ZwMapViewOfSection(_hSection, NtCurrentProcess(), &_BaseAdress, 0, 
        0, 0, &MaximumSize, ViewUnmap, MEM_RESERVE, PAGE_READWRITE); 
      } 
     } 
    } 

    return status; 
} 

void demoS() 
{ 
    SECTION_EX se; 
    STATIC_OBJECT_ATTRIBUTES(oa, "\\??\\c:\\***"); 
    // reserve 256Mb,but initially commit only 32kb or file size 
    if (0 <= se.Create(&oa, 0x8000, 0x10000000)) 
    { 
     se.Extend(0x18000); 
     se.Extend(0x1e245); 
     se.Extend(0x74100); 
    } 
} 

обновление: я обнаружил, что начать с победы 8.1 мы можем зарезервировать область памяти с секцией использованными также недокументированныеFILE_MAP_RESERVE - так нужно называть

_BaseAdress = MapViewOfFileEx(_hSection, FILE_MAP_ALL_ACCESS|FILE_MAP_RESERVE, 0, 0, MaximumSize, 0); 

, но в Windows 7, Vista и XP - это не будет работать. однако ZwMapViewOfSection работал с флагом MEM_RESERVE даже в XP.

так обычная ситуация - часто win32 оболочка имеет меньше функционального сравнения соответствовала функции Nt */Zw *.

и до сих пор ни какого-либо аналог win32/оболочка для ZwExtendSection (этого вызова не распространяется как файл и вид)

+0

Это только расширяет существующее отображение, не так ли? Я считаю, что OP хочет создать новое сопоставление - другой файл - рядом с существующим. –

+0

@HarryJohnston - на самом деле вызов ZwExtendSection распространяется как на файл, так и на отображение в одном вызове. и это действительно сработало! я уже использую его - нужна резервная память для (потенциальной) огромной базы данных и добавляет к ней записи - так что резерв (но не карта) большой области памяти. при добавлении новой записи требуется расширение - я расширяю раздел - просмотр и файл с поправкой – RbMm

+1

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

1

Правильное решение заключается в rearchitecture, чтобы исключить требование в отношении соседних отображений.

В зависимости от ваших конкретных потребностей одним из подходов будет использование одного разреженного файла (as described here), первоначальная длина которого равна количеству адресного пространства, которое вы хотите зарезервировать. Поскольку файл разрежен, только блоки, которые фактически используются, занимают место на диске.

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


One (не совсем правильный!) Альтернативы можно было бы подключить функцию VirtualAlloc, так что вы можете сделать это заблокировать по мере необходимости. Это позволит вам эффективно управлять вашим зарезервированным диапазоном памяти - вы можете освободить его, наметить его часть, а затем повторно зарезервировать остальную часть, аналогичную ответу Росс Ридж, без какой-либо озабоченности, что другой поток будет выделять память пока вы это делаете.

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

NB: Я предполагаю, что здесь, что использование По какой-то причине один файл неприемлем. Если один файл будет работать, вы должны использовать разреженный файл, как было предложено выше. Если по какой-то причине один файл будет работать, но использование разреженного файла не является вариантом, я бы порекомендовал подход RbMm к этому - ни одно из решений хорошее, но я думаю, что это немного более рискованно. (Это, безусловно, сложнее.)

+0

только честность - вы считаете, что все это лучше (даже «подключить VirtualAlloc» и т. д.), чем использовать собственное решение, которое требует использования ntdll api ? или вы не верите, что мой код действительно работал по мере необходимости? я действительно хочу понять и интересно. извините за запрос – RbMm

+0

@RbMm: ваше решение, если я правильно понимаю, зависит от предположения, что использование одного файла (а не нескольких файлов) будет приемлемым. В этом случае, однако, вместо этого можно использовать разреженный файл, избегая необходимости использовать собственный API. Я предлагаю только перехват в случае, если использование одного файла неприемлемо. Я переписал, чтобы уточнить это. (У меня нет абсолютно никаких сомнений в том, что ваше решение будет работать, но я считаю, что использование разреженного файла было бы более безопасным и столь же эффективным.) –

+0

да, это решение для одного файла (OP несколько раз меняет это в вопросе - одно/несколько). о разреженном файле - я проверю это на следующий день (теперь поздно в моем часовом поясе) - вы уверены, что когда мы скажем, что 256 МБ разреженной файловой памяти будет действительно зарезервировано, но не будет совершено? (однако даже если они совершены - физическая память не будет связана с этим, до первого доступа). интересным будет проверка - я никогда не буду отображать разреженный файл в памяти :) – RbMm