目录
虚拟地址空间
为什么要有虚拟内存?
认知这张图吗?之前说这是计算机物理内存的数据分布图
一个进程运行时需要将数据保存到内存中,如果每个进程运行时都直接控制物理内存地址,那就会造成:
- 不同进程访问同一块物理空间,数据安全不可控;
- 对于有限的物理内存,同时有大量进程时每个进程只能占用一小部分空间;
- 进程无法直接自己分配空间,因为并不能确定某一块空间其他进程是否正在使用;
- OS也无法直接为每个进程分配空间,因为进程开始前OS也不确定其具体需要多少空间;
因此OS为了更方便、更安全的为进程分配物流内存,就有了虚拟内存机制
什么是虚拟内存
虚拟内存就是一种OS内存管理的概念,可以说上面的内存分布图其实是虚拟内存的分布,而显示物理内存的分布更为复杂。
对于每个进程来说:
- OS为每一个进程都虚拟出一个4G的虚拟内存地址空间(32位);
- 进程的数据还是存放在真实的物理内存中,且真实物理内存是有限的(至少4G 32位)
- 物理地址的数据存放并不是连续的,且虚拟地址与物理地址并不是一一对应,一般一个进程开辟的虚拟内存实际使用大小远小于真实大小;
- 程序不能直接访问真实的物理内存地址空间,都是通过虚拟地址与物理地址的转换(OS转换)
子进程创建 && 写时拷贝原理
fork()函数
与父子进程的概念息息相关,如果不了解父子进程,可以看看这个:进程概念——父子进程、僵尸进程和孤儿进程
进程创建接口函数fork()
fork()函数是系统调用函数
fork()函数调用的内部情况
1. 通过fork()函数观察虚拟内存机制
从之前 父子进程 的例子中就看到,即使父进程拷贝出的子进程中两者变量地址相同,但两进程互相并不干扰:
原因就是进程对数据的操作是发生在虚拟内存上的,不同进程有不同的虚拟空间和映射表
2. fork()函数采用写时拷贝
什么是写时拷贝?
- 一个进程除了自己的PCB进程信息,还有运行时的内存数据信息;
- 子进程是在父进程运行过程中完全拷贝了父进程的PCB信息;
- 但对于内存数据,当子进程不修改内存数据时,父子进程完全共享同一块物理内存数据;一但父子进程某一方修改了内存数据,为了父子进程独立性,OS会为修改数据的进程新开辟一块内存并拷贝存放这个要修改的数据,这就是写时拷贝;
字面理解:父子进程只有“写数据”时现实物理空间会发生拷贝,否则公用。
- 写时拷贝时OS为节省内存空间而实现的管理内存机制。
3. fork()函数的具体流程:
-
给子进程分配新的内核数据结构(PCB存放在内存的内核区),并拷贝父进程PCB信息;
-
子进程拷贝父进程页表,实现内存共享;
刚创建后,OS并没有为子进程开辟新的空间存放父进程的内存数据,父子进程共享物理内存数据;
如果不改变变量值,父子进程会一直共享一块数据
一旦改变变量值,才以写时拷贝的方式拷贝一份(并更新页表)。此时父子进程通过各自的页表,指向不同的物理地址(只读部分仍在物理内存上公用)
-
将子进程添加到系统进程追中(添加到双向链表中);
-
fork返回后,开始调度器(OS进行)调度;