答案是
它不是内存!!!
程序地址空间的说法都不太准确,应该叫做进程地址空间!
所以对于进程地址空间是属于操作系统的概念!我们当时学C语言的时候对这部分只是有了一个浅浅的认识,方便我们的理解!
其实程序地址空间其实叫做进程地址空间,而进程是操作系统上的概念。因此,进程地址空间分为如上图所示的几个部分:
栈区(stack): 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这 >些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
堆区(heap): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收 。分配方式类似于链表。
数据段(静态区) ( static ) :存放全局变量、静态数据。程序结束后由系统释放。
代码段: 存放函数体(类成员函数和全局函数)的二进制代码。
#include
#include
using namespace std;
int main()
{
int global = 100;
pid_t id = fork();
if (id == 0)
{
// child
cout<<"id地址"<<&id<<endl;
global = 200;
cout << "我是子进程pid:" << getpid() << " 我要改global,global:" << global << " global地址:" << &global << endl;
}
else
{
//father
cout<<"id地址"<<&id<<endl;
cout << "我是父进程pid:" << getpid() << " 我的儿子是:" << id << " global:" << global << " global地址:" << &global << endl;
}
return 0;
}
在调用fork创建子进程,返回时发生写实拷贝
每一个进程都有一个自己的地址空间
对于这个写实拷贝的个人理解就是,一班和二班都有一个叫小明的人,但是他俩不是一个人(因为班级不同),对应到进程地址空间里就是 虚拟地址一样但是对应的物理地址却不同(因为页表不同)
#include
#include
using namespace std;
int main()
{
int global = 100;
pid_t id = fork();
if (id == 0)
{
// child
global = 200;
cout << "我是子进程pid:" << getpid()
<< " 我要改global,global:" << global
<< " global地址:" << &global << endl;
}
else
{
//father
cout << "我是父进程pid:" << getpid()
<< " 我的儿子是:" << id << " global:" << global
<< " global地址:" << &global << endl;
}
return 0;
}
我们可以得出一个结论:我们在C/C++中使用的地址,
绝对不可能
是物理地址!!!
因为如果是物理地址,上图的情况是不可能产生的!!!
内存就是一个硬件,不能阻拦访问你访问!只能被动地进行读取和写入!
因此如果我们能够直接访问甚至修改物理内存,将会造成不可预料的后果,因此为了安全起见,操作系统不会让我们直接访问物理内存。
并且运用虚拟地址可以方便转换需要访问的物理地址,提高程序运行效率
每一个进程在启动的时候,都会让操作系统给它创建一个地址空间,该进程地址空间就是进程地址空间
每一个进程都会有一个自己的进程地址空间!!
那么操作系统要不要管理这些进程地址空间呢?
那么怎么去管理呢?
进程地址空间,其实是内核的一个数据结构,操作系统肯定会先描述在组织这些数据结构,mm_struct
所以我们验证说程序地址空间是不准确的,准确的应该说成进程地址空间,那么我们应该怎么来理解呢?
进程地址空间在逻辑上是一个抽象的概念,我们在谈这个概念之前我们需要引入一个进程独立性。
进程独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰!
所谓的地址空间:其实就是OS通过软件的方式,给进程提供一个软件视角,认为自己会独占系统的所有资源(内存)