虚拟内存管理
先弄清楚几个概念:
物理地址(Physical Address,PA)——CPU地址总线传来的地址,物理地址中很大一部分是留给内存条中的内存的
虚拟地址(Virtual Address,VA) / 线性地址(Linear Address)—— 是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。
逻辑地址(Logical Address) / 相对地址 —— 是在有地址变换功能的计算机中,访内指令给出的地址 (操作数) 叫逻辑地址,也就是是机器语言指令中,用来指定一个操作数或是一条指令的地址。要经过寻址方式的计算或变换才得到内存储器中的实际有效地址即物理地址。一个逻辑地址由两部分组成,段标识符:段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。
Linux中虚拟地址与物理地址之间的转换:
逻辑地址到物理地址的转换分为两步:
1)段转换(segment convert):逻辑地址转换为线性/虚拟地址; 2)页转换(page convert):线性/虚拟地址转换为物理地址。
段转换:首先根据segment selector找到global descriptor table(GDT)中对应的位置,找到描述符descriptor,每个descriptor项中有base、LIMIT、Type、DPL等,根据base找到该段的起始地址(线性地址中),然后加上offset,得到线性地址。
页转换:线性地址的前10位是page dircetory中的索引位置,page directory中存放了page table的物理地址,根据线性地址的第二个10位可以得其在page table中的索引位置,加上page table的物理地址,可以得到该地址的PTE。PTE中记录了该地址对应的物理地址。
虚拟内存管理可以控制物理内存的访问权限。物理内存本身是不限制访问的,任何地址都可以读写,而操作系统要求不同的页面具有不同的访问权限,这是利用CPU模式和MMU的内存保护机制实现的。例如Text Segment被只读保护起来,防止被错误的指令意外改写,内核地址空间也被保护起来,防止在用户模式下执行错误的指令意外改写内核数据。
虚拟内存管理最主要的作用是让每个进程都有独立的地址空间。所谓独立的地址空间是指,不同进程中的同一个VA被MMU(Memory Management Unit,内存管理单元)映射到不同的PA(Physical Address,物理地址),并且在某一个进程中访问任何地址都不可能访问到另外一个进程的数据。另一方面每个进程都认为自己独占整个虚拟地址空间,这样链接器和加载器的实现会比较容易,不必考虑各进程的地址范围是否冲突。
VA到PA的映射会给分配和释放内存带来方便,物理地址不连续的几块内存可以映射成虚拟地址连续的一块内存。比如要用 malloc 分配一块很大的内存空间,虽然有足够多的空闲物理内存,却没有足够大的连续空闲内存,这时就可以分配多个不连续的物理页面而映射到连续的虚拟地址范围。
Linux内核内存分配:https://www.cnblogs.com/clnchanpin/p/7267338.html
Windows虚拟内存管理:https://blog.youkuaiyun.com/lanuage/article/details/51959747
虚拟内存管理一些面试题:https://blog.youkuaiyun.com/yyf_it/article/details/52053775
换页算法
现代操作系统都使用分页来管理物理内存,intel80386之后一页大多数是4K。操作系统将磁盘的一部分划分出来,作为虚拟内存,由于内存的速度比磁盘快得多,所以操作系统按照某种换页机制将不需要的页换到磁盘中来,将需要的页调到内存中,这就叫做换页,或者可以说是系统换页。
1)最佳置换算法(OPT)(理想置换算法):从主存中移出永远不再需要的页面;如无这样的页面存在,则选择最长时间不需要访问的页面。于所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。
2)先进先出置换算法(FIFO):是最简单的页面置换算法。这种算法的基本思想是:当需要淘汰一个页面时,总是选择驻留主存时间最长的页面进行淘汰,即先进入主存的页面先淘汰。其理由是:最早调入主存的页面不再被使用的可能性最大。
3)最近最久未使用(LRU)算法:这种算法的基本思想是:利用局部性原理,根据一个作业在执行过程中过去的页面访问历史来推测未来的行为。它认为过去一段时间里不曾被访问过的页面,在最近的将来可能也不会再被访问。所以,这种算法的实质是:当需要淘汰一个页面时,总是选择在最近一段时间内最久不用的页面予以淘汰。
4)时钟(CLOCK)置换算法
LRU算法的性能接近于OPT,但是实现起来比较困难,且开销大;FIFO算法实现简单,但性能差。所以操作系统的设计者尝试了很多算法,试图用比较小的开销接近LRU的性能,这类算法都是CLOCK算法的变体。
简单的CLOCK算法是给每一帧关联一个附加位,称为使用位。当某一页首次装入主存时,该帧的使用位设置为1;当该页随后再被访问到时,它的使用位也被置为1。对于页替换算法,用于替换的候选帧集合看做一个循环缓冲区,并且有一个指针与之相关联。当某一页被替换时,该指针被设置成指向缓冲区中的下一帧。当需要替换一页时,操作系统扫描缓冲区,以查找使用位被置为0的一帧。每当遇到一个使用位为1的帧时,操作系统就将该位重新置为0;如果在这个过程开始时,缓冲区中所有帧的使用位均为0,则选择遇到的第一个帧替换;如果所有帧的使用位均为1,则指针在缓冲区中完整地循环一周,把所有使用位都置为0,并且停留在最初的位置上,替换该帧中的页。由于该算法循环地检查各页面的情况,故称为CLOCK算法,又称为最近未用(Not Recently Used, NRU)算法。
https://www.cnblogs.com/fkissx/p/4712959.html
进程间通信
进程的概念:
进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放。可以认为进程是一个程序的一次执行过程。
进程通信:
IPC(InterProcess Communification),每个进程都有自己不同的用户地址空间,任何一个进程的全局变量在另一个进程中都完全看不到,所以进程之间通信必须要通过内核。在内核中开辟一块儿缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再把内核缓冲区的数据读走,内核提供的这种机制叫做进程间通信。(让不同进程能够看到一份公共资源,此公共资源就可以实现二者之间的通信,公共资源不属于任何进程,它由操作系统内核提供,属于操作系统。)
进程通信主要包括:管道、系统IPC(包括消息队列、信号量、共享存储)和SOCKET。
1.管道
管道包括三种:
①普通管道(pipe):有两种限制,一是半双工,只能单向传输;二是只能在父子进程间使用。
②命名管道(name_pipe,FIFO):是半双工,只能单向传输。允许无亲缘关系进程间的通信。
③流管道(s_pipe):可以双向传输,但是只能在父子进程间使用。
管道的创建:
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是管道不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。管道是通过调用pipe函数创建的。即int pipe(int fd[2]);经由参数fd返回的两个文件描述符:fd[0]为读而打开,fd[1]为写而打开,fd[1]的输出是fd[0]的输入。通常,进程会先调用pipe,接着调用fork,从而创建了父进程与子进程的IPC通道。fork之后做什么取决于我们想要的数据流的方向,对于从父进程到子进程,父进程关闭管道的读端fd[0],子进程关闭写端fd[1]。
关闭管道的一端:
1)当读一个写端被关闭的管道时,在所有数据都被读取后,read返回0,表示文件结束;
2)当写一个读端被关闭的管道时,则产生信号SIGPIPE,若忽略该信号或者捕捉该信号并从其处理程序返回,wirte返回-1.
2.系统IPC
其三种方式类同,都是使用了内核里的标识符来识别。
消息队列(message queue):由消息的链表存放在内核中,并且由消息队列标识符标识,消息队列克服了传递信息量少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
信号量(semophore):信号量是一个计数器,可以控制多个进程对共享资源的访问。它常作为一种锁资源,防止某进程正在访问时,其他资源也过来访问该资源,因此,信号量主要作为进程间以及同一进程内不同线程之间的同步手段。
共享内存(shared memory):映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式。它是专门针对于其它进程通信方式效率低设计的,它常常与其他通信机制(如信号量)配合使用来实现线程之间同步与通信。
3.SOCKET
套接字。最早出现在UNIX系统中,是UNIX系统主要的信息传递方式。使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。与其他通信机制不同的是,它可用于不同及其间的进程通信。
补充一个:
信号 ( sinal ) : 是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。信号是Linux系统中用于进程之间通信或操作的一种机制,信号可以在任何时候发送给某一进程,而无须知道该进程的状态。如果该进程并未处于执行状态,则该信号就由内核保存起来,知道该进程恢复执行并传递给他为止。如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
各种进程通信方式的缺点:
1.管道:速度慢,容量有限,只有父子进程能通讯;
2.FIFO:任何进程间都能通讯,但速度慢 ;
3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题;
4.信号量:不能传递复杂消息,只能用来同步;
5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存。
进程同步(生产者消费者问题、哲学家进餐问题、读写者问题)
涉及到进程同步的相关概念:
临界资源(临界区):指的是一次只能被一个进程使用的资源叫做临界资源。
同步:为完成某项任务而建立的两个/多个进程,这些进程在合作过程中需要协调工作次序进行有序的访问而互相等待所产生的制约关系。
互斥:两个/多个进程访问同一临界资源,同时只能一个进程访问,其他进程等待的一种相互制约的关系。注意与同步相区别。
信号量:本身是一个计数器,使用P,V两个操作来实现计数的减与加,当计数不大于0时,则进程进入睡眠状态,它用于为多个进程提供共享数据对象的访问。
互斥量:如果信号量只存在两个状态,那就不需要计数了,可以简化为加锁与解锁两个功能,这就是互斥量。
以下三个问题的伪代码参看:https://www.cnblogs.com/pangxiaodong/archive/2011/08/12/2136314.html
生产者消费者问题:
问题描述:一组生产者进程和一组消费者进程共享一块初始为空,大小确定的缓冲区。只有当缓冲区未满时,生产者进程才可以把信息放入缓冲区,否则就要等待;只有缓存区不为空时,消费者进程才能从中取出消息,否则就要等待。缓冲区一次只能一个进程访问(临界资源)。
问题分析:生产者与消费者进程对缓冲区的访问是互斥关系,而生产者与消费者本身又存在同步关系,即必须生成之后才能消费。因而对于缓冲区的访问设置一个互斥量,再设置两个信号量一个记录空闲缓冲区单元,一个记录满缓冲区单元来实现生产者与消费者的同步。
哲学家进餐问题:
问题描述:一张圆桌上坐着五名哲学家,每两名哲学家之间的桌子摆一根筷子,哲学家只有同时拿起左右两根筷子时才可以用餐,用餐完了筷子放回原处。
问题分析:这里五名哲学家就是五个进程,五根筷子是需要获取的资源。可以定义互斥数组用于表示五根筷子的互斥访问,为了防止哲学家个取一根筷子出现死锁,需要添加一定的限制条件。一种方法是限制仅当哲学家左右筷子均可以用时,才拿起筷子,这里需要一个互斥量来限制获取筷子不会出现竞争。
读写者问题:
问题描述:有读者与写者两个并发进程共享一个数据,两个或以上的读进程可以访问数据,但是一个写者进程访问数据与其他进程都互斥。
问题分析:读者与写者是互斥关系,写者与写者是互斥关系,读者与读者是同步关系。因而需要一个互斥量实现读与写、写与写互斥,一个读者的访问计数和实现对计数的互斥。
死锁的四个必要条件,避免方法
死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
导致死锁的四个必要条件:
①互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
②请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
③不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
④循环等待条件: 若干进程间形成首尾相接循环等待资源的关系
注:只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁的避免:
死锁避免的基本思想:系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。
如果操作系统能保证所有进程在有限时间内得到需要的全部资源,则系统处于安全状态否则系统是不安全的。
- 安全状态是指:如果系统存在 由所有的安全序列{P1,P2,…Pn},则系统处于安全状态。一个进程序列是安全的,如果对其中每一个进程Pi(i >=1 && i <= n)他以后尚需要的资源不超过系统当前剩余资源量与所有进程Pj(j < i)当前占有资源量之和,系统处于安全状态则不会发生死锁。
- 不安全状态:如果不存在任何一个安全序列,则系统处于不安全状态。他们之间的对对应关系如下图所示:
下面我们来通过一个例子对安全状态和不安全状态进行更深的了解
如上图所示系统处于安全状态,系统剩余3个资源,可以把其中的2个分配给P3,此时P3已经获得了所有的资源,执行完毕后还能还给系统4个资源,此时系统剩余5个资源所以满足(P2所需的资源不超过系统当前剩余量与P3当前占有资源量之和),同理P1也可以在P2执行完毕后获得自己需要的资源。
如果P1提出再申请一个资源的要求,系统从剩余的资源中分配一个给进程P1,此时系统剩余2个资源,新的状态图如下:那么是否仍是安全序列呢那我们来分析一下
系统当前剩余2个资源,分配给P3后P3执行完毕还给系统4个资源,但是P2需要5个资源,P1需要6个资源,他们都无法获得资源执行完成,因此找不到一个安全序列。此时系统转到了不安全状态。
死锁的预防:
我们可以通过破坏死锁产生的4个必要条件来 预防死锁,由于资源互斥是资源使用的固有特性是无法改变的。
- 破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。
- 破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
- 破坏“循环等待”条件:采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程。
Linux的一些基本命令,如 ls、tail、chmod
ls —— 显示目录下文件列表 ls -a —— 显示所有的文件的名称(文件前面有"."代表的是隐藏文件)
ls -l —— 显示文件的详细信息(简写的方式:ll) ll -h —— 友好的显示
tail —— 显示一个文件后面的内容
chmod —— 变更文件或目录的权限
Linux其它常用命令:
①查看帮助:man 退出帮助目录: q
②切换目录:cd 切换到上一级: cd .. 切换到根目录: cd /
③创建目录:mkdir 目录名 删除一个空目录:rmdir 目录名
④显示文件所有内容 cat 文件名
⑤分页显示:more(空格表示下一页,回车表示下一行) 或者 less (page up/page down翻页)
文件的操作:
⑥创建一个空白文件:touch 文件名
⑦复制文件:cp 文件名 目录/文件名 例:cp a.txt b/a.txt
⑧移动文件(重命名): mv 文件名 目录/文件名 重命名: mv 文件名 新文件名
⑨删除文件:rm 文件名 (带询问删除) rm -f 文件名:不带询问删除
rm -r 文件名:带询问的递归删除 rm -rf 文件名: 不带询问的递归删除
⑩打包或压缩文件/目录:tar 参数 文件名 要打包或压缩的文件目录
常用参数:-cvf:打包一个文件或目录 -zcvf:打包并压缩一个文件或目录,压缩格式为.gzip -xvf:解压或打开一个tar文件
例:将当前目录下的所有文件打包成 test.tar tar -cvf test.tar ./*
将当前目录下的所有文件打包并压缩为 test1.tar.gz tar -zcvf test1.tar.gz
将test.tar解压到当前目录 tar -xvf test.tar
将test.tar解压到b目录 tar -xvf test.tar -C b
⑪vi和vim编辑器(编辑普通文件时有三种模式:命令模式、插入模式、编辑模式,使用ESC或 i 或:来切换模式):
切换到命令模式:按Esc键 切换到插入模式:按 i 或 o 或 a键
命令模式下—— :q 退出 :q! 强制退出 :wq 保存并退出 :set number 显示行号 :set nonumber 隐藏行号
系统管理命令:
⑫清屏:clear
⑬查看所有进程:ps -ef
⑭杀掉某个进程:kill 进程号 强制杀掉某个进程:kill -9 进程号
⑮显示当前系统时间 :date 设置当前系统时间:date -s "2018-09-21 19:01:30"
网络管理:
⑯查看所有的网络设置:ifconfig 禁用网卡 :ifconfig 网卡名 down 启动网卡:ifconfig 网卡名 up
⑰测试网络连通:ping ip号 (和Windows中的一样)
⑱查看网络端口:netstat 例:netstat -an | grep 3306 查看3306端口的占用情况
⑲关机或重启机器:shutdown + 参数(-r 关机重启 -h 关机不重启 now 立刻关机) halt :关机 reboot :重启