一个 32 位程序的用户空间区域可以有多大?

缘起

我在《调试实战 | 记一次有教益的内存碎片转储文件分析》中分析了一个由于内存碎片导致的崩溃转储。发现一个很“奇怪”的现象——程序是 32 位的,但是在查看堆空间大小的时候,居然有将近 4GB

相信各位小伙伴儿应该听过下面这种说法:32 位进程有 4GB 的虚拟内存,其中低 2GB 是用户空间,应用程序可以访问,高 2GB 是内核空间,应用程序不能访问,但是内核可以访问。在系统开启 /3GB 的情况下,用户空间可以提升至 3GB,内核空间被压缩到 1GB

这也是我学到的关于 32 位进程虚拟内存相关的知识,但是上述描述不够准确。比如,开启了 /3GB,进程用户空间就一定可以提升至 3GB 吗?在 64 位系统上,上述说法还成立吗?

带着上述疑问,我翻看了微软官方文档,本文尽可能全面的总结进程的虚拟内存空间划分。因为能力有限,本文只涉及 x86/x64 平台,不涉及 ARM 平台。

说明: 本文总结的限制主要指的是虚拟内存的限制。物理内存与虚拟内存的关系可以简单想象成 1v1 的关系。如果用户空间代码只能访问 2GB 大小的虚拟内存,那么最多访问 2GB 的物理内存,即使机器上装了 16GB 的内存。

32 位程序用户态内存空间的限制

32 位进程用户空间大小受两方面的限制:

  1. 系统级别的限制:是否开启 4GT(4 gigabyte tuning),也就是 /3GB 开关。

  2. 进程本身的限制:是否设置 IMAGE_FILE_LARGE_ADDRESS_AWARE 标志。

说明: 只有以上两个条件同时满足,在 32 位操作系统上的应用程序用户态虚拟内存空间才可以达到 3GB

32 位程序在 32 位系统上的情况

下面的表格总结了各种设置下进程用户空间大小的限制:

虚拟地址范围未开启 IMAGE_FILE_LARGE_ADDRESS_AWARE开启 IMAGE_FILE_LARGE_ADDRESS_AWARE
未开启 4GT低 2GB (0x00000000 ~ 0x7FFFFFFF)低 2GB (0x00000000 ~ 0x7FFFFFFF)
开启 4GT低 2GB (0x00000000 ~ 0x7FFFFFFF)低 3GB (0x00000000 ~ 0xBFFFFFFF)
开启 4GT 并设置 USERVAM低 2GB (0x00000000 ~ 0x7FFFFFFF)低 (0x00000000 ~ M-1)

说明: 开启 4GT 的情况下,用户空间上限默认是 3GB,但是可以手动指定一个 2GB~3GB 之间的值作为上限。

32 位程序在 64 位系统上的情况

32 位程序在 64 位系统上的情况比较单一。如果设置了 IMAGE_FILE_LARGE_ADDRESS_AWARE 标志,那么用户空间大小可以达到 4GB,否则只能使用低 2GB

虚拟地址范围未开启 IMAGE_FILE_LARGE_ADDRESS_AWARE开启 IMAGE_FILE_LARGE_ADDRESS_AWARE
不论是否开启 4GT低 2GB (0x00000000 ~ 0x7FFFFFFF)4GB (0x00000000 ~ 0xFFFFFFFF)

上面两个表格的关键内容是我从微软官方文档中摘录的,关于内存和地址空间的更多限制的可以参考下图(同样截自微软官方文档):

4a3cc2ac98d99bbb8df88abf4c8a1861.png
memory-and-address-space-limits

如何开启 4GT

因为 4GT 只对 32 位系统有意义,所以下述设置方法仅针对 32 位系统生效。

xp server2003

xp 或者 server2003 上,可以通过修改 Boot.ini 文件来开启 4GT

[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Server03, Standard" /fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Server03, Standard /3GB Enabled" /fastdetect /3GB
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Server03, Standard /3GB Enabled 2.5GB" /fastdetect /3GB /USERVA=2560

倒数第 2 行表示开启 4GT,并且用户地址空间最大值是默认的 3GB

最后一行表示开启 4GT,并且设置用户地址空间的最大地址值为自定义的 2.5GB

Vista 及以上

vista 及以上的操作系统上,可以通过 bcdedit 进行设置。我以 win7 为例。

管理员权限运行 cmd,输入 bcdedit /set increaseuerva target_user_virtual_address_mb

比如,如果想设置最大地址值为 2.5GB,则可以输入 bcdedit /set increaseuerva 2560。如果想设置最大地址值为 3GB,则可以输入 bcdedit /set increaseuerva 3072

如果不想设置了,可以输入 bcdedit /deletevalue increaseuserva

说明:以上设置需要管理员权限,并且重启后生效

介绍完调整系统设置的方法后,我们看看如何设置应用程序。

如何设置 IMAGE_FILE_LARGE_ADDRESS_AWARE

如果有源码,可以直接调整 vs 的工程设置。

在工程文件上右键,属性,打开工程属性设置对话框。找到 Configuration Property -> Linker -> System 选项,设置 Enable Large Addresess 的值为 Yes。如下图:

846715553d0935be6b149408ff6ff2e4.png
enable-large-address-aware-flag

当然,也可以直接在 Linker 下的 Command Line 中手动加上 /LARGEADDRESSAWARE 选项。效果是一样的。

136cf6d5cc6b1c101b81e10aa7020f05.png
manually-set-large-address-aware-option

通过以上设置,可以为新编译出来的程序设置 Large Address Aware 标志。

如果没有源码,或者程序已经生成了,该如何修改呢?直接修改 PE 文件中对应的标志位即可。下图是我使用 CFF Explorer 修改应用程序的 Large Address Aware 标志的截图。

0b48f72b2031ea2d0a0708d656959985.png
manually-modify-pe-header-to-support-large-address

验证

为了加深自己的印象,也为了验证参考资料的准确性,我特意写了一个非常简单的程序,该程序可以获取到用户态内存最高地址。代码如下:

#include "stdafx.h"
#include "windows.h"

double ToGb(size_t bytes)
{
 return bytes / 1024.0 / 1024.0 / 1024.0;
}

int _tmain(int argc, _TCHAR* argv[])
{
 LPVOID result = VirtualAllocEx(GetCurrentProcess(), (LPVOID)0, 0x1000, MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE);
 printf("highest address : %0x, about %.3lfGB\r\n", result, ToGb((size_t)result));
 return 0;
}

相关的测试工程已经上传到了 gitee,有需要的小伙伴儿可以自行下载。

验证 32 位程序在 32 位系统下的运行效果

第一张图是未开启 4GT 的运行结果:

04ab58708f371558eb35f4bc6d3d500c.png
highest-available-address-4gt-disabled-32-bit-system

第二张图是开启 4GT 并且使用默认值(3GB)的的运行结果:

508a768e27dc86b8eb4ac47e3667a802.png
highest-available-address-4gt-enabled-32-bit-system

第三张图是开启 4GT 并且指定 /USERVA 大小为 2.5GB 的运行结果:

936cd5393cb0a02f1ea4f554e6f28f6b.png
highest-available-address-4gt-enabled-userva-2.5gb-32-bit-system

验证 32 位程序在 64 位系统下的运行效果

我直接在我的 win10 系统中运行了,结果如下:

725e75d92012682eea00be998a4daca2.png
highest-available-address-on-64bit-system

从以上输出结果可知,微软的文档没有骗我们。

PAE

除了 /3GB,各位小伙伴儿可能还听过一个叫 PAE 的东东,全称是 Physical Address Extension,直译过来就是物理地址扩展。从名字可以看出是跟物理地址有关的。

我认为 PAE 解决的最重要的一个问题是,让 32 位操作系统可以使用更多的物理内存。假设,一台装有 16GB 的物理内存的机器上,装了 32 位的 windows 操作系统,在没开启 PAE 情况下,只能使用低 4GB 的物理内存,无法使用高 12GB 的物理内存。如果开启了 PAE,那么这 16GB 都可以使用。

我目前理解这个选项是通过控制分页模式来达到访问更多物理内存的效果的。如果不开启 PAE,那么将使用 10-10-12 分页,开启 PAE 后,会使用 2-9-9-12 分页。关于分页模式,强烈推荐大家听一听海哥的内核课程。

开启或关闭 PAE

xp 或者 server2003 上,可以通过修改 Boot.ini 文件来开启或关闭 PAE

; 开启 PAE
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Server03, Standard" /fastdetect /PAE

; 关闭 PAE
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Server03, Standard" /fastdetect /NOPAE

vista 及以上的操作系统上,可以通过 bcdedit /set 命令来开启或关闭 PAE

; 开启 PAE
bcdedit /set [{ID}] pae ForceEnable

; 关闭 PAE(开启 DEP 的时候不能关闭 PAE,必须同时关闭 DEP)
bcdedit /set [{ID}] nx AlwaysOff
bcdedit /set [{ID}] pae ForceDisable

总结

应用程序开启了 Large Address Aware 标志后可以访问更大的虚拟内存空间。如果可能,一定要开启!

可以在 vs 工程中通过 /LargeAddressAware 选项开启,也可以直接修改 PE 文件中对应的标志位进行修改。

参考资料

  • https://docs.microsoft.com/en-us/windows/win32/memory/virtual-address-space

  • https://docs.microsoft.com/en-us/windows/win32/memory/4-gigabyte-tuning

  • https://docs.microsoft.com/en-us/windows/win32/memory/physical-address-extension

  • https://docs.microsoft.com/en-us/windows/win32/memory/memory-limits-for-windows-releases

  • https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/bcdedit--set

  • https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/boot-parameters-to-configure-dep-and-pae

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/779734.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣 第 134 场双周赛 解题报告 | 珂学家

前言 题解 T1/T3是环形的处理技巧,这边可以double数组(更准确地讲,添加一个合适的小尾巴). T4是典题,前不久周赛刚考过,是一道结论题,也可以借助数据结构处理。 T1. 交替组 I 和T3一起讲 T2. 与敌人战斗后的最大分数…

昇思25天学习打卡营第13天|K近邻算法实现红酒聚类

K近邻算法(K-Nearest-Neighbor, KNN)是一种用于分类和回归的非参数统计方法,是机器学习最基础的算法之一。它正是基于以上思想:要确定一个样本的类别,可以计算它与所有训练样本的距离,然后找出和该样本最接…

机器学习与现代医疗设备的结合:革新医疗健康的未来

🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 引言 随着技术的不断进步,机器学习(Machine Learning, ML)在现代医疗设备中的应用正在改变着…

7.5cf

Problem - D - Codeforces 大致题目意思&#xff1a;找#的圆心 #include<bits/stdc.h> typedef long long ll;#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) const ll N1e21; char a[N][N]; using namespace std;int main() {IOS;int t;cin>>t;whi…

含并行连结的网络

一、Inception块 1、白色部分通过降低通道数来控制模型复杂度&#xff0c;蓝色做特征提取工作&#xff0c;每条路上的通道数可能不同&#xff0c;大概我们会把更重要的那部分特征分配更多的通道数 2、Inception只改变高宽&#xff0c;不改变通道数 3、在不同的情况下需要选择…

gitee项目上不同的项目分别使用不用的用户上传

最近使用根据需要&#xff0c;希望不同的项目使用不同的用户上传&#xff0c;让不同的仓库展示不同的用户名&#xff01;&#xff01;&#xff01; 第一步查看全局的用户信息&#xff1a; # 查看目前全局git配置信息 git config -l #会输出全局的git配置信息 第二步进入到要设…

【MySQL】1.初识MySQL

初识MySQL 一.MySQL 安装1.卸载已有的 MySQL2.获取官方 yum 源3.安装 MySQL4.登录 MySQL5.配置 my.cnf 二.MySQL 数据库基础1.MySQL 是什么&#xff1f;2.服务器&#xff0c;数据库和表3.mysqld 的层状结构4.SQL 语句分类 一.MySQL 安装 1.卸载已有的 MySQL //查询是否有相关…

【ubuntu】安装(升级)显卡驱动,黑屏|双屏无法使用问题解决方法

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 ubuntu 安装(升级)显卡驱动&#xff0c;黑屏|双屏无法使用问题解决方法 由于项目需要&#xff0c;对显卡驱动进行升级。升级完就黑屏。。。。&#xff0…

平台稳定性里程碑 | Android 15 Beta 3 已发布

作者 / 产品管理副总裁、Android 开发者 Matthew McCullough 从近期发布的 Beta 3 开始&#xff0c;Android 15 达成了平台稳定性里程碑版本&#xff0c;这意味着开发者 API 和所有面向应用的行为都已是最终版本&#xff0c;您可以查阅它们并将其集成到您的应用中&#xff0c;并…

qt 开发笔记堆栈布局的应用

1.概要 画面中有一处位置&#xff0c;有个按钮点击后&#xff0c;这片位置完全换成另一个画面&#xff0c;这中情况特别适合用堆栈布局。 //堆栈布局的应用 #include <QStackedLayout> QStackedLayout *layout new QStackedLayout(this); layout->setCurrentIndex(…

无法下载cuda

cuda下载不了 一、台式机电脑浏览器打不开cuda下载下面二、解决办法 一、台式机电脑浏览器打不开cuda下载下面 用360、chrome、Edge浏览器都打不开下载页面&#xff0c;有的人说后缀com改成cn&#xff0c;都不行。知乎上说是网络问题&#xff0c;电信换成换成移动/联通的网络会…

文心一言最常用的20条指令及指令说明,含增强指令

下面是20条文心一言的指令及其说明&#xff0c;每条指令尽量简洁明了&#xff0c;以便在有限的字数内提供尽可能多的信息。以下是这些指令及其说明&#xff1a; 1. 查询天气 指令&#xff1a;今天北京的天气怎么样&#xff1f;说明&#xff1a;此指令用于查询特定城市&#xf…

Python结合MobileNetV2:图像识别分类系统实战

一、目录 算法模型介绍模型使用训练模型评估项目扩展 二、算法模型介绍 图像识别是计算机视觉领域的重要研究方向&#xff0c;它在人脸识别、物体检测、图像分类等领域有着广泛的应用。随着移动设备的普及和计算资源的限制&#xff0c;设计高效的图像识别算法变得尤为重要。…

数据结构基础--------【二叉树基础】

二叉树基础 二叉树是一种常见的数据结构&#xff0c;由节点组成&#xff0c;每个节点最多有两个子节点&#xff0c;左子节点和右子节点。二叉树可以用来表示许多实际问题&#xff0c;如计算机程序中的表达式、组织结构等。以下是一些二叉树的概念&#xff1a; 二叉树的深度&a…

高考选专业,兴趣与就业前景该如何平衡?

从高考结束的那一刻开始&#xff0c;有些家长和学生就已经变得焦虑了&#xff0c;因为他们不知道成绩出来的时候学生应该如何填报志愿&#xff0c;也不知道选择什么样的专业&#xff0c;毕竟大学里面的专业丰富多彩&#xff0c;如何选择确实是一门学问&#xff0c;而对于学生们…

Zynq7000系列FPGA中DMA引擎编程指南

DMA引擎的编程指南通常涉及一系列步骤和API调用&#xff0c;以确保数据在内存之间的高效传输&#xff0c;而无需CPU的直接干预。 DMA引擎的编程指南包括以下部分&#xff1a; 一、编写微代码为AXI事务编写CCRx程序 通道微码用于设置dmac.CCRx寄存器以定义AXI事务的属性。这是…

Node.js-path 模块

path 模块 path 模块提供了 操作路径 的功能&#xff0c;如下是几个较为常用的几个 API&#xff1a; 代码实例&#xff1a; const path require(path);//获取路径分隔符 console.log(path.sep);//拼接绝对路径 console.log(path.resolve(__dirname, test));//解析路径 let pa…

java反射介绍

Java反射API允许你在运行时检查和修改程序的行为。这意味着你可以动态地创建对象、查看类的字段、方法和构造函数&#xff0c;甚至调用它们。这是一个强大的特性&#xff0c;但也应该谨慎使用&#xff0c;因为它可以破坏封装性。 以下是使用Java反射的一些常见用途&#xff1a;…

041基于SSM+Jsp的高校校园点餐系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

OPENCV(图像入门笔记)

使用OpenCV读取图像 使用cv.imread()函数读取图像。 第一个参数为图像名称 第二个参数是一个标志&#xff0c;它指定了读取图像的方式。分别有三种 cv.IMREAD_COLOR&#xff1a; 加载彩色图像。任何图像的透明度都会被忽视。它是默认标志。 cv.IMREAD_GRAYSCALE&#xff1a;以…