【Linux】进程的概念--程序地址空间(2)
语言级别的地址
以前我们在学习C语言指针的时候,会打印地址,会有内存级别的内存。
观看一段代码
所以语言级别的地址,都是虚拟地址,并不是真实的物理地址。
打印一个是虚拟的,打印全部都是虚拟的,全部打印出来的这个空间,不是真实的内存。它叫虚拟地址空间。
在Linux地址下,这种地址叫做 虚拟地址 /线性地址
我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理
OS必须负责将 虚拟地址 转化成 物理地址
认识进程地址空间
进程在自己执行的时候,进程自己会认为自己独占系统资源(事实上并不是)。
举个例子:
所以 进程地址空间(虚拟地址空间)就是操作系统给每一个进程画的大饼。
操作系统画的饼(进程地址空间),也是需要被管理的。
操作系统管理的本质就是:先描述再管理。
所以操作系统内核就有一种数据结构 mm_struct来管理每个进程的进程地址空间。
mm_struct,能描述进程地址空间的每一个属性。
地址空间的本质就是:内核的一种数据结构(mm_struct)
mm_struct
以32位机器为例子
从虚拟地址要通过页表映射,找到物理地址,然后到内存中找对应的数据.
以上都是操作系统帮我们做的
虚拟地址都是连续的地址空间,所以虚拟地址也叫线性地址.
每个进程的都有自己的虚拟进程地址空间,都需要通过页表映射到真实的物理地址.
每个进程都看不到自己的物理内存,只知道自己的虚拟内存,然后操作系统通过页表映射到真实的物理地址,供进程调用数据.
为什么存在地址空间&&页表
4.1.变相保护操作系统
如果让进程直接访问物理内存,万一越界或者非法访问了怎么办呢?
所以直接让进程访问物理内存很不安全,万一修改到系统内存里面的数据咋办?
万一读取到系统内存的数据,是不是就隐私泄漏了呢?
所以页表不仅仅是用来映射物理地址的,还能检查地址访问是否合法.我们对物理内存做非法访问的时候就会被操作系统拦截.
所以虚拟地址空间和页表的存在是为了变相保护操作系统.
4.2.保证了进程独立性
上面我们写过一个代码,父进程创建了子进程,子进程一段时间后修改了全局变量的值,此时当我们去打印这个全局变量的值和对应的地址的时候,就会发现一个问题,为什么打印的地址一样而值却不一样?
此时我们就知道了,打印的这个地址是虚拟地址.是通过页表映射到物理地址的.
所以就很好的解释了为什么.打印的地址一样而值却不一样.
写时拷贝的问题
父进程创建子进程,可以认为子进程是父进程哪里拷贝过来的,一开始其实子进程和父进程的全局变量通过页表映射到的是同一块物理内存,但是当我们尝试写入或者修改的时候,操作系统就会在内存重新找一块内存空间,然后进行数据拷贝,更改页表映射,然后让进程进行修改.这种技术就叫做写时拷贝.是为了保证进程的独立性.两个进程互相不影响.
所以操作系统为保证进程的独立性,做了很多工作,通过地址空间,通过页表,让不同的进程,映射到不同的物理内存处.可以更加方便的进行进程和进程的数据代码解耦.保证进程独立性.
4.3.让编译代码和进程执行统一看待虚拟地址空间
当一个可执行程序在磁盘的时候,程序里面有地址吗? 有的
你不要认为只有操作系统会遵守虚拟地址空间,编译器也要遵守!
编译器在编译代码的时候,就是按照虚拟地址空间的方式对我们的代码和数据进行编址的!
所以我们在编写到代码的时候需要选择32位还是64位编译,编译出来的地址是4字节和8字节.这里32位和64位说的就是按照32位的虚拟地址空间还是64位的虚拟地址空间,进行代码和数据的编址.
逻辑地址还有其他的实现方案,所以 逻辑地址 != 虚拟地址
在Linux中:逻辑地址的实现方案和虚拟地址编址差不多,非常相似,所以有时候可以直接将逻辑地址直接填进页表的左边,当成虚拟地址.
总结:
虚拟进程的另一个作用:
让进程以统一的视角,来看待进程对应的代码和数据等各个区域,方便使用.
方便编译器也以统一的视角来进行编译代码.
因为规则是一样的.