【hello Linux】程序地址空间

目录

1. 内存空间布局 

2. 虚拟地址的引出

3. 进程地址空间

补充:



【hello Linux】程序地址空间_第1张图片

 Linux

1. 内存空间布局 

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

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

【hello Linux】程序地址空间_第2张图片

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

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

  #include 
  #include 
  #include 
  #include 
  
  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;    
  }    

【hello Linux】程序地址空间_第3张图片

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

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

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

2. 虚拟地址的引出

先来看一段代码:

#include     
#include     
#include     
    
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;                                                                                                                                                                              
}    

【hello Linux】程序地址空间_第4张图片

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

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

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

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

那是怎么回事呢?

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

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

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

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

3. 进程地址空间

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

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

如下所示: 

 【hello Linux】程序地址空间_第5张图片

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

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

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

补充:

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

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

CPU的调度。

坚持打卡!

你可能感兴趣的:(Linux,linux)