进程地址空间
例子引入:
我们在讲C语言的时候,老师给大家画过这样的空间布局图,但是我们对它不了解
我们写一个代码来验证Linux进程地址空间
#include<stdio.h>
#include<assert.h>
#include<unistd.h>
int g_value=100;
int main()
{
pid_t id=fork();
assert(id>=0);
if(id==0)
{
//child
while(1)
{
printf("我是子进程,我的id是:%d,我的父进程是:%d, g_value:%d,&g_value:%p\n",getpid(),getppid(),g_value,&g_value);
sleep(1);
}
}
else
{
//father
while(1)
{
printf("我是父进程,我的id是:%d,我的父进程是:%d,g_value:%d,&g_value:%p\n",getpid(),getppid(),g_value,&g_value);
sleep(2);
}
}
return 0;
}
这里没什么问题,就是他们的g_valule 和其地址都是一样的,
我们将代码调整一下,让子进程的g_value++
#include<stdio.h>
#include<assert.h>
#include<unistd.h>
int g_value=100;
int main()
{
pid_t id=fork();
assert(id>=0);
if(id==0)
{
//child
while(1)
{
printf("我是子进程,我的id是:%d,我的父进程是:%d,g_value:%d,&g_value:%p\n",getpid(),getppid(),g_value,&g_value);
sleep(1);
g_value++;//只有子进程会进行修改
}
}
else
{
//father
while(1)
{
printf("我是父进程,我的id是:%d,我的父进程是:%d,g_value:%d,&g_value:%p\n",getpid(),getppid(),g_value,&g_value);
sleep(2);
}
}
return 0;
}
我们可以发现子进程的g_value变了,但是父进程没有变,两个的地址还是一样的
❓为什么他们两个地址相同但是读出来的数据不同呢?(下文会解答)
故事引入:
画的饼:进程地址空间,10亿美金:内存,老板:操作系统,四个私生子是进程
❓大富翁,要不要把“饼”管理起来呢?
显然需要的,遵循先描述再组织的原则
所以,进程地址空间,就是就是给进程画的大饼
进程地址空间 → 逻辑上抽象的概念 → 让每个进程都认为自己独占系统的所有资源
**概念:**操作系统通过软件的方式,给进程提供一个软件视角,认为自己是独占系统的所有资源(内存)。
区域和页表:
什么叫做区域?我们来拿一张桌子来理解,初中的时候小花和小胖分过 “38线”
三八线的本质就是区域划分!
🔥地址空间本身就是一个线性区域,地址空间是线性结构的!
struct mm_struct {
long code_start;
long code_end;
long init_start;
long init_end;
long uninit_start;
long uninit_end;
long heap_start;
long heap_end;
long stack_start;
long stack_end;
...
}
如果限定了区域,那么区域之间的数据是什么?
是虚拟地址or线性地址
🔥程序加载到内存,由程序变成进程后,由操作系统给每个进程构建的一个页表结构,就是 页表。
🔥数据和代码真正只能在内存中!
找到地址不是目的,而是手段
这个结构也体现了进程具有独立性
pid_t id=fork()
if(){}
else
{}
为什么进程地址空间要存在?
🔥这两个存在的意义:1.防止地址随意访问,保护物理内存与其他进程
❓常量字符串不能修改,这是为什么呢?💡因为页表访问的时候是有权限的,权限不能修改
char*str=“hello world”;
*str=‘H’;