Действительно ли возможно не разбить на страницы всю память в Windows?

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

Существует ли путь, или с помощью Windows API или через вызов ядра, чтобы заставить Windows не разбивать на страницы все (или как можно больше) память? Возможно, путем продвижения через список выполнения процессов и заставляют диспетчера памяти не разбивать на страницы память каждого процесса?

11
задан Kerrek SB 30 May 2010 в 17:31
поделиться

3 ответа

Ну, это несложно реализовать самостоятельно. Используйте VirtualQueryEx() для обнаружения виртуальных адресов, используемых процессом, ReadProcessMemory() для принудительной перезагрузки страниц.

Вряд ли это вообще что-то изменит, просто ваша программа будет делать свою работу бесконечно долго. Общей диагностикой медленной перезагрузки страниц является фрагментированный файл подкачки. Часто встречается, например, в Windows XP, когда диск долгое время не подвергался дефрагментации и ему позволялось часто заполняться почти до отказа. Утилита PageDefrag от SysInternals может помочь решить эту проблему.

8
ответ дан 3 December 2019 в 07:36
поделиться

Обновление 3: Я выложил свою полную программу на github.


Хорошо, основываясь на ответах, вот наивное предложение для инструмента, который пытается вернуть все приложения в физическую память:

  1. Выделите небольшой кусок памяти X, может быть 4MB. (Он должен быть нестраничным?)
  2. Перебираем все процессы:
    • Для каждого процесса скопируйте фрагменты его памяти в X. (Возможно, сначала приостановить процесс?)

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

Возможные варианты удобства и оптимизации:

  • Сначала проверьте, что общее требуемое пространство не превышает, скажем, 50% от общего физического пространства.
  • Опционально запускать только процессы, принадлежащие текущему пользователю, или список, указанный пользователем.
  • Сначала проверьте, действительно ли каждый кусок памяти помещен на диск или нет.

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


Обновление: Вот мой пример функции. Она принимает ID процесса в качестве аргумента и копирует по одному байту из каждой хорошей страницы процесса. (Второй аргумент - максимальный размер памяти процесса, получаемый через GetSystemInfo().)

void UnpageProcessByID(DWORD processID, LPVOID MaximumApplicationAddress, DWORD PageSize)
{
  MEMORY_BASIC_INFORMATION meminfo;
  LPVOID lpMem = NULL;

  // Get a handle to the process.
  HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);

  // Do the work
  if (NULL == hProcess )
  {
    fprintf(stderr, "Could not get process handle, skipping requested process ID %u.\n", processID);
  }
  else
  {
    SIZE_T        nbytes;
    unsigned char buf;

    while (lpMem < MaximumApplicationAddress)
    {
      unsigned int stepsize = PageSize;

      if (!VirtualQueryEx(hProcess, lpMem, &meminfo, sizeof(meminfo)))
      {
        fprintf(stderr, "Error during VirtualQueryEx(), skipping process ID (error code %u, PID %u).\n", GetLastError(), processID);
        break;
      }

      if (meminfo.RegionSize < stepsize) stepsize = meminfo.RegionSize;

      switch(meminfo.State)
      {
      case MEM_COMMIT:
        // This next line should be disabled in the final code
        fprintf(stderr, "Page at 0x%08X: Good, unpaging.\n", lpMem);

        if (0 == ReadProcessMemory(hProcess, lpMem, (LPVOID)&buf, 1, &nbytes))
          fprintf(stderr, "Failed to read one byte from 0x%X, error %u (%u bytes read).\n", lpMem, GetLastError(), nbytes);
        else
          // This next line should be disabled in the final code
          fprintf(stderr, "Read %u byte(s) successfully from 0x%X (byte was: 0x%X).\n", nbytes, lpMem, buf);

        break;
      case MEM_FREE:
        fprintf(stderr, "Page at 0x%08X: Free (unused), skipping.\n", lpMem);
        stepsize = meminfo.RegionSize;
        break;
      case MEM_RESERVE:
        fprintf(stderr, "Page at 0x%08X: Reserved, skipping.\n", lpMem);
        stepsize = meminfo.RegionSize;
        break;
      default:
        fprintf(stderr, "Page at 0x%08X: Unknown state, panic!\n", lpMem);
      }

      //lpMem = (LPVOID)((DWORD)meminfo.BaseAddress + (DWORD)meminfo.RegionSize);
      lpMem += stepsize;
    }
  }

  CloseHandle(hProcess);
}

Вопрос: Состоит ли область, размер которой я увеличиваю, максимум из одной страницы, или я пропускаю страницы? Должен ли я попытаться узнать также размер страницы и увеличивать только на минимальный размер региона и страницы? Обновление 2: Размер страницы всего 4 килобайта! Я изменил приведенный выше код, чтобы инкремент выполнялся только с шагом 4kiB. В окончательном варианте мы избавимся от fprintf внутри цикла.

10
ответ дан 3 December 2019 в 07:36
поделиться

Нет, Windows изначально не предоставляет такой возможности. Такие программы, как Cacheman и RAM IDLE, достигают этого, просто выделяя большой кусок ОЗУ, заставляя другие вещи перемещаться по страницам на диск, что эффективно выполняет то, что вы хотите.

-2
ответ дан 3 December 2019 в 07:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: