操作系统
Linux常用指令
Linux 中查看进程运行状态的指令
查看进程运行状态:ps命令。(process status,提供了进程的一次性查看,所提供的查看结果是不动态连续的,如果想对进程时间监控,应该用top工具)
grep命令(global search regular expression and print out the line,全面检索正则表达式并把行打印出来)能使用正则表达式搜索文本,并把匹配的行打印出来。
ps -aux|grep PID
用来查看某PID进程状态。ps -aux 输出格式:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
ps的所有指令:
- ps a 显示现行终端机下的所有程序,包括其他用户的程序。
2)ps -A 显示所有程序。
3)ps c 列出程序时,显示每个程序真正的指令名称,而不包含路径,参数或常驻服务的标示。
4)ps -e 此参数的效果和指定”A”参数相同。
5)ps e 列出程序时,显示每个程序所使用的环境变量。
6)ps f 用ASCII字符显示树状结构,表达程序间的相互关系。
7)ps -H 显示树状结构,表示程序间的相互关系。
8)ps -N 显示所有的程序,除了执行ps指令终端机下的程序之外。
9)ps s 采用程序信号的格式显示程序状况。
10)ps S 列出程序时,包括已中断的子程序资料。
11)ps -t 指定终端机编号,并列出属于该终端机的程序的状况。
12)ps u 以用户为主的格式来显示程序状况。
13)ps x 显示所有程序,不以终端机来区分。
- ps a 显示现行终端机下的所有程序,包括其他用户的程序。
查看内存使用情况:free命令
如果加上 -h 选项,输出的结果会友好很多:
有时我们需要持续的观察内存的状况,此时可以使用 -s 选项并指定间隔的秒数:
1
$ free -h -s 3
上面的命令每隔 3 秒输出一次内存的使用情况,直到你按下 ctrl + c。
tar 解压文件的参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 五个命令中必选一个
-c:建立压缩
-x:解压
-t:查看内容
-r:向压缩归档文件末尾追加文件
-u:更新原压缩包中的文件
# 这几个参数是可选的
-z:有 gzip 属性的
-j:有 bz2 属性的
-Z:有 compress 属性的
-v:显示所有过程
-o:将文件解开到标准输出
# -f参数是必须的
压缩:tar –cvf jpg.tar *.jpg //将目录里所有jpg文件打包成tar.jpg
tar –czf jpg.tar.gz *.jpg //将目录里所有jpg文件打包成jpg.tar后,并且将其用gzip压缩,生成一个gzip压缩过的包,命名为jpg.tar.gz
解压:tar –xvf file.tar //解压 tar包
tar -xzvf file.tar.gz //解压tar.gz
如何修改文件权限
① 文件权限描述
Linux中的基本权限有9个,分别是owner/group/others三种身份各有自己的read/write/execute权限
② 修改权限指令: chomd
③ chmod语法:
1
2
3
4
5
6chmod [-R] xyz 文件或目录
选项与参数:
xyz: 就是数字模型的权限属性,为 rwx 属性数值的相加。
-R:进行递归(recursive)的持续变更,亦即连同次目录下的所有文件都会变更
例:
chmod 770 dy.c # 即修改 dy.c 文件的权限为 770如何以root权限运行某个程序
su:(switch user 切换用户)
sudo:
1
2sudo chown root app(文件名)
sudo chmod u+s app(文件名)
进程与线程
进程的状态
上图interrupt的箭头方向反了。
创建状态(new)
进程正在被创建,还未到就绪状态
就绪状态 (ready)
进程已经处于准备运行状态,即进程已经获得了除了处理器之外的一切所需资源,一旦得到处理器资源即可运行。
运行状态(running)
进程正在处理器上运行
阻塞状态(waiting)
进程正在等待某一事件而暂停运行如等待某资源为可用或者等待IO操作完成。即使处理器空闲,这个进程也不能运行。
结束状态(terminated)
进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运行。
进程间的通信方式(7种)
每个进程有不同的用户地址空间,任何一个进程的全局变量其他进程都看不到,进程之间的数据交换必须依赖内核,进程1将数据拷贝到内核缓冲区,然后进程2从内核缓冲区将数据读走,内核提供的这种机制叫做进程间通信(IPC)。
管道/匿名管道(pipe)
管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。
只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
总结:匿名,只能在有亲缘关系的进程之间通信,半双工,存在于内存,当在一端写数据时必须确定另一端有进程
有名管道(FIFO)
提供了一个路径名与之关联,以有名管道的文件形式存在于文件系统中,那么即使与有名管道不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过有名管道相互通信。
总结:有名管道以文件的形式存储在磁盘上,进程可以通过访问路径名来与创建线程进行通信,但是由于管道是一种特殊的文件,因此使用前需要用open()打开,如果以读写方式(O_RDWR)打开则一定不会阻塞;以只读(O_RDONLY)方式打开时,调用open()的函数会被阻塞直到其他某个进程为写而打开它为止;如果以只写方式(O_WRONLY)打开时同样也会被阻塞,直到有以读方式打开该管道
信号
信号的生命周期:
① 信号被某个进程产生,并设置此信号传递的对象(一般为对应进程的PID),并传递给操作系统
② 操作系统判断PID所对应的进程是否被阻塞,若阻塞,就先保留该信号,一旦不阻塞,传递给对应进程
③ 对应进程接收到信号后,保存上下文,暂停当前代码的执行,执行对应的中断服务程序
消息队列
存在于内核中的消息链表,在读写数据时,不需要另外某个进程在队列上等待消息的到达。
共享内存
多个进程通过读写同一内存进行通信。每个进程都有属于自己的进程控制块和地址空间,并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。
当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存,这块内存可以被两个进程同时看到。这样当一个进程进行写操作,另一个进程读操作就可以实现进程间通信。但是,我们要确保一个进程在写的时候不能被读,因此我们使用信号量来实现同步与互斥。
信号量
信号量是用来解决进程之间的同步与互斥问题的一种进程之间的通信机制,包括一个称为信号量的变量和该信号量下等待资源的进程等待队列,以及对信号量进行的两个原子操作。信号量对应于某一种资源,取一个非负的整形值。信号量的值是指当前可用的资源数量。
由于信号量只有两种操作,一种是等待信号,另一种是发送信号。即P和V,它们的行为如下:
- P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行。
- V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1。
套接字
套接字是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
线程间的同步方式
线程同步是两个或者多个共享关键资源的线程的并发执行。(共享是一个动词)
互斥量
采用互斥对象机制,只有拥有互斥对象的线程可以拥有访问公共资源的权限。因为互斥对象一般只有一个,就能保证公共资源不会被多个线程同时访问,例如Java的synchronized和各种Lock都是采用这种机制。
信号量
允许同一时刻多个线程访问统一资源,但是会控制统一时刻访问资源的最大线程数量,由信号量变量(sv)确定。
事件
wait/notify 通过通知操作的方式来保证多线程同步。事件一般用来控制线程的先后顺序。
事件被分为:手动置位和自动置位。手动置位是同时向所有该事件的等待线程发信号通知,某一操作已经做完了,使他们都处于有信号的状态,都成为可调度线程;自动置位是向某一线程发信号,使它为有信号状态,成为可调度线程。
进程调度
进程调度是指操作系统按照某种策略或规则选择进程占用CPU进行运行的过程。
调度的时机(什么时候会发生进程调度?)
- 正在执行的进程执行完毕
- 执行中的进程因提出IO请求或者发出等事件而暂停执行
- 时间片完成
- 高优先者进入
调度的方式
先来先服务(FCFS)调度算法
从就绪队列中选择最先进入队列的进程为之分配资源
短作业优先的调度算法
从就绪队列中选择一个估计运行时间最短的进程为之分配资源
时间片轮转调度算法
多级反馈队列调度算法
优先级调度算法
① 高优先权优先调度算法
② 高响应比优先调度算法 优先级=(服务时间+等待时间)/服务时间=1+等待时间/服务时间
等待时间一定时,服务时间越短,优先级越高,有利于短作业
服务时间一定时,等待时间越长,优先级越高,有利于先到先服务
对于长作业,作业的优先级会随着等待时间的增加而增加,因此该算法既照顾了短作业,又考虑了作业到达的先后顺序,不会使长作业长时间得不到响应。
死锁
多个进程/线程同时被阻塞,他们其中的一个或者全部都在等待某个资源被释放。由于进程/线程被无限期的阻塞,因此程序不可能正常终止。
死锁产生的必要条件
- 互斥:资源必须在一个时间只能有一个进程可以使用。
- 占有并等待:一个进程至少占有一个资源并等待另一资源,而该资源被其他进程所占有
- 非抢占:资源不能被抢占,只有等持有该资源的进程执行完任务后,资源才可以被释放
- 循环等待:一组等待进程,循环占有资源并等待其他线程释放资源
解决死锁的方法
预防:限制并发进程对资源的请求
一般通过破坏2和4来预防死锁。
静态分配策略破坏第二个条件(占有并等待)。在执行前获取到该进程所需要的所有资源。
层次分配策略破坏第四个条件(循环等待)。一个进程得到某个资源后,只能再申请更高一级的资源;而在释放的时候也要先释放较高层的资源。
避免:对资源的使用情况做出预测,避免死锁产生
银行家算法:当一个进程申请使用资源的时候,银行家算法先试探分配给该进程资源,然后通过安全性算法判断分配后系统是否处于安全状态
(预测分配后系统是否安全)
检测:检测死锁的发生,精确的确定与死锁有关的进程和资源
系统定时运行“死锁检测”程序,判断系统是否发生了死锁,如果发生了再去解除它。
如果进程—资源图无环路,未发生死锁;有环路而且每个资源只有一份,发生死锁;有环路但有资源有多份,要具体分析。
解除:将进程从死锁状态下解脱出来
- 立即结束所有的进程,重新启动操作系统
- 撤销涉及死锁的进程,解除死锁后继续运行。但是这种方法也会付出很大的代价,可能其中的一些进程已经计算了很长时间。
- 逐个撤销涉及死锁的进程,直到解除死锁
- 抢占资源
内存管理
内存管理(Memory Management)是操作系统设计中最重要和最复杂的内容之一。虽然计算机硬件一直在飞速发展,内存容量也在不断增长,但是仍然不可能将所有用户进程和系统所需要的全部程序和数据放入主存中,所以操作系统必须将内存空间进行合理地划分和有效地动态分配。操作系统对内存的划分和动态分配,就是内存管理的概念。
内存管理功能
内存的分配与回收:malloc、free 创建进程后为他们分配内存空间,当结束后内存空间也会被回收
地址转换:将程序中的逻辑地址转换为内存中的物理地址
内存空间的扩充:利用虚拟存储技术或自动覆盖技术,从逻辑上扩充内存
覆盖技术:解决程序大小超过物理内存总和的问题,内存中分为一个“固定区”和若干个“覆盖区”,需要常驻内存的段放在“固定区”中,调入后就不再调出(除非运行结束)
将程序分为多个段(多个模块)常用的段常驻内存,不常用的段在需要时调入内存。
交换技术:
设计思想:内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)
中级调度(内存调度),就是要决定将哪个处于挂起状态的进程重新调入内存。
暂时换出外存等待的进程状态为挂起状态(挂起态, suspend)挂起态又可以进一步细分为就绪挂起、阻塞挂起两种状态存储保护:保证各个作业在自己的内存中运行,互不干扰
(内存保护)内存分配前,需要保护操作系统不受用户进程的影响,同时保护用户进程不受其他用户进程的影响。通过釆用重定位寄存器和界地址寄存器来实现这种保护。重定位寄存器含最小的物理地址值,界地址寄存器含逻辑地址值。每个逻辑地址值必须小于界地址寄存器;内存管理机构动态地将逻辑地址与界地址寄存器进行比较,如果未发生地址越界,则加上重定位寄存器的值后映射成物理地址,再送交内存单元。每一个逻辑地址都需要与这两个寄存器进行核对,以保证操作系统和其他用户程序及数据不被该进程的运行所影响。
逻辑地址和物理地址
逻辑地址:CPU所生成的地址。逻辑地址是内部和编程使用的、并不唯一。例如,你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于当前进程数据段的地址(偏移地址),不和绝对物理地址相干。
物理地址:加载到内存地址寄存器中的地址,内存单元的真正地址。在前端总线上传输的内存地址都是物理内存地址,编号从0开始一直到可用物理内存的最高端。这些数字被北桥(Nortbridge chip)映射到实际的内存条上。物理地址是明确的、最终用在总线上的编号,不必转换,不必分页,也没有特权级检查(no translation, no paging, no privilege checks)。
物理地址=重定位寄存器中的地址+逻辑地址
内存管理机制
连续分配管理方式
单一连续分配
内存分为系统区和用户区,系统区提供给操作系统使用,通常在低地址部分;用户区提供给用户使用,是内存中除系统区之外的内存空间。这种方式无需进行内存保护。
优点:简单、不会产生外部碎片;
缺点:只能用于单用户、单任务的操作系统中,有内部碎片,存储器的效率极低。
固定分区分配
将用户内存空间划分为若干个固定大小的区域,每个区域装入一道作业。当有空闲分区后,就可以从外存的后备作业队列中,选择适当大小的作业装入该分区,如此循环。
该分配方式分为两种方法:
- 分区大小相等
- 分区大小不等
特点:可能程序太大放不进任何一个分区内,需要使用覆盖技术进行扩容。会产生内部碎片
动态分区分配
这种分区方法不预先将内存划分,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统中分区的大小和数目是可变的。动态分区在开始分配时是很好的,但是之后会导致内存中出现许多小的内存块,产生外部碎片。
非连续分配管理方式
分页存储管理
分页存储管理方式中,又根据运行作业时是否要把作业的所有页面都装入内存才能运行分为基本分页存储管理方式和请求分页存储管理方式。
固定分区会产生内部碎片,动态分区会产生外部碎片,这两种技术对内存的利用率都比较低。我们希望内存的使用能尽量避免碎片的产生。
这就引入了分页的思想:把主存空间划分为大小相等且固定的块,块相对较小,作为主存的基本单位。每个进程也以块为单位进行划分,进程在执行时,以块为单位逐个申请主存中的块空间。
由于块很小,这样只会在为最后一个不完整的进程块分配内存时会产生内部碎片,这种碎片相对于进程来说是非常小的。
基本概念
内存中的块称为页框,进程中的块称为页。进程在执行时需要申请内存时,为每一页匹配一个页框。为方便地址转换,页面的大小应为2的整数幂。同时页面大小应适中,如果页面太小,每个进程的页面数量就会增加,那么页表就过长,占用大量内存,而且会增加逻辑地址到物理地址转换的开销。页面过大又会使页内碎片增大,降低内存使用率。
按字节编址,假设用来存放页的地址长度为32位(即进程有4G),其中0-11位为页内地址,因此每页大小为4kB;12-31为页号,地址空间最多允许有2^20页。
页表
我们对数据页进行编址时,每页大小为4kB,而我们应该对每页都设置一个页表中的页表项与之对应,那么也就是一共有2^20个页表项。
页表项:在页表中,一个页号以及与其对应的物理块号称之为一个页表项。(红框框起来的部分)
页表项大小为4B,也就是32位,其中页号占20位,那么物理块号应该占12位,也就是页框号有2^12个。我们可以发现页框号的数目要小于页数,说明划分进程为页,每一页都有自己的代号,但调入内存时并不是把全部进程都调入。
我们知道页表项大小为4B,数量为2^20个,那么页表占用的内存等于4B*2^20=4MB,并且这4MB的内存必须连续。但是对于内存来说4M已经是一个不小的内存,没必要让所有的页表项都存储在内存中,因为进程在一段时间内只会访问某些特定的进程页。(后面引入二级页表的概念,在后面会提到)
地址变换机构
系统中一般会设置一个页表寄存器,其中存放页表的起始地址F以及页表长度M(即该进程一共有多少页)。当进程未执行时,页表的起始位置和页表长度存放在进程控制块中,进程执行时,才将页表起始位置和长度存入页表寄存器。设页面大小为L,逻辑地址A到物理地址E的转换过程如下:
- 计算页号P=A/L和页内偏移量W=A%L;
- 判断页号P和页表长度M的大小,如果P>=M,说明越界,就产生越界中断;否则继续执行;
- 页表中页号P对应的页表项地址=页表起始地址F+页号P*页表项长度,取出页表项中的物理块号b。
- 计算E=b*L+W,用得到的物理地址E去访问内存。
分页存储管理方式存在的两个问题
每个访问操作都涉及到逻辑地址到物理地址的转换,因此地址转换要足够快,否则访问速度会降低。
具有快表的地址变换机构
根据上面介绍的地址变换过程可知,若页表全部存在内存中,则存取一个数据或者一条指令至少要访问两次内存:一次时访问页表,确定所存取的数据或指令的物理地址,第二次才根据地址存取数据和指令。
为此,在地址变换机构中增设了一个具有并行查找能力的高速缓冲存储器—快表(TLB),用来存放当前访问的若干页表项,以加快地址变换的过程。而与此对应,内存中的页表一般称为慢表。
在具有快表的分页机制中,地址的变换过程为:
- CPU给出逻辑地址后,由硬件进行地址转换并将页号送入高速缓存寄存器,并将页号和快表中的所有页号做比较
- 如果找到了匹配的页号,说明访问的页表项在快表中,就直接从快表中取出该页对应的页框号,与页内偏移量拼接形成物理地址,这样数据仅通过一次访存便可实现。
- 如果没有找到,就需要访问主存中的页表,在读出页表项之后,同时将其存到快表中,以便后面有可能的再次访问。但若快表已满,则必须按照一定的算法对旧的页表项进行替换。
一般快表的命中率可以达到90%以上,这样,分页带来的速度损失就降低到10%以下。快表主要利用了局部性原理。
每个进程引入了页表,用于存储映射机制,页表不能太大,否则内存利用率会降低。(时间换空间)
由于引入了分页管理,进程在执行的过程中不需要将所有页都调入内存页框中,而只要将保存有映射关系的页表调入内存即可。但我们仍然需要考虑页表的大小。
举例:逻辑地址有32位,页面大小为4kB,页表项大小为
,那么该进程的页表大小为4B*2^20,约100w个页表项,每个进程的页表大小为4M,这明显不符合实际条件。而即使不考虑对全部逻辑地址空间进行映射的情况,一个逻辑地址空间稍大的进程,其页表大小也可能是过大的。
再假设一个40Mb的进程,页表项也就有40kb,如果将所有的页表项内容全部保存在内存中,那么需要10个内存页框来保存(一个页框保存4kB)。整个进程大约有1w个页面,而在实际执行中只需要几十个页面进入内存页框就可以运行,但如果把10个页面大小的页框全部装入内存,这相对实际执行时的几十个进程页面的大小来说,降低了内存利用率。从另一方面来说,这10页的页表项也并不需要同时保存在内存中,大多数情况下,映射所需要的页表项都在页表的同一页面中。
将页表映射的思想进一步延伸,就可以得到二级分页:将页表的10页空间也进行地址映射,建立上一级页表,用于存储页表的映射关系。这里对页表的10个页面进行映射只需要10个页表项,所以上一级页表只需要1页就足够,可以存储2^10=1024个页表项。在进程执行时,只需要将这个上一级页表调入内存即可,进程的页表和进程本身的页面,可以在后面的执行中再调入内存。
在32位系统中,全部32位逻辑地址空间,按字节编码,每页2^12字节,也就是4kB,共有2^20个页面,这些页面也就是会有2^20个页表项,每个页表项是32bit,也就是4B。我们知道一个页面可以放置4kB的数据,也就是1个页面可以存放2^10个页表项,那么一共需要2^10个页面去存放这些页表项,我们就可以为这2^10个页面构建上级页表,也就恰好是一个页面就可以存放。
举例:32位系统中进程分页的工作过程:假定内核已经给一个正在运行的进程分配的逻辑地址空间是0x20000000到0x2003FFFF,这个空间由64个页面(0x00-0x3F)组成。在进程运行时,我们不需要知道全部这些页的页框的物理地址,很可能其中很多页还不在主存中。这里我们只注意在进程运行到某一页时,硬件是如何计算得到这一页的页框的物理地址即可。现在进程需要读逻辑地址0x20021406中的字节内容,这个逻辑地址按如下进行处理:
逻辑地址: 0x20021406 (0010 0000 0000 0010 0001 0100 0000 0110 B)
顶级页表字段:0x80 (00 1000 0000 B)
二级页表字段:0x21 (00 0010 0001B)
页内偏移量字段:0x406 (0100 0000 0110 B)
分段存储管理
段式管理方式按照用户进程中的自然段划分逻辑空间。例如,用户进程由主程序、两个子程序、栈和一段数据组成,于是可以把这个用户进程划分为5个段,每段从0开始编址,并分配一段连续的地址空间(段内要求连续,段间不要求连续,因此整个作业的地址空间是二维的)。其逻辑地址由段号S与段内偏移量W两部分组成。
如图,段号为16位,段内偏移量为16位,则一个作业最多可有2^16=65536个段,最大段长为64KB。
在页式系统中,逻辑地址的页号和页内偏移量对用户是透明的,但在段式系统中,段号和段内偏移量必须由用户显示提供,在高级程序设计语言中,这个工作由编译程序完成。
段表
每个进程都有一张逻辑空间与内存空间映射的段表,其中每一个段表项对应进程的一个段,段表项记录该段在内存中的起始地址和段的长度。段表的内容如图所示。
在配置了段表后,执行中的进程可以通过查找段表,找到每个段所对应的内存区。
段页式存储管理
页式存储管理能有效地提高内存利用率,而分段存储管理能反映程序的逻辑结构并有利于段的共享。如果将这两种存储管理方法结合起来,就形成了段页式存储管理方式。
在段页式系统中,作业的地址空间首先被分成若干个逻辑段,每段都有自己的段号,然后再将每一段分成若干个大小固定的页。对内存空间的管理仍然和分页存储管理一样,将其分成若干个和页面大小相同的存储块,对内存的分配以存储块为单位。
为了实现地址变换,系统为每个进程建立一张段表,而每个分段有一张页表。段表表项中至少包括段号、页表长度和页表起始地址,页表表项中至少包括页号和块号。
分页和分段机制的共同点和区别
- 共同点
- 分页机制和分段机制都是为了提高内存利用率,减少内存碎片。
- 页和段都是离散存储的,所以两者都是离散分配内存的方式。但是,每个页和段中的内存是连续的。
- 区别
- 页的大小是固定的,由操作系统决定;而段的大小不固定,取决于我们当前运行的程序。
- 分页仅仅是为了满足操作系统内存管理的需求,而段是逻辑信息的单位,在程序中可以体现为代码段,数据段,能够更好满足用户的需要。
CPU寻址
每个CPU的寻址能力是要看其地址线的数量,32位CPU一般有32根地址总线,那么就一共可以寻232个地址=也就是4x1024x1024x1024=4G个地址,1个地址对应1字节的存储单位,对应到内存上就是4GB(4GByte)
寻址方式:现代的处理器使用的都是一种叫做虚拟地址寻址的寻址方式。
内存管理单元(MMU):CPU内部的一个硬件,负责完成虚拟地址到物理地址的转换。
TLB( Translation Look- aside buffer)专门用于缓存内存中的页表项,一般在MMU单元内部。
为什么要使用虚拟地址空间?
如果不使用虚拟地址空间,程序直接访问和操作的都是物理内存。
- 用户程序可以访问任意内存,寻址内存的每一个字节,这样就有意无意地破坏操作系统,造成操作系统崩溃。
- 想要同时运行多个程序特别困难,例如想同时运行一个微信和一个QQ音乐都不行。例如:微信在运行的时候给内存地址1xxx赋值,QQ音乐也同样给内存地址1xxx赋值,那么QQ音乐对内存的赋值就会覆盖掉微信之前所赋的值,这就造成了微信程序的崩溃。
使用虚拟地址访问内存的优势
- 程序可以使用相邻的虚拟地址访问物理内存中不相邻的大物理缓冲区。
- 程序可以使用虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页保存到磁盘中。数据页和代码页会根据需要在内存和磁盘之间移动。
- 不同进程之间的虚拟地址相互隔离。一个进程中的代码无法更改另一个进程正在使用的物理内存。
虚拟内存
虚拟内存和虚拟地址空间
前面提到了虚拟地址,实际上虚拟地址空间和虚拟内存并不是一个概念。
虚拟内存
虚拟内存是在磁盘上划分出一块空间由操作系统管理,当内存耗尽时充当内存来用。
虚拟地址空间
操作系统会给每个进程分配一个虚拟地址空间(vitural address),每个进程包含的栈、堆、代码段这些都会从这个地址空间中被分配一个地址,这个地址就被称为虚拟地址。MMU会将虚拟地址转换为物理地址。
CPU在寻址的时候,按照虚拟地址来寻址,通过MMU将虚拟地址转换为物理地址。因为只有程序的一部分加入了内存中,那么就会出现所寻找的地址不在内存中的情况。如果在内存不足的情况下,就会通过页面调度算法来将内存中的页面置换出来,将外存中的页面加入到内存中,使程序继续正常运行。
页面置换算法
- OPT页面置换算法(最佳页面置换算法):所选择的被淘汰页面将是以后永不使用的,或者是在长时间内不再被访问的,这样可以保证最低的缺页率。
- FIFO页面置换算法(先进先出页面置换算法):总先淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰。
- LRU页面置换算法(最近最久未使用页面置换算法)
- LFU页面置换算法(最少使用页面置换算法)