Linux程序地址空间


进程:链接: link.

1. 程序地址空间回顾

在学习C语言的时候,就经常看到这样的空间布局图,但又不是完全一样的。因为这个更准确的说是操作系统那一部分的图。其实这个叫做进程地址空间。共享区里存放的是:动态库和共享内存
在这里插入图片描述
通过代码来解释

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 
  4 int g_val = 100;
  5 int g_unval;
  6 
  7 int main(int argc,char *argv[],char *env[])
  8 {
  9   printf("code addr : %p\n",main);
 10   printf("g init addr : %p\n",&g_val);
 11   printf("g uninit addr: %p\n",&g_unval);
 12 
 13   char *mem = (char*)malloc(10);
 14 
 15   printf("heap addr: %p\n",mem);
 16 
 17   printf("stack addr: %p\n",&mem); // 因为你的char*是一个局部变量所以存放在栈里
 18 
 19   printf("opt addr: %p\n",argv[0]);
 20   printf("opt addr: %p\n",argv[argc-1]);
 21 
 22   printf("env addr : %p\n",env[0]);                                                                                                                                                     
 23   return 0;
 24 }

在这里插入图片描述
代码区,初始化的数据,未初始化的数据,堆(且地址从低向高增长),栈(地址高到低不断减小),命令行参数,环境变量,内核空间,他们的地址都是逐渐增加的。

2. 地址空间深入理解

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 
  4 int g_val = 100;
  5 
  6 int main()
  7 {
  8   pid_t id = fork();
  9   if(id == 0){
 10     //child
 11     while(1){
 12       printf("g_val : %d , g_val addr : %p, child\n", g_val, &g_val);
 13       sleep(1);
 14     }
 15   }
 16   else if(id >0){
 17     //parent
 18     while(1){
 19       printf("g_val : %d ,g_val addr : %p,father\n", g_val, &g_val);
 20       sleep(1);
 21     }
 22   }
 23   else{
 24     printf("output error");
 25   }
 26   return 0;
 27 }                                                                                                                                                                                       

在这里插入图片描述
我们发现,输出出来的变量值和地址都是一模一样的,很好理解呀,因为子进程按照父进程为模板,父子并没有对变量进行任何修改。但是此时稍微修改一下,让子进程先跑起来,并且走它的过程中会把g_val 的值修改为1000,再看效果?

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 
  4 int g_val = 100;
  5 
  6 int main()
  7 {
  8   pid_t id = fork();
  9   if(id == 0){
 10     //child
 11     g_val = 1000;
 12     while(1){
 13       printf("g_val : %d , g_val addr : %p, child\n", g_val, &g_val);
 14       sleep(1);
 15     }
 16   }
 17   else if(id >0){
 18     //parent
 19     sleep(3);                                                                                                                                                                           
 20     while(1){
 21       printf("g_val : %d ,g_val addr : %p,father\n", g_val, &g_val);
 22       sleep(1);
 23     }
 24   }
 25   else{
 26     printf("output error");
 27   }
 28   return 0;
 29 }

在这里插入图片描述
我们发现,父子进程输出的地址是一样的,但是变量的内容却不一样

  1. 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
  2. 但地址值是一样的,说明,该地址绝对不是物理地址
  3. 在Linux地址下,这种地址叫做虚拟地址
  4. 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理OS必须负责将虚拟地址转化成物理地址
  5. 地址空间仅仅只是一种区域的划分,只有物理内存才具有保存数据的能力

在这里插入图片描述

这里也反映出了进程在运行的时候,具有独立性

进程的地址空间是虚拟地址是不发生变化的,变化的只是虚拟地址到物理内存上的映射关系

2.1 什么是地址空间?

地址空间:本质是用来描述进程所占有资源的一张表,在OS内核中的本质就是一个数据结构struct mm_struct{};将内存划分为了多个区域

系统中可能存在多个进程,所以系统中一定存在多个地址空间,既然多了,就需要被管理起来,如何管理呢?
先描述:地址空间本质就是一个数据结构struct
在组织struct mm_struct {}; 而这个结构体内的成员是通过对区域的划分来区别开的

struct mm_struct
{
	 unsigned long code_start;
	 unsigned long code_end;
	 unsigned long init_date_start;
	 unsigned long init_date_end;
	 unsigned long uninit_date_start;
	 unsigned long uninit_date_end;
	 unsigned long heap_start;
	 unsigned long heap_end;
	 ....	 
};

    struct mm_struct mm = {0x011010,0x012032,.....};

申请空间的本质:向内存所要空间,得到物理地址,然后在特定的区域申请没有被使用的虚拟地址,然后建立映射关系,最终返回虚拟地址。

2.2 为什么要有地址空间?

我直接使用物理内存的地址不行吗?
如果没有地址空间,那么进程直接访问的就是物理地址,就会出现很多错误比如野指针问题无法在避免,还有就是进程的数据存放在物理内存中是不连续的,一旦空间不连续,就会造成访问不方便,并且增加了异常越界的概率。为了解决上面的问题就引入了地址空间。加入了地址空间以后你的任何非法的操作都由OS去帮你检查。简单点说就是起到保护内存和将空间连续化处理
在这里插入图片描述

2.3 地址空间是怎么工作的?

通过地址空间所提供的地址,然后在通过页表转化为物理内存,拿到数据和内容。
MMU是内存管理单元,虚拟转物理通过软件效率可能会很低,所以采用软硬件结合来转换,并且还比较方便,相对简单很多。
在这里插入图片描述
便于理解的一个图表,其中学生都是可以在教室里面乱坐的,也有可能某个学生不在教室,是在家里上课的。
在这里插入图片描述
tast_struct 中是包含了很多的进程链接信息
运行队列:是把进程PCB从等待队列中拿来运行的过程
等待队列:本质是把进程PCB排队的过程

task_struct中一些需要被解释的:
程序计数器: 程序中即将被执行的下一条指令的地址
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
在这里插入图片描述

I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Linux 中,内核地址空间是操作系统内核独占的虚拟内存空间,用于存放内核代码、数据结构和驱动程序等。内核地址空间对用户进程是不可见的,用户进程无法直接访问和修改内核地址空间中的内容。 Linux 内核地址空间通常被划分为以下几个部分: 1. 物理内存映射区(Physical Memory Mapping):这个区域用于将物理内存映射到内核地址空间中,使得内核可以直接访问物理内存的内容。在这个区域中,每个物理页都有一个对应的内核虚拟地址,内核可以通过这些虚拟地址来访问物理内存。 2. 内核代码区(Kernel Code):这个区域存放了内核的代码,包括各种系统调用、中断处理程序和驱动程序等。内核代码区通常是只读的,以保证内核代码的完整性和安全性。 3. 内核数据区(Kernel Data):这个区域用于存放内核运行时的数据结构,如进程控制块(Process Control Block)、中断向量表、系统全局变量等。内核数据区中的数据可以被内核读写,但对用户进程来说是不可见的。 4. 内核堆(Kernel Heap):内核堆是内核动态分配内存的区域,用于存放内核运行时需要的临时数据结构和缓冲区等。内核堆的大小是可变的,根据需要进行扩展或收缩。 5. 内核栈(Kernel Stack):每个内核线程和中断处理程序都有自己的内核栈,用于保存函数调用的上下文信息。内核栈的大小通常比较小,以节省内存空间。 总体来说,Linux 的内核地址空间是操作系统内核独占的一块虚拟内存空间,用于存放内核代码、数据和驱动程序等。内核地址空间对用户进程是不可见的,用户进程无法直接访问和修改其中的内容。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值