码迷,mamicode.com
首页 > 其他好文 > 详细

操作系统学习-1-操作系统概论及存储器管理

时间:2016-04-22 07:11:14      阅读:268      评论:0      收藏:0      [点我收藏+]

标签:

!:什么是操作系统?
操作系统没有一个完整、精确、公认的定义,因为操作系统是一个复杂的系统软件,其外沿或边界不是很清楚。但是可以从功能的角度对其进行定义,从功能的角度来说的话,可以分为对内和对外,对外即对应用程序:对应用程序来说,操作系统是一个控制软件:能够管理应用程序,包括对应用程序的启动、中断、挂起、杀死等,并且能够为应用程序提供各种各样的服务,譬如:网卡服务,声卡服务,io服务等。对内即对硬件资源:对硬件资源来说,操作系统是一个资源管理软件:能够协调的为各种应用程序分配相应的资源,这些资源包括:CPU资源,内存资源,各种外设资源等。
!:软件的分类?
软件可以分为应用软件和系统软件,常见的应用软件如办公软件,影音娱乐软件等。系统软件分为两种:工具类系统软件,如编译器,动态程序库等;操作系统软件,如kernel,shell等(题外话:kernel主要是对计算机硬件资源的管理,那其中主要是哪三大部分管理呢?CPU资源管理、内存资源管理、硬盘资源管理。这三部分在操作系统层面分别抽象为:进程管理、地址空间管理和文件管理)
!:操作系统(内核)的特征?
并发性(区别于并行,并发是指一段时间内有多个进程在运行,并行是指在某一个时间点上有多个进程在运行,一般呢,在CPU一个单核心内部是并发,在一个CPU的多个核心中进程的执行是并行的)
共享姓(表面上看起来是多个程序『同时』访问硬件资源,而实际上是互斥共享的,即对于互斥资源来说,某个时间点只能有一个进程在访问资源,譬如内存资源的访问:对于整个内存资源来说是并行访问的,但是对于内存中某一个最小粒度的内存单元来说,是互斥访问的)
虚拟性(利用多道程序设计技术,使每个用户(进程/程序)都觉得这个计算机好像单独为他服务)
异步性(程序的执行不是一贯到底的,而是走走停停,向前推进的速度不可预知,但是只要程序运行的操作系统环境相同,OS需要保证程序运行的结果相同)
!:学习操作系统应该注意的问题?
当前的许多课本或者教学内容存在一定的老化性,许多之前需要操作系统管理的内容,现在已经差不多不用考虑了,譬如进程调度、磁盘IO管理等,课本的知识是滞后于我们当前硬件发展的,课本上讲的很重要的问题,现在已经不那么重要了
!:一条鸡汤~
我听过的我会忘记
我看过的我会记住
只有我做过的我才能理解
!:操作系统家族?
Unix家族:Mac OS等
Linux家族:Ubuntu,Fedora,andriod等
Windows家族:Windows 10,WPH等
其他操作系统:工控领域,自动化领域,大型机操作系统等
===============================================================================
!:操作系统的启动?
当你摁下电源的时候,会进行接下来的一系列操作:
BIOS(Basic Input/Output System)加电自检(检查各种硬件设备是否能正常工作,譬如检查显卡在不在啊什么的)
设备检查正常后,由BIOS加载bootloader(用来加载OS)到内存中
由bootloader将OS从硬盘加载至内存,CPU的控制权交给OS
系统启动
!:什么是系统调用?
所谓系统调用,即是应用程序向操作系统发出的服务请求,具体就是应用程序向操作系统发出的一条操作指令(操作系统对底层硬件的访问进行了封装,并提供了对外访问的接口,那这些接口API就是系统调用API,通常在这些系统调用API之上还有一层更好层次的API,应用程序是通过访问这更高层次的API(譬如Windows的win32 API,POSIX API用于POSIX-based System(包括Unix,Linux,Mac OS X的所有版本等)),来实现系统调用的)
!:什么是异常?
所谓异常,即是应用程序产生的意想不到的指令导致发生了错误,譬如非法指令或者其他坏的处理状态(如:内存出错)等,不得不由操作系统出面处理的场景
!:什么是中断?
所谓中断,(中断来源于外设,有待商榷)即是需要时,CPU停止当前程序的执行转而去执行或处理新情况的过程
!:为什么应用程序被设计称为不能直接访问硬件系统的?
首先是出于安全的考虑,对于计算机硬件来说,操作系统是可以被信任的,而第三方的应用程序是不被信任的,这些应用程序可能会去恶意破坏计算机的软硬件系统
再者是出于应用程序开发方便的考虑,如果让应用程序直接访问硬件,会额外的增加应用程序的开发和维护成本,如果建立在操作系统之上,则会大大减少开发成本,因为操作系统已经对底层硬件做了很好的封装,应用程序只需调用系统调用,请求操作系统服务即可
!:操作系统如何处理中断和异常呢?
操作系统内部维护了一个处理中断和异常的表,操作系统为每个中断和异常都编了号,这张表的key是这些号码,Value是处理中断和异常的服务程序的起始地址,发生中断时,直接查表,然后由响应的中断处理程序进行处理即可
中断的处理过程详细如下(中断的过程对用户来说是透明的):
*设置中断标记:将内部或外部事件设置中断标记,并get到中断的ID
*保存当前的处理状态:程序的执行进度(下一条指令的地址),相关寄存器内容
*根据中断ID查表,找到中断处理程序,处理中断
*清除中断标记
*恢复之前保存好的处理现场,继续程序的执行
异常的处理过程如下:
*get到异常ID
*保存当前处理状态:程序的执行进度(即下一条指令地址),相关寄存器内容
*异常处理:要么杀死异常的程序、要么重新执行刚才的指令
*恢复现场,并且继续中断钱程序的执行
!:操作系统软件结构?
内核:)系统调用:)高层API:)
系统调用是对内核服务的封装,高层API是对系统调用API的封装
通常,应用程序通过使用高层API来实现系统调用,但是也可以直接访问系统调用API,也可以直接访问内核,只不过后两种在应用程序中占很少一部分
!:什么是用户态和内核态?
用户态:应用程序掌握CPU执行权的状态,这种状态下,其执行权比较低,譬如不能直接访问IO,也不能执行一些特权指令,比如挂起一个进程什么的
内核态:操作系统内核掌握CPU执行权的状态,这种状态下,CPU可以执行任何指令,因为其具有所有执行权
应用程序通过系统调用可以实现CPU从用户态到内核态的转变
一个应用程序执行一个函数调用要比执行一次系统调用的性能要高:因为执行一个函数调用是在程序当前堆栈中完成的,而执行系统调用,需要切换到内核堆栈,并且还要完成特权级的转换,即要从用户态转换成内核态,切换堆栈和特权级是需要很大开销的,所以性能比起一般函数调用要略低的。
=============================================================================
!:内存的层次结构?
(寄存器::L1高速缓存::L2高速缓存)::L3高速缓存::内存::磁盘
从前到后,访问速度依次降低
从前到后,单位存储单元造价依次降低
!:地址空间?
物理地址空间:直接和硬件相对应,即硬件所能支持的地址空间,譬如内存条所支持的内存大小
逻辑地址空间:一个运行程序所拥有的地址范围,在程序看来,地址空间就是一个一维的线性地址空间
!:逻辑地址的生成?
(在C程序中,变量引用就是一块地址空间的引用;到了汇编程序中,地址扔以一种人能看得懂但是更接近机器语言的英文标志符表示;到了机器语言中,地址就真的编程逻辑地址了,是看不出来变量名称或者什么的)
对于C程序来说,编译并生成可执行文件之后,在可执行文件中,就生成了逻辑地址
这里的逻辑地址是保存在硬盘上的,整个生成过程是不需要操作系统参与的
!:物理地址的生成?
在CPU中有一个叫MMU(Memory Management Unit)的区域,用于将逻辑地址转换成物理地址,具体过程如下:
1.CPU的ALU(arithmatic and logic unit)要执行一条指令,需要拿到这个地址的内容,那CPU就要发出指令从内存中取出这条指令,取的时候带的参数就是这条指令的逻辑地址
2.CPU的MMU组件就从自己的维护的映射表中查找是否有现成的映射关系,如果有直接去取,如果没有,就去内存中找
3.从逻辑地址定位到物理地址的转换规则是由操作系统维护的,在内存中找到这条指令后,返回给CPU
4.CPU取出这条指令,然后执行
!:操作系统如何保证运行程序之间内存空间的相对独立性?
操作系统为每个程序设置了逻辑地址空间的基址和界限,在CPU根据逻辑地址去内存中找指令时,操作系统会对逻辑地址进行检验,如果超过了为这个程序设定的界限,则是非法访问了内存,不允许
!:连续物理内存分配(使用连续内存分配有很多问题,是一开始设计计算机的时候用的,现在使用的并不多,那这个过程中产生的问题可能有其他的解决方法,也可能还了其他内存分配机制后,就没有这样或那样的问题了)
1.内存碎片问题
*即内存中不能被利用的地址空间
*内部碎片:在分配单元中未被使用的内存
*外部碎片:在分配单元间未被使用的内存
2.分配策略
*首次适配:为了分配n个字节,使用第一个(从零地址往下找)可用的空闲块(这个空闲快比n个字节要大)(容易产生大量外空间块)
*最优适配:为了分配n个字节,从内存中未分配的空闲块中找出和n个字节大小相差最小的空闲块进行分配(容易产生几乎不可用的空间块)
*最差适配:为了分配n个字节,从内存中找到一个跟要分配内存大小差距最大的一个内存块进行分配(破坏了大空闲块以致大分区无法被分配)
(并不是这里的3个算法哪个更好,这里只是简单的内存分配,程序分配需要大的块也需要小的块,谈不上最优)
3.压缩式与交换式碎片整理
*压缩式(compact)
*就是通过已分配内存块的挪动(重置)来使未分配的内存块放到一起
*问题
1.什么时候进行重置
*不能在程序运行的时候进行挪操作,可以在程序阻塞,等待,挂起的时候挪
2.内存重置开销大不大呢
*内存拷贝的开销还是很大的
*交换式(swap)
*就是抢占等待的程序,回收他们的内存——把他们从内存挪到硬盘,数据并没有丢失,只是从内存挪到硬盘了
1.选择哪一个程序换出呢(对于连续内存分配来说,换入换出的单位是程序,不是程序片段)
2.什么时候进行换入换出
3.换入换出的开销
(这些问题都是使用连续物理内存分配要解决的,有的还没找到好的解决方式时,这种分配方式已经过时了)
=============================================================================
!:非连续内存分配
0.为什么使用非连续内存分配
因为使用连续的内存分配总是不可避免的产生碎片问题从而降低了内存的利用率,使用非连续的内存分配就能避免这一问题产生
非连续内存分配的优点:
*更好的内存利用率
*允许代码和数据共享
*支持动态加载和动态连接
非连续内存分配的缺点:
*缺点就是分配管理本身,所谓管理就是如果把逻辑地址转化成物理地址
*使用软件的解决方法开销很大,因为对于每一条指令都要使用软件进行映射,开销太大啦
*常见的硬件解决方案有:分段和分页
1.分段
*分段寻址方案(根据应用程序的运行特点进行分割,是有意义的分割)
1.程序中逻辑地址本身是连续的,但我们可以根据程序运行的特点对逻辑地址进行分段,譬如把逻辑地址分为:堆、运行栈、程序数据、程序txt段(库、用户代码),然后把这些段分别放到不同的物理地址区域中(这就是分段映射了),以不同的操作方式的权限进行操作
2.如何进行分段映射呢?
1.在编写和编译应用程序的时候,内存被抽象为了一维的逻辑地址,当程序加载到内存的时候,不同的段已经映射到不同的物理内存块中了,一个段对应一个内存块
2.程序访问物理内存需要一个2维的二元组(段号,段内偏移),逻辑地址就分成了两部分,即段号和段内偏移
3.有两种具体的实现:段寄存器+地址寄存器(X86)、单地址
4.一条逻辑地址怎么转化成物理地址呢?逻辑地址有两部分组成:段号+偏移。首先,根据段号去查段表(由操作系统维护,①逻辑地址段号和物理地址块的对应关系,即这个段号对应的内存快的起始地址②不同的段长度大小不同,这个信息也是放到段表里面的),然后看一下偏移地址是否超出了该段的限制,内存访问异常,正常的话是段表对应物理地址起始地址加上偏移地址得该条逻辑地址对应的物理地址
5.段表在程序运行之前就应该由操作系统建立好,怎么建跟硬件有很强的联系(知道到这就行了)
*现在用的还是比较少的,主要的还是分页机制
2.分页
*分页寻址方案(页的大小不变,而且是没有意义的切割)
1.分页和分段一样,都需要一个页号(段号)和页内偏移(段内偏移)
2.如果进行页映射呢?
1.对逻辑地址空间和物理地址空间进行分页,分页的大小一样,都是2的幂次方
2.逻辑页(Page)和物理页(Frame:页帧):物理地址被分为相同大小的内存块,一个物理地址由两部分组成:帧号和帧内偏移
3.那一条逻辑地址如何转换成物理地址呢?物理地址=2的S次方*f+o,其中:S为页帧的比特位数,f是帧号,o为页内偏移
4.OK,页内偏移和帧内偏移是一样的,关键是逻辑地址中的页号和帧号不一定相同,这就需要一个页表了,建立起逻辑页号和物理页帧的对应关系
5.页表是由操作系统建立维护的,每个运行的程序都有一个页表
6.逻辑地址分的页是连续的,映射到物理页帧的时可能不连续了就:不是所有的页在一开始就有对应的页帧,毕竟逻辑地址空间可以大于物理地址空间
3.页表(PageTable)
*页表概述
1.每个运行的程序都有一个页表,而且页表是动态变化的,逻辑地址中页号想要找到其对应的物理地址帧号,需要两个东西:页表基地址(存放在页表基地址寄存器中,存两部分内容,一个是页表基址,一个是页表长度)、页号
2.每个页表相当于一个大数组,索引是页号,值是帧号
3.不是所有的页号都对应一个页帧号
4.页表是位于内存上的,所以,每次寻址都要访问两次内存
问题:
1.页表可能会很大,因为越发展可用的地址空间越大,如果每页1024字节,一个64位机器的页表内存都放不下呀
2.访问效率问题:页表是放到内存中的,一次寻址都要访问两次内存
如何处理这些问题呢?
1.(解决效率问题)可以通过缓存,把常用的页表对应关系缓存到离CPU较近的地方,先找缓存,缓存没有了再找实际的页表
2.(解决空间问题)间接访问,可以把大的空间拆分成较小的空间
*快表(TLB(Translation Look-aside Buffer):转换后备缓冲区位于CPU的MMU单元)
1.要缓存的内容就是页表中的内容
2.先在TLB中找页帧号,如果找到了就直接去内存中寻址访问,如果没找到,不得不查页表,寻址后并且把页表更新到TLB中
3.由于程序的运行具有空间和时间的局部性,快表中页表被miss掉的可能性在10%左右
*二级、多级页表(牺牲时间换空间,牺牲的时间又可以通过TLB来缓解)
*其实无论使用二级还是多级页表,都只是对大页表进行了拆分,减少了大页表占据连续的大的内存空间的可能性,而本身并不能缓解大页表占据大内存的情景,能想到的避免大的页表占据大的内存的策略就是不一下子把程序的全部页表都加载到内存,即使用虚拟内存的技术
*二级页表
1.将逻辑地址段再拆分,分为三段:一级页表段+二级页表段+段内偏移,根据一级页表段查看一级页表(存放的是对应二级页表的基址),根据二级页表段查看实际对应的内存页帧号,然后这个页帧号加上对应的帧内(页内)偏移就是实际的物理地址
*多级页表
1.二级页表推广出去就行,也是将逻辑地址再分,和二级页表很像,只是换成多级页表了
*反向页表(整个系统只有一个页表)
*不让页表和逻辑地址空间大小相对应,而和物理地址空间相对应(逻辑地址空间的增长速度大于物理地址空间的增长速度)
*所谓反向是指:通常的页表是用逻辑地址的页号去索引物理地址的页帧号,也就是页表是和逻辑地址相关联的;而反向是使用物理地址的页帧号去索引逻辑地址的页号,那这个页表就只和物理地址的大小有关了,不会因逻辑地址空间很大而导致了占据很大内存的大页表
*问题也很明显,如何去根据逻辑地址查找对应的物理地址呢?
有很多中实现方案,但是,比较好的方案是『基于哈希的查找方案』,实现是:定义一个哈希函数,输入为Page Number输出为Frame Number
=============================================================================
!:虚拟内存技术
1.起因
程序规模的增长速度远大于存储器容量的增长速度
*要让更多的程序运行在有限的硬件内存上
*要让更大的程序运行在有限的硬件内存上
*所以就把硬盘的一部分当成虚拟内存,程序运行的时候,并不把程序的全部内容加载到内存中,只把部分的要用的加载,等用到的时候,没有了再从硬盘加载
2.覆盖技术(虚存技术产生之前)
*如果程序太大,超过了内存的容量,可以采用手动覆盖(Overlay)的技术,只把需要的指令和数据保存到内存当中,这里的手动是指需要程序员编写程序的时候自己处理内存的管理
*根据程序运行时函数之间的调用关系来对程序进行拆分,相互之间没有调用关系的分开,当然有一个程序是常驻内存的,用来管理这个覆盖技术,可以把函数之间的调用关系使用一棵树的形式表示出来,然后同一级别的树位于同一覆盖区(回想以下老师上课讲的例子)
*问题:
1.程序员在编写程序的时候多了额外的考虑和负担(主要)
2.覆盖本身的开销
3.交换技术(虚存技术产生之前)
*如果程序太多,超过了内存的容量,可以采用自动交换(Swapping)技术,把暂时不运行的程序送到外存中去,由操作系统完成,但是成本很高,因为每次换入换出都是整个程序(主要问题就是交换的粒度太大:整个程序 )
*这种技术是早期Unix使用的
*和交换式的连续内存分配一样有相似的问题:
1.什么时候进行交换?
2.交换的策略:换谁进来,换谁出去?
*整个过程由操作系统完成,对程序员是透明的
4.虚存技术
*工作情况
基于程序的局部性原理,程序运行时没有必要把程序的全部内容加载到内存,只需把程序当前运行所需的部分片段装入内存即可,其余部分留在硬盘上。
程序运行时,如果他索要访问的页(段)已经在内存了,OK,继续执行下去,如果没有,就会产生一个缺页中断,由OS把请求页(段)调入内存,如果内存已满,则使用特定的页(段)置换策略,换出一部分页(段)出来,再将所需页(段)加载到内存,重新执行被中断的指令,以使程序继续运行
*程序的局部性原理
时间局部性:如果一条指令被执行了,则不久以后该条指令可能会再次执行,如果某个数据被访问了,则不久以后,该条数据可能会在被访问
空间局部性:一旦程序访问了某个存储单元,则其附近的存储单元在不久之后也将被访问
*基本特征
1.内存变大了(加上了硬盘呀)
2.部分交换
3.空间本身的不连续性
*虚拟页式内存管理
1.大多数的虚拟内存管理系统都采用了页式存储管理技术。即在页式存储管理的基础上增加了请求调页和页面置换的功能
2.跟一般的页表相比,页表表项增加了一些东西,以满足虚拟内存的实现,譬如增加了驻留位:表示该页是在内存则为1在外存则为0;保护位(不是1位):读写保护位;修改位:该页是否被修改过;访问位:该页是否被访问过(用于置换算法)
*局部页面置换算法
1.最优页面置换算法(OPT:Optimal Page Transfer)
由Belady提出
要换出的页面是将永久不使用的或者将来最长时间内不会使用的。
是一种理论上的算法,因为将来哪些页面将被调入是无法预知的,但是可以用来评价其他页面置换算法。
2.先进先出算法(FIFO:First In First Out)
要换出的页面是最早进入内存的页面,即在内存中驻留时间最久的页面要被淘汰。
最简单,但是缺页率略高,因为这种置换是没有实际意义的,完全是按照时间来的,并没有考虑到程序运行时的动态特征
3.最近最久未使用算法(LRU,Least Recently Used)
要换出的页面是最近最少使用的页面。对于内存中的每个页面,都设置一个访问字段,用来记录自上次访问之后所经历的时间T,当要淘汰一个页面时,选择T最大的页面淘汰。
虽然算法较好,但是支持该算法所需要的硬件支持也很多,实现起来比较麻烦,成本较高。在实际应用中使用的是近似LRU算法,而Clock算法就是一个近似LRU算法。
4.时钟页面置换算法(Clock)
这里的Clock并不是轮询的意思,二是在查找置换页面的时候像一个钟表
首先为每个页面添加一个访问位,再将所有的页面通过链接指针连成一个循环队列,当某页被访问的时候,该页的访问位设置为1(硬件完成的,当然也可以用软件实现);置换算法在这个循环队列中使用FIFO的方式选择要换出的页面,如果该页面的访问位是0,直接换出,如果是1,置为0并且接着往下找
对于时钟页面置换算法,有一个其增强的算法即增强时钟页面置换算法(又叫二分机会法)
这样使脏页总能在一次时钟扫描中留下来,在时钟页面置换的基础上又利用了dirty bit(修改位)位,其置换策略为:
技术分享
为什么要使脏页先留一下,对于非脏页(没有被修改的页)直接释放就行,不用写回硬盘,对于脏页,还要写会硬盘,成本较高
5.最不常用算法(LFU:Least Frequently Used)
为每个页面记录一段时间内被访问的频率,要换出的是访问频率最低的页面,可以使用和LRU一样的一套硬件来实现LFU,同样实现成本较高。
问题很多,首先使用最不常用来淘汰页面本身就有点问题
6.Belady现象
FIFO算法出现的现象
操作系统为每个程序分配的可用物理内存往往是小于程序运行所需的逻辑地址空间的,使用虚拟内存技术运行改程序,在程序运行的过程中肯定会产生缺页中断,通常来说呢,分配给该程序的可用物理内存越大,缺页率应该越低,而Belady现象指的是对于某些页面置换算法而言,被分配的物理内存大了,缺页率也跟着变大的现象
7.抖动现象
产生原因:由于系统中运行的进程太多,导致进程在运行的时候大部分时间用于页面的换入/换出,而无法做一些实际有效的工作,导致CPU的利用率急剧下降到很低并趋于0的状况,这种状况称为『抖动』
8.LRU、FIFO和Clock的比较
LRU和FIFO本质上都是先进先出的策略,只是LRU考虑了程序的动态运行特征,对于程序中访问的到的页面,从栈中取出来重新放到栈顶,而FIFO则没有按照程序的运行进行动态的调整
而Clock算法是对LRU的近似
如果程序没有很好的局部性访问特征,Clock和LRU都可能退化成FIFO算法 
*全局页面置换算法
工作集模型
(背景知识:根据程序运行的局部性原理,程序在运行期间其对页面的访问是不均匀的,不同段的时间内可能集中在不同的几个页面内)
所谓工作集:是指在某段时间间隔内,进程运行实际要访问的页面的集合。
工作集页置换算法
虽然程序需要少量的几页就能运行,但是为了较少的产生缺页,将程序的全部工作集装入内存,然而我们并不知道未来将要访问到工作集到底是哪些页面,只能根据最近的时间内的行为作为未来时间内行为的近似了
具体就是因为工作集窗口随着运行时间在向后平移,OK,那平移之后不在工作集中的页要换出去,然而并不是因为内存不够用了,而是因为不在工作集了,内存可能还很够用
缺页率置换算法
缺页率=缺页次数/访问总数
缺页率高的时候增加工作集的长度,缺页率低的时候减少工作集的长度
OK,怎么增加怎么减少呢?
1.首先设定一个初始的窗口的大小
2.缺页率是一个区间的值
3.可以设定这样一个值,如果两次缺页中断中间访问的次数大于该值,表示缺页率较低了,这时候要减少工作集的长度,减少的页是在这个区间中没有被访问到的页;如果两次中断中间隔得访问次数的间隔小于该值,表示缺页率略高,要扩容,就把缺的页加到当前工作集中

操作系统学习-1-操作系统概论及存储器管理

标签:

原文地址:http://blog.csdn.net/u013165504/article/details/51214262

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!