在Windows操作系统中是否有可能将所有内存取消分页?

13

我有足够的RAM,但是在启动和完成大量进程后,似乎大多数应用程序的虚拟内存已经被换出到磁盘,而切换回任何旧进程需要很长时间将内存重新加载到RAM。

是否有一种方法可以通过Windows API或内核调用让Windows取消分页所有(或尽可能多的)内存?也许通过遍历运行中的进程列表并让内存管理器取消分页每个进程的内存?

3个回答

11

更新3:我已将完整的程序上传到Github


根据迄今为止的回复,这里有一个天真的建议,尝试将所有应用程序都放回物理内存中:

  1. 分配一小块内存X,可能是4MB。(应该是不可分页的吗?)
  2. 遍历所有进程:
    • 对于每个进程,将其内存的块复制到X。(可能先暂停该进程?)

假设你有2GB的RAM,只有1GB实际上被进程所需。如果所有内容都在物理内存中,你只需要复制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:页面大小仅为4KiB!我将上述代码更改为每次递增4KiB。在最终代码中,我们将摆脱循环内的fprintf。


1
经过一些测试,我发现这确实可以工作得很好!它确实具有这样的效果,使一个应用程序"取消分页"变得再次响应,并且不需要任何意外的硬盘访问。 - Kerrek SB
1
@Tomalak:我将另一个答案设为被采纳的答案,因为我的程序最终基于那个想法。当mplayer或任何Adobe程序将你的整个生活页面写入磁盘后,它非常有用。 - Kerrek SB
有人能提供这个工具的二进制文件供下载和安装吗? - Tomas
@Tomas:我在家有源代码,如果你需要的话,我可以上传到Github。它曾经非常有用。(之后我换了一台内存更大、带有SSD的新机器,问题不那么紧迫了。) - Kerrek SB
Kerrek,如果您能上传一个适用于Windows XP/7的二进制文件就太好了!如果它可以允许仅对具有给定PID的进程进行取消交换(例如在命令行上)那就更好了... :-) - Tomas
显示剩余8条评论

8

实现起来并不难。使用VirtualQueryEx()发现进程使用的虚拟地址,使用ReadProcessMemory()强制页面重新加载。

这样做可能不会有任何区别,只会导致您的程序需要花费更长的时间来完成其工作。页面重新加载缓慢的常见诊断是分段的页面文件。例如在Windows XP上普遍存在,当硬盘长时间未进行碎片整理,并且经常充满时。SysInternals'的PageDefrag实用程序可以帮助解决问题。


2
是的,因为对每个进程的每个字节调用ReadProcessMemory肯定比只将事物分页回来要更好......哦,顺便说一下,您需要管理员权限并获取SE_DEBUG_NAME特权才能使用进程内存函数。 - Billy ONeal
由于我下面列出的程序基本上是基于这个想法的,所以我将把它作为被接受的答案。我现在已经将程序编写成了一个相当有用的形式,如果您需要完整的源代码,请告诉我。我发现它是一个非常有用的工具! - Kerrek SB
1
@Kerrek SB: “我很惊讶[...]分页内存[...]没有实际请求”;反过来也同样令人惊讶。操作系统需要能够坚持自己的任务,这本质上是_不民主的_。 话虽如此,VirtualLock将会做到接近你所需的功能,而使用SetProcessWorkingSetSize。参见kb108449 - sehe
我认为知识库文章提供的信息是最好的。另外,请注意我和Randolph一起。 - sehe
1
只是一条注释:这个程序非常有用,而且正好做到了我想要的:我在我和我的笔记本电脑醒来后运行了unpage命令,煮咖啡,当我回来时,所有“未分页”的进程(浏览器、邮件、资源管理器)都非常响应迅速。(单独的资源管理器在被分页时会非常烦人,因为即使打开右键上下文菜单也会使用户界面停顿几秒钟。) - Kerrek SB
显示剩余3条评论

-2

不,Windows本身没有提供这样的功能。像Cacheman和RAM IDLE这样的程序通过简单地分配大块内存来实现此目的,迫使其他东西分页到磁盘,从而有效地实现了您想要的效果。


6
听起来好像会产生他所期望的相反效果。 - anon
@Neil:他是想要分页还是取消分页?从“未分页”很难判断...如果他想要所有内容都分页,那么他正在寻找Windows的默认行为,所以我假设他想要非默认行为,即将所有内容分页到磁盘。 - Billy ONeal
1
@Neil:毫无疑问,但是还原过程的默认行为是什么?问题在于,在某个阶段,我运行的进程数量超过了物理RAM的容量,因此一些内容确实会被分页到磁盘上,但是当大多数进程都完成时,我似乎仍然有很多可用的物理RAM,但是所有旧的进程仍然被分页到磁盘上。【待续】 - Kerrek SB
@Hack59。您可以在进程资源管理器中查看每个进程的页面错误数量。但除非您的硬盘马上就要死了或者像Windows已停止使用DMA模式这样奇怪的情况,否则仅凭此可能无法解释您所看到的问题。http://winhlp.com/node/10。 - Martin Smith
1
@MSmith: 我不认为出现了这种问题;在重启后一切都很完美。只有在计算机开机几天并且我运行(并完成)许多视频编码,播放和3D游戏后,我的“常驻”进程(浏览器,电子邮件)似乎已经被分页到磁盘上,并且在内存再次可用之后没有恢复。即使现在,有大约600MB的可用RAM,Opera总共有70M的页面故障,每秒大约有100个新故障。我能找出Opera的多少内存在磁盘上,并指示系统将其加载到RAM中吗? - Kerrek SB
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接