> 文章列表 > 【hello Linux】程序地址空间

【hello Linux】程序地址空间

【hello Linux】程序地址空间

目录

1. 内存空间布局 

2. 虚拟地址的引出

3. 进程地址空间

补充:


 Linux🌷

1. 内存空间布局 

在之前我们学习C语言时,我们知道可以大致将内存划分为:堆区、栈区和静态区;

其实这样的划分是相当粗略的,下来看一下较为细致的空间布局图:

用户空间是供我们使用的,内核空间是供操作系统使用的。

下来用一段代码来验证一下地址空间划分是否真如上述一般:

  #include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>int g_val=100;int g_unval;    int main(int argc,char* argv[],char* env[])    {    const char* s="hello linux!\\n";    printf("code addr: %p\\n",main);    printf("const string addr: %p\\n",s);    printf("init_val addr: %p\\n",&g_val);                                                                                                                                                printf("uninit_val addr: %p\\n",&g_unval);    char* heap1=(char*)malloc(1);    char* heap2=(char*)malloc(1);    char* heap3=(char*)malloc(1);    printf("heap-1 addr: %p\\n",heap1);    printf("heap-2 addr: %p\\n",heap2);    printf("heap-3 addr: %p\\n",heap3);    int a=10;    int b=2;    printf("stack-1 addr: %p\\n",&s);    printf("stack-2 addr: %p\\n",&heap1);    printf("stack-3 addr: %p\\n",&heap2);    printf("stack-4 addr: %p\\n",&heap3);    printf("stack-5 addr: %p\\n",&a);    printf("stack-6 addr: %p\\n",&b);    printf("cmd addr: %p\\n",argv[0]);    printf("env addr: %p\\n",env[0]);    return 0;    }    

 经过证实,内存空间布局确实如上述图中所画。

由低地址->高地址,首先是:代码区、字符串常量区、已初始化的全局变量、未初始化的全

局变量、堆区(地址由小到大)、栈区(地址由大到小)、命令行参数、环境变量。

2. 虚拟地址的引出

先来看一段代码:

#include <stdio.h>    
#include <unistd.h>    
#include <stdlib.h>    int g_val=100;    int main()    
{    //创建子进程    if(fork()== 0)    {    //child    int count=5;    while(count)    {    printf("I am child, times: %d ,g_val=%d ,&g_val=%p\\n",count,g_val,&g_val);    count--;    sleep(1);    if(count==3)    {    printf("******child开始更改数据************\\n");    g_val=200;    printf("******child更改数据完成************\\n");    }    }    }    else    {    //parent    while(1)    {    printf("I am father,g_val=%d,&g_val=%p\\n",g_val,&g_val);    sleep(1);    }    }    return 0;                                                                                                                                                                              
}    

上述代码:创建了一个子进程,父子进程分别打印g_val的值和g_val的地址,当子进程在count=3

时,将g_val的值更改,发现父子进程打印出来的g_val的值不一样了,这个我们知道—是因为“写时

拷贝”的原因呢。但我们惊奇的发现,它们的值不一样,但是地址却一样。

在这时我们很疑惑,难道同一地址空间能存不同的值吗?显然是不能的!

那是怎么回事呢?

其实我们打印出来的这个地址,绝对不是物理地址(物理内存),是虚拟地址!

我们在用C/C++语言所看到的地址,全部都是虚拟地址!

物理地址,用户一概看不到,由OS统一管理。

OS负责将虚拟地址转化为物理地址

3. 进程地址空间

进程每次被创建出来,其实OS不止为进程创建task_struct,其实还为每个进程开辟了虚拟地址空

间mm_struct(大小4GB / 在32位下),以及页表(保存虚拟地址—>物理地址的映射)

如下所示: 

 

这张图也就很好的说明了:
同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址!

在这时我们便要对进程这一概念进一步完善:

进程 = task_struct + mm_struct + 页表 + 数据 + 代码

补充:

我们在学习进程优先级时,知道nice为优先级的优先级的修正值,其取值为-19~20,共40个级别;

其实进程的运行队列也是有40个,将优先级相同的进程放在一个运行队列中,这时候更加方便

CPU的调度。

坚持打卡!

😃