linux进程地址空间

目录

1.进程的回顾

1.1竞争性

1.2独立性

1.3并行和并发

1.4进程的切换

2.环境变量

2.1环境变量简介

​编辑

2.2命令行参数

3.程序地址空间

3.1地址空间简介

3.2虚拟地址

3.3页表概念的引入

3.4谈谈细节

3.5进程地址空间管理

3.6进程地址空间的存在意义

3.7页表


1.进程的回顾

1.1竞争性

系统里面的进程很多,CPU资源有限,所以不同的进程之间具有竞争性

1.2独立性

CPU上面的资源不允许同时被访问,都是没一个进程独立被使用的,一个进程的运行是不可以影响其他的进程的;

1.3并行和并发

多个进程在多个CPU下面同时运行,这个就叫做并行;

多个进程在一个CPU下面采用进程切换的方式,在一段时间里面,所有的进程都可以被运行,这个就是并发;

1.4进程的切换

函数的返回值是怎么被外部拿到的:通过CPU里面的寄存器被我们的外部得到的;

我们的系统如何知道进程执行到了哪一行代码:通过程序计数器eip来记录当前进程的正在执行的指令的下一行的指令的地址;

通用寄存器:eax,ebx;                   状态寄存器:status

在我们的CPU里面,一定会存在很多的寄存器,我们的CPU里面的寄存器扮演的角色是什么:寄存器也具有对于数据的保存能力,把进程的高频的内容数据放到寄存器里面去,方便我们的CPU管理,快速地找到我们想要的数据;

CPU寄存器保存的是进程的相关的信息,官方称呼:上下文数据(实际上就是我们的进程相关的信息数据);

我们的进程从CPU上面离开的时候,这个相关的上下文数据需要被保存甚至带走,进程在被切换的时候,回来的时候,就可以直接把自己的数据放上去,这样就可以直接运行了,总之就是我们的进程切换的时候会执行上下文的切换,方便自己下次被调用的时候可以实现无缝衔接

2.环境变量

2.1环境变量简介

获取环境变量的指令:

我们自己可以去创建环境变量,但是我们自己创建的环境变量都是在我们的本地,我们使用这个echo指令可以进行查看,可以发现这个创建是成功的,但是我们使用上面的env指令进行查看的时候发现这个里面并没有我们自己创建的环境变量;

这个时候,如果我们想要看到自己创建的环境变量,就只需要创建的时候在前面加上export就可以了,这个时候我们在使用env查看的时候就会发现这个我们自己创建的环境变量了;

我们可以手动的取消对于环境变量的相关设置,我么可以使用unset指令把我们的自己创建的环境变量删除掉,再使用env进行查看就发现没有了;

2.2命令行参数

环境变量为软件,指令,工具提供命令行选项的支持;

进程启动,不仅仅只是把这个进程加载到内存里面去,还需要命令行参数,系统环境变量表都会被传进来给我们的进程;

我们运行的进程都是子进程,bash本身在启动的时候会从操作系统的配置文件里面读取环境变量信息,子进程会继承父进程交给我们的环境变量;

3.程序地址空间

3.1地址空间简介

我们的程序存储区分为代码区,字符常量区,全局数据区,堆区,栈区等等几个部分,地址是有低地址到高地址进行增长的,我们把这个叫做地址空间;

在栈区定义的变量,这个先定义先入栈,后定义的变量后入栈,因此这个后定义的变量的地址会更大,这个栈区是向下增长,同理这个,堆区是向上增长的;堆区和栈区相对而生;

static修饰的局部变量编译的时候,已经被搞到全局数据区里面了,这个局部变量的生命周期就是一个全局变量的,因此随着我们的函数的调用,这个static修饰的局部变量的数值不会发生改变;

3.2虚拟地址

我们可以看到这个上面的实验,刚开始的这个打印的g_val是100,后来这个对应的是200,但是这个变换之后这个地址是不变的,怎么可能一个变量一个地址,但是这个变量的数值却不一样,这个就是因为这个打印的结果不是真实的物理地址,而是我们的虚拟地址

3.3页表概念的引入

页表是一张显示这个虚拟地址和真真实的物理地址的关系的映射表;

我们的这个父进程和子进程都有自己的进程地址空间,子进程的进程地址空间 是对于这个父进程的一个拷贝,所有的信息都是一样的,相当于就是一份拷贝;

但是当我们对于这个父进程里面的变量进行修改的时候(子进程会和父进程共享代码和数据)因此这个父进程的变量在子进程里面也是存在的,但是因为这个进程具有独立性,因此这个会发生写实拷贝,这个物理地址会发生变化,开辟新的物理地址去存储这个修改后的变量的数值;

因此这个上面的打印结果,打印的是虚拟地址,这个虚拟地址子进程就是拷贝的父进程的,所以这个打印的结果是一样的,但是这个实际上的物理地址不是一样的;

3.4谈谈细节

到底什么是进程地址空间:数据总线排列组合形成的地址的范围[0,2^32);

进程地址空间实际上就是我们的进程的一个可以使用的范围,我们可以在这个区域上面进行区域的划分,存放各种数据;

进程地址空间在内核里面就是一个内核对象结构体,这个结构体里面有地址区域的起始位置的地址start和终止位置的地址end;

3.5进程地址空间管理

对于任何一个进程,都会创建一个task_struct结构体对象,这个指针指向我们的进程地址空间对象,这个里面就有我们的各种区域的划分,方便我们对于这个区域的管理;

3.6进程地址空间的存在意义

让所有的进程以一个统一的视角去看待内存,因为地址空间使得所有的进程都需要虚拟地址,子进程和父进程的关系,调度是一套统一的流程;

当我们访问内存的时候,会增加一个转换的过程,在这个转换的过程中,虚拟地址空间会进行审查,例如我们对于这个只读区域进行修改,显然是不符合要求的,这个时候的地址空间就会进行这个请求的拦截,防止其进入物理内存,在某些程度上面保护了物理内存;

3.7页表

CPU里面的这个cr3寄存器把这个进程的页表的起始地址进行管理和存储,因此我们进行进程切换的时候,不需要担心这个页表找不到的情况,因为我们会通过这个cr3寄存器找到我们的页表;

左侧的叫做进程管理,右侧的叫做内存管理,因为这个页表的存在在,这个虚拟地址空间和页表的存在,使得内存管理和进程管理解耦合(避免强耦合,强耦合的话就是关联性比较强,容易相互牵扯问题);

进程=内核数据结构PCB+进程地址空间+页表+数据和代码,每一个进程都可以有一整套这个东西;

进程具有对立性:首先就是这个不同的进程对应的这个PCB是不一样的,其次就是这个物理地址部分,子进程和父进程开辟空间的地址是不一样的;

我们创建一个进程之后,首先要做的事情就是去创建内核数据结构,说白了就是去把这个虚拟地址空间,页表和物理内存这个框架去搭建起来,然后根据我们的这个程序的运行情况去惰性加载内存,不会一次性全部提供的,而是当我们的程序去执行某一个地方的时候,我们的物理地址空间才会被开辟,经由我们的页表的权限审核,确定这个是否要在我们的物理地址上面开辟空间;

因此这个里面存在缺页中断,就是这个虚拟地址空间没有对应的物理地址空间,就是这个页表上面的虚拟地址数量大于这个物理地址数量,就是因为这个物理空间不会一次性全部开辟,而是进行的惰性加载;实际上,我们之前介绍的这个写实拷贝,就是我们的这个子进程和父进程共享数据和代码,当我们需要对于这个子进程的数据进行修改的时候,这个因为进程的独立性,才会让这个操作系统重新开辟内存空间,方便对于这个修改的数据进行存放,从而不会影响这个父进程的代码和数据的原始值,这个实际上就是缺页中断的原理;

还有一点就是我们之前学习这个C语言的时候说的是这个代码是只读的,常量区是只读的,这个就是因为我们的页表里面有权限标识符,就是rw之类的,当我们的虚拟地址想要去开辟空间的时候,就回去经过这个页表的验证,符合这个权限要求之后才回去到对应的物理内存上面开辟空间;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值