Linux 第四节 进程地址空间

简介: 如果这里的地址是物理地址,就是说是真正硬件存储器上的地址,那我拿到了一个地址,它对应的值到底是10,还是20?

这节,我们重点就来说一个事情——进程地址空间。(这次比较短,连个目录都没有哈哈~~)


我们在讲C语言的时候,给大家画过这样的所谓的空间布局图

微信图片_20221210111917.png



什么栈区内存是有高地址向低地址增长,堆区是由低地址向高地址增长;


由于当时我们需要更好地理解malloc、更好地理解函数的开辟方式, 我们给大家画出了这么个模型。


可是我们并不真正理解它。


今天,我们来详细地探讨一下它。


我们先通过一个具体的例子来感受一下:

1 #include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5   int gav_l = 100;
  6 
  7   pid_t id = fork();
  8   if(id == 0)
  9   {
 10     //child
 11     gav_l = 10;
 12     printf("%d\n",gav_l);
 13     printf("i am a child , my piont is %p\n",&gav_l);                                                                                                                                        
 14     sleep(1);
 15   }
 16   if(id > 0)
 17   {
 18     //father
 19     sleep(2);
 20     printf("%d\n",gav_l);
 21     printf("i am father , my piont is %p\n",&gav_l);
 22     sleep(1);
 23   }
 24   return 0;
 25 }


这么一段代码,我们先让父进程sleep一下,就是说让子进程先跑。


我们来看运行结果



发现了什么?父进程和子进程的gav_l的值不一样,但是它们俩的地址是一样的!!!


用脚想:如果这里的地址是物理地址,就是说是真正硬件存储器上的地址,那我拿到了一个地址,它对应的值到底是10,还是20?


所以,我们得出了一个结论:这里的地址不是物理地址,而是虚拟地址。


我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理


OS必须负责将 虚拟地址 转化成 物理地址 。


那怎么理解呢?


这就引出了我们今天的重点——进程地址空间

image.png



我们结合上面的图来看。


对于一个进程而言,在OS为其创建的时候(即创建task_struct),里面会有一个mm_struct的指针,指向mm_struct这么一个结构体,在这个结构体里面,就有着一块一块区域的划分,比如:

image.png



意思就是说,其人为地 创建并规定了 某某空间从哪到哪,从哪到哪。


然后,由OS完成从虚拟内存向物理内存的映射,这项工作通过页表完成:

image.png


对于页表,举个例子,结合上图:


比如说,在mm_struct里,其是一个地址,那么其通过某种映射关系,映射到物理内中。


然后,页表将物理地址和虚拟地址存储并匹配起来,这样,日后就可以通过虚拟地址找到物理内存中的地址,进而找到其存储的值。


当然,这里只是做一个举例,关于页表,实际上存储的东西远远不止有这些。比如还有像读写信息啦等等。


我们现在就能更好的理解进程了。一个进程,其包含了很多,我们现在可以这样认为:


一个进程是包含了 一堆数据结构以及代码和数据。


在创建新的进程时,


其会依照父进程为模板,在OS为其分配PCB等数据结构的同时,会将父进程的代码和数据拷贝一份到子进程中。


由于代码都只是可读了,而数据是可读可写的,


那么对于父子进程来说,就存在了这样的关系:它们的代码是共享的,而数据是各自私有的。而为了节省内存,在拷贝过后,父子进程的数据实际上也是由同一块物理内存存储的。


就是说,当其未修改的时候,


是像这样的:

image.png



那如果数据被修改了呢?就像我们上面说的代码的那个例子一样?


那我们就需要再来说说写时拷贝。


写时拷贝:顾名思义,在写的时候进行拷贝。意思就是,在拷贝后,如果对父子进程的数据进行修改,那么OS就会帮我们在物理内存中拷贝一份相同大的数据空间(就基本是你写多少拷贝多少),然后在新的空间中去写入,再将页表中的对应的映射地址修改掉,指向新的地址即可。


就像我们刚刚的那张图这样:

image.png


那么,为什么要有这样的存在呢?


第一:可以保护内存。对物理内存的访问,由OS去完成,不让用户去瞎搞,这样一定程度上是对内存进行了保护。


第二:给所有的进程地址空间都提供一致的地址,并且提供可以连续访问的地址。保证进程的独立性。


试想,如果多个进程同时跑,没有虚拟内存,那出现这样的情况怎么办?

image.png



并且这样也会使得用户在用每个进程的时候,从用户的角度来说,由于相同的元素,地址却不一样,会很难受。这样进程的独立性也就无法保证了。




目录
相关文章
|
22天前
|
资源调度 Linux 调度
Linux c/c++之进程基础
这篇文章主要介绍了Linux下C/C++进程的基本概念、组成、模式、运行和状态,以及如何使用系统调用创建和管理进程。
28 0
|
5天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
8 1
|
17天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
15 1
|
22天前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
20 0
Linux c/c++之IPC进程间通信
|
22天前
|
Linux C++
Linux c/c++进程间通信(1)
这篇文章介绍了Linux下C/C++进程间通信的几种方式,包括普通文件、文件映射虚拟内存、管道通信(FIFO),并提供了示例代码和标准输入输出设备的应用。
17 0
Linux c/c++进程间通信(1)
|
22天前
|
Linux C++
Linux c/c++之进程的创建
这篇文章介绍了在Linux环境下使用C/C++创建进程的三种方式:system函数、fork函数以及exec族函数,并展示了它们的代码示例和运行结果。
23 0
Linux c/c++之进程的创建
|
26天前
|
网络协议 Linux 开发工具
linux系统配置固定地址
linux系统配置固定地址
|
2月前
|
Linux Shell
6-9|linux查询现在运行的进程
6-9|linux查询现在运行的进程
|
22天前
|
Linux C++
Linux c/c++进程之僵尸进程和守护进程
这篇文章介绍了Linux系统中僵尸进程和守护进程的概念、产生原因、解决方法以及如何创建守护进程。
15 0
|
2月前
|
存储 监控 安全
探究Linux操作系统的进程管理机制及其优化策略
本文旨在深入探讨Linux操作系统中的进程管理机制,包括进程调度、内存管理以及I/O管理等核心内容。通过对这些关键组件的分析,我们将揭示它们如何共同工作以提供稳定、高效的计算环境,并讨论可能的优化策略。
40 0