У меня есть много RAM, однако, после запуска и окончания большого количества процессов, кажется, что большая часть виртуальной памяти приложений была разбита на страницы к диску, и переключающийся на любой из более старых процессов требует, чтобы очень долгое время загрузило память назад в RAM.
Существует ли путь, или с помощью Windows API или через вызов ядра, чтобы заставить Windows не разбивать на страницы все (или как можно больше) память? Возможно, путем продвижения через список выполнения процессов и заставляют диспетчера памяти не разбивать на страницы память каждого процесса?
Ну, это несложно реализовать самостоятельно. Используйте VirtualQueryEx()
для обнаружения виртуальных адресов, используемых процессом, ReadProcessMemory()
для принудительной перезагрузки страниц.
Вряд ли это вообще что-то изменит, просто ваша программа будет делать свою работу бесконечно долго. Общей диагностикой медленной перезагрузки страниц является фрагментированный файл подкачки. Часто встречается, например, в Windows XP, когда диск долгое время не подвергался дефрагментации и ему позволялось часто заполняться почти до отказа. Утилита PageDefrag от SysInternals может помочь решить эту проблему.
Обновление 3: Я выложил свою полную программу на github.
Хорошо, основываясь на ответах, вот наивное предложение для инструмента, который пытается вернуть все приложения в физическую память:
Предположим, что у вас 2 ГБ оперативной памяти, а процессам фактически требуется только 1 ГБ. Если все находится в физической памяти, вы скопируете только 256 фрагментов, и это не конец света. В конце концов, есть хороший шанс, что все процессы теперь полностью находятся в физической памяти.
Возможные варианты удобства и оптимизации:
Я могу перебрать все процессы с помощью 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 внутри цикла.
Нет, Windows изначально не предоставляет такой возможности. Такие программы, как Cacheman и RAM IDLE, достигают этого, просто выделяя большой кусок ОЗУ, заставляя другие вещи перемещаться по страницам на диск, что эффективно выполняет то, что вы хотите.