Linux进程概念—02
1.进程地址空间
我们先通过一段代码来了解一下。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int g_val = 1;
int main()
{
pid_t pid = fork();
if(pid == 0)
{
g_val = 100;
printf("child %d, %p\n", g_val, &g_val);
}
else if(pid > 0)
{
printf("parent %d, %p\n", g_val, &g_val);
}
else
{
printf("fork\n");
}
sleep(2);
return 0;
}
这段代码的运行结果为:
child 100, 0x80497d8
parent 1, 0x80497d8
一个全局变量,在子进程中修改后打印的是100,父进程中依然打印1。他们数据不同,表示肯定不是共用同一块空间,但是输出结果却显示是同一块空间。
这是因为进程中访问的地址都是虚拟地址,而我们所说的程序地址空间实际上是一个进程的虚拟地址空间。
虚拟地址空间每一个进程一份,因此虚拟地址空间可以随意使用。
虚拟地址空间原理
虚拟地址空间其实就是一个mm_struct结构体。是一个对内存空间的描述,通过这个描述向进程虚拟出一个完整的、连续的内的空间。
操作系统为进程创建一个虚拟地址空间的同时,也创建了一个页表用于映射虚拟地址与物理地址的关系。
虚拟地址空间好处
进程如果直接方法物理内存,进程中代码数据的使用都是连续的地址,若直接使用物理内存,会造成内存浪费。直接访问物理内存会因为缺乏内存访问控制导致进程的不安全。
进程使用虚拟地址空间,通过页表映射物理内存,可以实现进程中数据在物理内存上的离散式存储。通过这种方法提高了内存利用率。在页表中可以直接针对某个地址设置访问权限,实现内存的访问控制。进一提高了进程的独立性。
2.内存管理方式
分页式:页号+页内偏移
页号:页表中页表项的编号。
页内偏移:具体一个变量首地址相较于内存页起始位置的偏移量。
物理内存块号*物理内存块大小+虚拟地址中的页内偏移=物理地址
假设内存大小为4G,页大小4096kb,有一个虚拟地址十进制表示为:4528,页表中页号为1的块号为2,计算物理地址。
4096kb为一页,内存页个数=4G/4096kb=2^20。说明虚拟地址的高20位都是页号。低12位都是页内偏移。
4528二进制表示为0000 0000 0000 0000 0001 0001 1011 0000
得知页号为1,页内偏移为432。
物理地址=2*4096+432=8624。
分页式内存管理的优点:将物理内存进行分块管理,通过页表映射实现数据在物理内存上的离散式存储,提高内存利用率。
分段式:段号+段内偏移
段表项的组成=段号+段长+本段在内存中的起始地址。
分段式内存管理的优点:对内存的管理更加方便,将内存空间分为了代码段,初始化全局段…;什么变量就在什么段申请地址。
段页式:段号+段内页号+页内偏移
先分段,在每一个分段内采用分页。每一个分段都有一个页表,通过地址中的段号找到段表项,通过段表项中段内页表其实地址找到自己的页表。通过地址中的段内页号,在这个页表中找到页表项,通过页表项中的物理块号+页内偏移得到最终的物理地址。
段页式集合了分页式与分段式的优点。
3.缺页中断
假设内存只有4G,运行的程序的数据和代码都是在内存中,意味着运行的程序多了,有可能内存就不够用了。
磁盘的分区有两种:交换分区/文件系统分区。
交换分区:作为交换内存使用。
当内存不够用的时候,操作系统会根据一定的算法,将某块内存中的数据保存到磁盘的交换分区中,空出这块内存重新加载数据。
但是每个进程的页表中记录了每一个虚拟地址对于的物理地址。如果某个虚拟页面的物理内存中的数据被交换出去了,保存到交换分区,则将这个页表项置为缺页中断。
等待下次这个进程要访问这个被交换出去的数据的时候,触发缺页中断,重新从交换分区将数据换回来。
在内存不够的时候,什么样的数据会被交换出去?
LRU算法:最久未使用–内存中最久没有被访问过的数据。