程序地址空间

2024-01-10 11:40:48
1.验证地址空间排布

为了验证地址的排布,我们将地址空间的各个区域的地址打印出来

#include <stdio.h>
#include <unistd.h> 
#include <stdlib.h> 

int g_unval;
int g_val=100;

//函数参数
int main(int argc,char *argv[],char *env[])
{
  const char* str= "hello world";
 // 10;
 // 'v';
  //函数和数组的规则是一样的,函数名也代表了该函数的地址
  printf("code addr:%p\n",main);
  printf("init global addr:%p\n",&g_val);
  printf("uninit global addr:%p\n",&g_unval);
  //malloc 返回的是个指针 指针指向的即创建的在堆上申请空间的地址
  char *heap_mem=(char*)malloc(10);
  char *heap_mem1=(char*)malloc(10);
  char *heap_mem2=(char*)malloc(10);
  char *heap_mem3=(char*)malloc(10);
  printf("heap_addr:%p\n",heap_mem);
  printf("heap_addr:%p\n",heap_mem1);
  printf("heap_addr:%p\n",heap_mem2);
  printf("heap_addr:%p\n",heap_mem3);
  
  printf("stack_addr:%p\n",&heap_mem);
  printf("stack_addr:%p\n",&heap_mem1);
  printf("stack_addr:%p\n",&heap_mem2);
  printf("stack_addr:%p\n",&heap_mem3);

  printf("raed only string:%p\n",str);

  for(int i=0;i<argc;i++)
  {
    printf("argv[%d]:%p\n",i,argv[i]);
  }

  for(int i=0;env[i];i++)
  {
    printf("env[%d]:%p\n",i,env[i]);
  }
  return 0;
}

代码注释:

为了打印代码区的地址,因为函数名同数组名一样,代表其首地址,故我们打印代码区的地址时输入main即可

main函数最多可以输入三个参数? 分别是命令行参数的个数 命令行参数 环境变量参数,可以通过遍历输出,将命令行的参数地址打印出来

打印堆区地址时,直接输入该指针即可,指针所指即malloc在堆区上申请空间的地址

打印栈区地址时,代码上创建的变量都在栈上,指针也是变量,所以取指针的地址即可

打印结果如下

输出的地址如同地址空间排布图一样,从上至下,并且验证了堆向下增长,栈向上增长。(堆栈相对而生)

在c/c++下只输入字面常量是可以编译通过的!并且正文代码上有一块字符常量区,都是只读的!

2.什么是地址空间

操作系统给这些进程分配地址空间时,要对这些空间进行管理

所以内存中的地址空间,本质上也是一种特定的数据结构,要和特定的进程相关联起来

早期的计算机是直接访问的物理内存,内存随时可以被用户读写,这样十分不安全

为解决以上弊端,现代的计算机提出给每个进程提供一个pcb结构体,给每个进程开创一个虚拟空间,虚拟空间通过页表映射到物理内存,当虚拟地址访问非法地址时,系统会禁止映射,这样就变相的保护了物理地址。只要保证每一个进程的页表映射的是物理内存的不同区域,就可以做到进程之间互不干扰,保证了进程的独立性。

总结:每个进程都要有地址空间,它是一种特殊的数据结构,里面包含了各区域的划分。

在task_struct结构体中可以找到指向struct mm_struct的指针,里面就包括了各区域的划分

3.关于虚拟地址空间
(1)程序在编译完成后形成可执行程序但并未加载到内存时,程序内部有地址么?

有的,地址空间不仅os要遵守,编译器同样遵守!编译器再编译代码时,就已经形成各个区域,采用和linux一样的编址方式,所以程序在编译时每一个字段,就早已形成虚拟地址!

代码内的每一个变量和函数之前都有自己的虚拟地址,并且会互相跳转,通过页表映射到物理内存,在反馈给cpu执行指令,cpu会先找到物理内存中的跳转地址(虚拟地址),再根据页表映射到物理内存。

代码向系统发送指令,指令内部也有地址,该地址是虚拟地址!

(2)为何代码中的一个变量会有不同的值?
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int g_val=100;

int main()
{
  pid_t id=fork();
  if(id<0)
  {
    perror("fork");
    return 0;
  }
  else if(id==0)
  {
    g_val=200;
    printf("child[%d]:%d:%p\n",getpid(),g_val,&g_val);
  }
  else
  {
    printf("parent[%d]:%d:%p\n",getpid(),g_val,&g_val);
  }
}

同一个地址但是值不同,说明改地址用的是虚拟地址

父子进程的g_val的虚拟地址相同,甚至一开始映射的物理地址也相同,当子进程的g_val值改变时,发生写时拷贝,子进程g_val映射的物理地址也发生改变

文章来源:https://blog.csdn.net/qq_63522381/article/details/135475895
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。