什么是地址空间?
在Linux系统下的多个进程,都会有如上的虚拟地址空间,并且是各自独立的;其并不是真实的内存(即物理内存),单个进程的虚拟地址空间中又被划分为不同的区域。
进程能够看到的只是一块虚拟的、有着不同分区的连续内存块,其能看到的地址也只是虚拟地址空间的虚拟地址,通过页表结构构建起的虚拟地址与物理地址的映射关系,实现虚拟地址到物理地址的转换,从而对物理内存进行操作。
为什么要有地址空间?
- 保护内存
我们知道在C语言中有指针操作,如果没有虚拟地址空间,那么直接操作的就是物理地址,指针操作很容易误修改其他进程的数据,从而造成运行结果紊乱;如果误修改了操作系统的某些数据,甚至可能造成操作系统崩溃。 - 内存连续化处理
在很多情况下,一个进程中的多个数据的存储是不连续的,访问起来不方便,也增加了异常越界的概率。操作系统通过虚拟化地址空间,将实际不连续的物理内存连续化处理了,
Linux如何管理地址空间?
管理 = 描述 + 组织。地址空间在操作系统中本质是一个数据结构struc mm_struct,包含着划分区域的信息,例如区域的起始的物理地址和结束的物理地址。struct mm_struct会保存在PCB中,操作系统只要拿到某一进程的PCB,就能得到该进程对应的内存信息。
写时拷贝
通常,父子进程代码共享,其任意一方不写入时,数据也是共享的。但由于进程的独立性,当有任意一方对其中的数据进行修改时,试图写入方需要拷贝一份待修改数据的副本,并修改页表结构的映射,因此父子进程的写入数据所占有的物理内存是不同的,而只读数据仍共享;从而父子进程各自的运行互不干扰。
这样做的好处就是节省空间和时间。父子进程代码共享,如果二者都只对数据进行读操作,那就不会涉及到相互影响,不需要在子进程创建后直接拷贝新的数据,从而节省时间;
再者,并不是所有的数据都需要被拷贝,可能只有部分的数据是会被修改的,因此在某一部分的数据被试图写入时,只拷贝这部分的数据,从而节省空间。例如父子进程共享着10M的数据,而某一进程只需要写入1M的空间,那么此时只需要拷贝1M的空间。
运行队列?等待队列?
操作系统在管理进程时,实际上是管理由多个task_struct组成的数据结构,如双向链表,因此task_struct中必然包含着彼此之间大量的连接信息,用于调度和切换等。
- 运行队列(runqueue)
由多个处于R状态的进程PCB连接而成,等待被CPU调度 - 等待队列(waitqueue)
由多个处于D状态的进程PCB连接而成,处于D状态的进程通常在等待外部硬件的I/O而陷入深度睡眠状态,而被放入等待队列当中;当等待的磁盘或网卡可以被读取或者写入时 会唤醒等待队列中的进程,将PCB从waitqueue转到runqueue中去执行当前进程的读写代码。
|