当前位置: 首页 > news >正文

建网站引流做淘宝wordpress的博客

建网站引流做淘宝,wordpress的博客,中国十大广告公司排名,石家庄核酸机构造假视频计算机结构 现代计算机模型是基于-冯诺依曼计算机模型 计算机在运行时#xff0c;先从内存中取出第一条指令#xff0c;通过控制器的译码#xff0c;按指令的要求#xff0c;从存储器中取出数据进行指定的运算和逻辑操作等加工#xff0c;然后再按地址把结果送到内存中去… 计算机结构 现代计算机模型是基于-冯诺依曼计算机模型 计算机在运行时先从内存中取出第一条指令通过控制器的译码按指令的要求从存储器中取出数据进行指定的运算和逻辑操作等加工然后再按地址把结果送到内存中去接下来再取出第二条指令在控制器的指挥下完成规定操作依此进行下去。直至遇到停止指令 程序与数据一样存贮按程序编排的顺序一步一步地取出指令自动地完成指令规定的操作是计算机最基本的工作模型 计算机五大核心组成部分 控制器是整个计算机的中枢神经其功能是对程序规定的控制信息进行解释根据其要求进行控制调度程序、数据、地址协调计算机各部分工作及内存与外设的访问等。 运算器运算器的功能是对数据进行各种算术运算和逻辑运算即对数据进行加工处理。 存储器存储器的功能是存储程序、数据和各种信号、命令等信息并在需要时提供这些信息。 输入输入设备是计算机的重要组成部分输入设备与输出设备合你为外部设备简称外设输入设备的作用是将程序、原始数据、文字、字符、控制命令或现场采集的数据等信息输入到计算机。 常见的输入设备有键盘、鼠标器、光电输入机、磁带机、磁盘机、光盘机等。 输出输出设备与输入设备同样是计算机的重要组成部分它把外算机的中间结果或最后结果、机内的各种数据符号及文字或各种控制信号等信息输出出来微机常用的输出设备有显示终端CRT、打印机、激光印字机、绘图仪及磁带、光盘机等。 计算机结构分成以下 5 个部分 输入设备输出设备内存中央处理器总线。 内存 在冯诺依曼模型中程序和数据被存储在一个被称作内存的线性排列存储区域。 存储的数据单位是一个二进制位英文是 bit最小的存储单位叫作字节也就是 8 位英文是 byte每一个字节都对应一个内存地址。 内存地址由 0 开始编号比如第 1 个地址是 0第 2 个地址是 1 然后自增排列最后一个地址是内存中的字节数减 1。 我们通常说的内存都是随机存取器也就是读取任何一个地址数据的速度是一样的写入任何一个地址数据的速度也是一样的。 CPU 冯诺依曼模型中 CPU 负责控制和计算为了方便计算较大的数值CPU 每次可以计算多个字节的数据。 如果 CPU 每次可以计算 4 个 byte那么我们称作 32 位 CPU 如果 CPU 每次可以计算 8 个 byte那么我们称作 64 位 CPU。 这里的 32 和 64称作 CPU 的位宽。 为什么 CPU 要这样设计呢 因为一个 byte 最大的表示范围就是 0~255。 比如要计算 20000*50就超出了byte 最大的表示范围了。 因此CPU 需要支持多个 byte 一起计算当然CPU 位数越大可以计算的数值就越大但是在现实生活中不一定需要计算这么大的数值比如说 32 位 CPU 能计算的最大整数是 4294967295这已经非常大了。 控制单元和逻辑运算单元 CPU 中有一个控制单元专门负责控制 CPU 工作还有逻辑运算单元专门负责计算。 寄存器 CPU 要进行计算比如最简单的加和两个数字时因为 CPU 离内存太远所以需要一种离自己近的存储来存储将要被计算的数字。 这种存储就是寄存器寄存器就在 CPU 里控制单元和逻辑运算单元非常近因此速度很快。 常见的寄存器种类 通用寄存器用来存放需要进行运算的数据比如需要进行加和运算的两个数据。程序计数器用来存储 CPU 要执行下一条指令所在的内存地址注意不是存储了下一条要执行的指令此时指令还在内存中程序计数器只是存储了下一条指令的地址。指令寄存器用来存放程序计数器指向的指令也就是指令本身指令被执行完成之前指令都存储在这里。 多级缓存 现代CPU为了提升执行效率减少CPU与内存的交互(交互影响CPU效率)一般在CPU上集成了多级缓存架构 CPU缓存即高速缓冲存储器是位于CPU与主内存间的一种容量较小但速度很高的存储器 由于CPU的速度远高于主内存CPU直接从内存中存取数据要等待一定时间周期Cache中保存着CPU刚用过或循环使用的一部分数据当CPU再次使用该部分数据时可从Cache中直接调用减少CPU的等待时间提高了系统的效率具体包括以下几种 L1-Cache L1- 缓存在 CPU 中相比寄存器虽然它的位置距离 CPU 核心更远但造价更低通常 L1-Cache 大小在几十 Kb 到几百 Kb 不等读写速度在 2~4 个 CPU 时钟周期。 L2-Cache L2- 缓存也在 CPU 中位置比 L1- 缓存距离 CPU 核心更远它的大小比 L1-Cache 更大具体大小要看 CPU 型号有 2M 的也有更小或者更大的速度在 10~20 个 CPU 周期。 L3-Cache L3- 缓存同样在 CPU 中位置比 L2- 缓存距离 CPU 核心更远大小通常比 L2-Cache 更大读写速度在 20~60 个 CPU 周期。 L3 缓存大小也是看型号的比如 i9 CPU 有 512KB L1 Cache有 2MB L2 Cache 有16MB L3 Cache。 当 CPU 需要内存中某个数据的时候如果寄存器中有这个数据我们可以直接使用如果寄存器中没有这个数据我们就要先查询 L1 缓存L1 中没有再查询 L2 缓存L2 中没有再查询 L3 缓存L3 中没有再去内存中拿。 总结 存储器存储空间大小内存L3L2L1寄存器 存储器速度快慢排序寄存器L1L2L3内存 安全等级 CPU运行安全等级 CPU有4个运行级别分别为 ring0ring1ring2ring3 ring0只给操作系统用ring3谁都能用。 ring0是指CPU的运行级别是最高级别ring1次之ring2更次之…… 系统内核的代码运行在最高运行级别ring0上可以使用特权指令控制中断、修改页表、访问设备等等。 应用程序的代码运行在最低运行级别上ring3上不能做受控操作。 如果要做比如要访问磁盘写文件那就要通过执行系统调用函数执行系统调用的时候CPU的运行级别会发生从ring3到ring0的切换并跳转到系统调用对应的内核代码位置执行这样内核就为你完成了设备访问完成之后再从ring0返回ring3。 这个过程也称作用户态和内核态的切换。 局部性原理 在CPU访问存储设备时无论是存取数据抑或存取指令都趋于聚集在一片连续的区域中这就被称为局部性原理 时间局部性Temporal Locality 如果一个信息项正在被访问那么在近期它很可能还会被再次访问。 比如循环、递归、方法的反复调用等。 空间局部性Spatial Locality 如果一个存储器的位置被引用那么将来他附近的位置也会被引用。 比如顺序执行的代码、连续创建的两个对象、数组等。 程序的执行过程 程序实际上是一条一条指令所以程序的运行过程就是把每一条指令一步一步的执行起来负责执行指令的就是 CPU 了。 那 CPU 执行程序的过程如下 第一步CPU 读取程序计数器的值这个值是指令的内存地址然后 CPU 的控制单元操作地址总线指定需要访问的内存地址接着通知内存设备准备数据数据准备好后通过数据总线将指令数据传给 CPUCPU 收到内存传来的数据后将这个指令数据存入到指令寄存器。第二步CPU 分析指令寄存器中的指令确定指令的类型和参数如果是计算类型的指令就把指令交给逻辑运算单元运算如果是存储类型的指令则交由控制单元执行第三步CPU 执行完指令后程序计数器的值自增表示指向下一条指令。这个自增的大小由 CPU 的位宽决定比如 32 位的 CPU指令是 4 个字节需要 4 个内存地址存放因此程序计数器的值会自增 4 简单总结一下就是一个程序执行的时候CPU 会根据程序计数器里的内存地址从内存里面把需要执行的指令读取到指令寄存器里面执行然后根据指令长度自增开始顺序读取下一条指令。 CPU 从程序计数器读取指令、到执行、再到下一条指令这个过程会不断循环直到程序执行结束这个不断循环的过程被称为 CPU 的指令周期。 总线 CPU 和内存以及其他设备之间也需要通信因此我们用一种特殊的设备进行控制就是总线。 地址总线用于指定 CPU 将要操作的内存地址 数据总线用于读写内存的数据 控制总线用于发送和接收信号比如中断、设备复位等信号CPU 收到信号后自然进行响应这时也需要控制总线 当 CPU 要读写内存数据的时候一般需要通过两个总线 首先要通过地址总线来指定内存的地址 再通过数据总线来传输数据 输入、输出设备 输入设备向计算机输入数据计算机经过计算将结果通过输出设备向外界传达。 如果输入设备、输出设备想要和 CPU 进行交互比如说用户按键需要 CPU 响应这时候就需要用到控制总线。 基础知识 中断 中断的类型 按照中断的触发方分成同步中断和异步中断 根据中断是否强制触发分成可屏蔽中断和不可屏蔽中断。 中断可以由 CPU 指令直接触发这种主动触发的中断叫作同步中断。 同步中断有几种情况。 比如系统调用需要从用户态切换内核态这种情况需要程序触发一个中断叫作陷阱Trap中断触发后需要继续执行系统调用。 还有一种同步中断情况是错误Fault通常是因为检测到某种错误需要触发一个中断中断响应结束后会重新执行触发错误的地方比如后面我们要学习的缺页中断。 最后还有一种情况是程序的异常这种情况和 Trap 类似用于实现程序抛出的异常。 另一部分中断不是由 CPU 直接触发是因为需要响应外部的通知比如响应键盘、鼠标等设备而触发的中断这种中断我们称为异步中断。 CPU 通常都支持设置一个中断屏蔽位一个寄存器设置为 1 之后 CPU 暂时就不再响应中断。 对于键盘鼠标输入比如陷阱、错误、异常等情况会被临时屏蔽。 但是对于一些特别重要的中断比如 CPU 故障导致的掉电中断还是会正常触发。 可以被屏蔽的中断我们称为可屏蔽中断多数中断都是可屏蔽中断。 内核态和用户态 什么是用户态和内核态 Kernel 运行在超级权限模式下所以拥有很高的权限。 按照权限管理的原则多数应用程序应该运行在最小权限下。 因此很多操作系统将内存分成了两个区域 内核空间Kernal Space这个空间只有内核程序可以访问 用户空间User Space这部分内存专门给应用程序使用。 用户空间中的代码被限制了只能使用一个局部的内存空间我们说这些程序在用户态 执行。 内核空间中的代码可以访问所有内存我们称这些程序在内核态 执行。 按照级别分 当程序运行在0级特权级上时就可以称之为运行在内核态 当程序运行在3级特权级上时就可以称之为运行在用户态 运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。 当我们在系统中执行一个程序时大部分时间是运行在用户态下的在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态比如操作硬件 这两种状态的主要差别 处于用户态执行时进程所能访问的内存空间和对象受到限制其所处于占有的处理器是可被抢占的 处于内核态执行时则能访问所有的内存空间和对象且所占有的处理器是不允许被抢占的。 为什么要有用户态和内核态 由于需要限制不同的程序之间的访问能力防止他们获取别的程序的内存数据或者获取外围设备的数据并发送到网络 用户态与内核态的切换 所有用户程序都是运行在用户态的但是有时候程序确实需要做一些内核态的事情 例如从硬盘读取数据或者从键盘获取输入等而唯一可以做这些事情的就是操作系统所以此时程序就需要先操作系统请求以程序的名义来执行这些操作 用户态和内核态的转换 系统调用 用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作比如fork()实际上就是执行了一个创建新进程的系统调用 而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现例如Linux的int 80h中断 如上图所示内核程序执行在内核态Kernal Mode用户程序执行在用户态User Mode。 当发生系统调用时用户态的程序发起系统调用因为系统调用中牵扯特权指令用户态程序权限不足因此会中断执行也就是 TrapTrap 是一种中断。 发生中断后当前 CPU 执行的程序会中断跳转到中断处理程序内核程序开始执行也就是开始处理系统调用。 内核处理完成后主动触发 Trap这样会再次发生中断切换回用户态工作。 异常 当CPU在执行运行在用户态下的程序时发生了某些事先不可知的异常这时会触发由当前运行进程切换到处理此异常的内核相关程序中也就转到了内核态比如缺页异常 外围设备的中断 当外围设备完成用户请求的操作后会向CPU发出相应的中断信号这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序如果先前执行的指令是用户态下的程序那么这个转换的过程自然也就发生了由用户态到内核态的切换 比如硬盘读写操作完成系统会切换到硬盘读写的中断处理程序中执行后续操作等 线程 线程系统分配处理器时间资源的基本单元是程序执行的最小单位 线程可以看做轻量级的进程共享内存空间每个线程都有自己独立的运行栈和程序计数器线程之间切换的开销小。 在同一个进程程序中有多个线程同时执行通过CPU调度在每个时间片中只有一个线程执行 进程可以通过 API 创建用户态的线程也可以通过系统调用创建内核态的线程。 用户态线程 用户态线程也称作用户级线程操作系统内核并不知道它的存在它完全是在用户空间中创建。 用户级线程有很多优势比如 管理开销小创建、销毁不需要系统调用。 切换成本低用户空间程序可以自己维护不需要走操作系统调度。 但是这种线程也有很多的缺点 与内核协作成本高比如这种线程完全是用户空间程序在管理当它进行 I/O 的时候无法利用到内核的优势需要频繁进行用户态到内核态的切换。 线程间协作成本高设想两个线程需要通信通信需要 I/OI/O 需要系统调用因此用户态线程需要额外的系统调用成本。 无法利用多核优势比如操作系统调度的仍然是这个线程所属的进程所以无论每次一个进程有多少用户态的线程都只能并发执行一个线程因此一个进程的多个线程无法利用多核的优势。 操作系统无法针对线程调度进行优化当一个进程的一个用户态线程阻塞Block了操作系统无法及时发现和处理阻塞问题它不会更换执行其他线程从而造成资源浪费。 内核态线程 内核态线程也称作内核级线程Kernel Level Thread这种线程执行在内核态可以通过系统调用创造一个内核级线程。 内核级线程有很多优势 可以利用多核 CPU 优势内核拥有较高权限因此可以在多个 CPU 核心上执行内核线程。 操作系统级优化内核中的线程操作 I/O 不需要进行系统调用一个内核线程阻塞了可以立即让另一个执行。 当然内核线程也有一些缺点 创建成本高创建的时候需要系统调用也就是切换到内核态。 扩展性差由一个内核程序管理不可能数量太多。 切换成本较高切换的时候也同样存在需要内核操作需要切换内核态 用户态线程和内核态线程之间的映射关系 如果有一个用户态的进程它下面有多个线程如果这个进程想要执行下面的某一个线程应该如何做呢 这时比较常见的一种方式就是将需要执行的程序让一个内核线程去执行。 毕竟内核线程是真正的线程因为它会分配到 CPU 的执行资源。 如果一个进程所有的线程都要自己调度相当于在进程的主线程中实现分时算法调度每一个线程也就是所有线程都用操作系统分配给主线程的时间片段执行。 这种做法相当于操作系统调度进程的主线程进程的主线程进行二级调度调度自己内部的线程。 这样操作劣势非常明显比如无法利用多核优势每个线程调度分配到的时间较少而且这种线程在阻塞场景下会直接交出整个进程的执行权限。 由此可见用户态线程创建成本低问题明显不可以利用多核。 内核态线程创建成本高可以利用多核切换速度慢。 因此通常我们会在内核中预先创建一些线程并反复利用这些线程。 协程 协程是一种比线程更加轻量级的存在协程不是被操作系统内核所管理而完全是由程序所控制也就是在用户态执行。 这样带来的好处就是性能得到了很大的提升不会像线程切换那样消耗资源。 子程序 或者称为函数在所有语言中都是层级调用比如A调用BB在执行过程中又调用了CC执行完毕返回B执行完毕返回最后是A执行完毕。 所以子程序调用是通过栈实现的一个线程就是执行一个子程序。 子程序调用总是一个入口一次返回调用顺序是明确的。 协程的特点在于是一个线程执行那和多线程比协程有何优势 极高的执行效率因为子程序切换不是线程切换而是由程序自身控制因此没有线程切换的开销和多线程比线程数量越多协程的性能优势就越明显不需要多线程的锁机制因为只有一个线程也不存在同时写变量冲突在协程中控制共享资源不加锁只需要判断状态就好了所以执行效率比多线程高很多。 线程安全 如果你的代码所在的进程中有多个线程在同时运行而这些线程可能会同时运行这段代码。 如果每次运行结果和单线程运行的结果是一样的而且其他的变量的值也和预期的是一样的就是线程安全的。 进程 在系统中正在运行的一个应用程序程序一旦运行就是进程是资源分配的最小单位。 在操作系统中能同时运行多个进程 开机的时候磁盘的内核镜像被导入内存作为一个执行副本成为内核进程。 进程可以分成用户态进程和内核态进程两类用户态进程通常是应用程序的副本内核态进程就是内核本身的进程。 如果用户态进程需要申请资源比如内存可以通过系统调用向内核申请。 每个进程都有独立的内存空间存放代码和数据段等程序之间的切换会有较大的开销 分时和调度 每个进程在执行时都会获得操作系统分配的一个时间片段如果超出这个时间就会轮到下一个进程线程执行。 注意现代操作系统都是直接调度线程不会调度进程。 分配时间片段 如下图所示进程 1 需要 2 个时间片段进程 2 只有 1 个时间片段进程 3 需要 3 个时间片段。 因此当进程 1 执行到一半时会先挂起然后进程 2 开始执行进程 2 一次可以执行完然后进程 3 开始执行不过进程 3 一次执行不完在执行了 1 个时间片段后进程 1 开始执行就这样如此周而复始这个就是分时技术。 创建进程 用户想要创建一个进程最直接的方法就是从命令行执行一个程序或者双击打开一个应用但对于程序员而言显然需要更好的设计。 首先应该有 API 打开应用比如可以通过函数打开某个应用 另一方面如果程序员希望执行完一段代价昂贵的初始化过程后将当前程序的状态复制好几份变成一个个单独执行的进程那么操作系统提供了 fork 指令。 也就是说每次 fork 会多创造一个克隆的进程这个克隆的进程所有状态都和原来的进程一样但是会有自己的地址空间。 如果要创造 2 个克隆进程就要 fork 两次。 那如果我就是想启动一个新的程序呢 操作系统提供了启动新程序的 API。 如果我就是想用一个新进程执行一小段程序比如说每次服务端收到客户端的请求时我都想用一个进程去处理这个请求。 如果是这种情况建议你不要单独启动进程而是使用线程。 因为进程的创建成本实在太高了因此不建议用来做这样的事情要创建条目、要分配内存特别是还要在内存中形成一个个段分成不同的区域。所以通常我们更倾向于多创建线程。 不同程序语言会自己提供创建线程的 API比如 Java 有 Thread 类go 有 go-routine注意不是协程是线程。 进程状态 创建状态 进程由创建而产生创建进程是一个非常复杂的过程一般需要通过多个步骤才能完成如首先由进程申请一个空白的进程控制块(PCB)并向PCB中填写用于控制和管理进程的信息然后为该进程分配运行时所必须的资源最后把该进程转入就绪状态并插入到就绪队列中 就绪状态 这是指进程已经准备好运行的状态即进程已分配到除CPU以外所有的必要资源后只要再获得CPU便可立即执行如果系统中有许多处于就绪状态的进程通常将它们按照一定的策略排成一个队列该队列称为就绪队列有执行资格没有执行权的进程 运行状态 这里指进程已经获取CPU其进程处于正在执行的状态。对任何一个时刻而言在单处理机的系统中只有一个进程处于执行状态而在多处理机系统中有多个进程处于执行状态既有执行资格又有执行权的进程 阻塞状态 这里是指正在执行的进程由于发生某事件如I/O请求、申请缓冲区失败等暂时无法继续执行的状态即进程执行受到阻塞此时引起进程调度操作系统把处理机分配给另外一个就绪的进程而让受阻的进程处于暂停的状态一般将这个暂停状态称为阻塞状态 终止状态 进程间通信IPC 每个进程各自有不同的用户地址空间任何一个进程的全局变量在另一个进程中都看不到所以进程之间要交换数据必须通过内核在内核中开辟一块缓冲区进程1把数据从用户空间拷到内核缓冲区进程2再从内核缓冲区把数据读走内核提供的这种机制称为进程间通信 管道/匿名管道 管道是半双工的数据只能向一个方向流动需要双方通信时需要建立起两个管道。 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程); 单独构成一种独立的文件系统管道对于管道两端的进程而言就是一个文件但它不是普通的文件它不属于某种文件系统而是自立门户单独构成一种文件系统并且只存在与内存中。 数据的读出和写入一个进程向管道中写的内容被管道另一端的进程读出写入的内容每次都添加在管道缓冲区的末尾并且每次都是从缓冲区的头部读出数据。 有名管道(FIFO) 匿名管道由于没有名字只能用于亲缘关系的进程间通信。 为了克服这个缺点提出了有名管道(FIFO)。 有名管道不同于匿名管道之处在于它提供了一个路径名与之关联以有名管道的文件形式存在于文件系统中这样即使与有名管道的创建进程不存在亲缘关系的进程只要可以访问该路径就能够彼此通过有名管道相互通信因此通过有名管道不相关的进程也能交换数据。 信号 信号是Linux系统中用于进程间互相通信或者操作的一种机制信号可以在任何时候发给某一进程而无需知道该进程的状态。 如果该进程当前并未处于执行状态则该信号就有内核保存起来知道该进程回复执行并传递给它为止。 如果一个信号被进程设置为阻塞则该信号的传递被延迟直到其阻塞被取消是才被传递给进程。 消息队列 消息队列是存放在内核中的消息链表每个消息队列由消息队列标识符表示。 与管道无名管道只存在于内存中的文件命名管道存在于实际的磁盘介质或者文件系统不同的是消息队列存放在内核中只有在内核重启(即操作系统重启)或者显示地删除一个消息队列时该消息队列才会被真正的删除。 另外与管道不同的是消息队列在某个进程往一个队列写入消息之前并不需要另外某个进程在该队列上等待消息的到达 共享内存 使得多个进程可以直接读写同一块内存空间是最快的可用IPC形式是针对其他通信机制运行效率较低而设计的。 为了在多个进程间交换信息内核专门留出了一块内存区可以由需要访问的进程将其映射到自己的私有地址空间进程就可以直接读写这一块内存而不需要进行数据的拷贝从而大大提高效率。 由于多个进程共享一段内存因此需要依靠某种同步机制如信号量来达到进程间的同步及互斥。 共享内存示意图 一旦这样的内存映射到共享它的进程的地址空间这些进程间数据传递不再涉及到内核换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。 信号量 信号量是一个计数器用于多进程对共享数据的访问信号量的意图在于进程间同步。 为了获得共享资源进程需要执行下列操作 创建一个信号量这要求调用者指定初始值对于二值信号量来说它通常是1也可是0。 等待一个信号量该操作会测试这个信号量的值如果小于0就阻塞也称为P操作。 挂出一个信号量该操作将信号量的值加1也称为V操作。 套接字(Socket) 套接字是一种通信机制凭借这种机制客户/服务器即要进行通信的进程系统的开发工作既可以在本地单机上进行也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。 信号 信号是进程间通信机制中唯一的异步通信机制可以看作是异步通知通知接收信号的进程有哪些事情发生了。 也可以简单理解为信号是某种形式上的软中断 可运行kill -l查看Linux支持的信号列表 kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN1 36) SIGRTMIN2 37) SIGRTMIN3 38) SIGRTMIN4 39) SIGRTMIN5 40) SIGRTMIN6 41) SIGRTMIN7 42) SIGRTMIN8 43) SIGRTMIN9 44) SIGRTMIN10 45) SIGRTMIN11 46) SIGRTMIN12 47) SIGRTMIN13 48) SIGRTMIN14 49) SIGRTMIN15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX 进程同步 临界区 通过对多线程的串行化来访问公共资源或一段代码速度快适合控制数据访问 优点保证在某一时刻只有一个线程能访问数据的简便办法 缺点虽然临界区同步速度很快但却只能用来同步本进程内的线程而不可用来同步多个进程中的线程 互斥量 为协调共同对一个共享资源的单独访问而设计的 互斥量跟临界区很相似比临界区复杂互斥对象只有一个只有拥有互斥对象的线程才具有访问资源的权限 优点使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享而且可以在不同应用程序的线程之间实现对资源的安全共享 信号量 为控制一个具有有限数量用户资源而设计它允许多个线程在同一时刻访问同一资源但是需要限制在同一时刻访问此资源的最大线程数目互斥量是信号量的一种特殊情况当信号量的最大资源数1就是互斥量了 信号量Semaphore是一个整型变量可以对其执行 down 和 up 操作也就是常见的 P 和 V 操作 down : 如果信号量大于 0 执行 -1 操作如果信号量等于 0进程睡眠等待信号量大于 0 up 对信号量执行 1 操作唤醒睡眠的进程让其完成 down 操作。 down 和 up 操作需要被设计成原语不可分割通常的做法是在执行这些操作的时候屏蔽中断。 如果信号量的取值只能为 0 或者 1那么就成为了 互斥量Mutex 0 表示临界区已经加锁1 表示临界区解锁。 事件 用来通知线程有一些事件已发生从而启动后继任务的开始 优点事件对象通过通知操作的方式来保持线程的同步并且可以实现不同进程中的线程同步操作 管程 管程有一个重要特性在一个时刻只能有一个进程使用管程。 进程在无法继续执行的时候不能一直占用管程否则其它进程永远不能使用管程。 管程引入了 条件变量 以及相关的操作wait() 和 signal() 来实现同步操作。 对条件变量执行 wait() 操作会导致调用进程阻塞把管程让出来给另一个进程持有。 signal() 操作用于唤醒被阻塞的进程。 使用信号量机制实现的生产者消费者问题需要客户端代码做很多控制而管程把控制的代码独立出来不仅不容易出错也使得客户端代码调用更容易。 上下文切换 对于单核单线程CPU而言在某一时刻只能执行一条CPU指令。 上下文切换(Context Switch)是一种将CPU资源从一个进程分配给另一个进程的机制。 从用户角度看计算机能够并行运行多个进程这恰恰是操作系统通过快速上下文切换造成的结果。 在切换的过程中操作系统需要先存储当前进程的状态(包括内存空间的指针当前执行完的指令等等)再读入下一个进程的状态然后执行此进程。 进程调度算法 先来先服务调度算法 该算法既可用于作业调度也可用于进程调度当在作业调度中采用该算法时每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业将它们调入内存为它们分配资源、创建进程然后放入就绪队列 短作业优先调度算法 从后备队列中选择一个或若干个估计运行时间最短的作业将它们调入内存运行 时间片轮转法 每次调度时把CPU分配给队首进程并令其执行一个时间片时间片的大小从几ms到几百ms当执行的时间片用完时由一个计时器发出时钟中断请求调度程序便据此信号来停止该进程的执行并将它送往就绪队列的末尾 然后再把处理机分配给就绪队列中新的队首进程同时也让它执行一个时间片这样就可以保证就绪队列中的所有进程在一给定的时间内均能获得一时间片的处理机执行时间 最短剩余时间优先 最短作业优先的抢占式版本按剩余运行时间的顺序进行调度当一个新的作业到达时其整个运行时间与当前进程的剩余时间作比较。 如果新的进程需要的时间更少则挂起当前进程运行新的进程。否则新的进程等待。 多级反馈队列调度算法 前面介绍的几种进程调度的算法都有一定的局限性如短进程优先的调度算法仅照顾了短进程而忽略了长进程多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业迅速完成因而它是目前被公认的一种较好的进程调度算法UNIX 操作系统采取的便是这种调度算法。 举例 多级队列就是多个队列执行调度先考虑最简单的两级模型 上图中设计了两个优先级不同的队列从下到上优先级上升上层队列调度紧急任务下层队列调度普通任务。 只要上层队列有任务下层队列就会让出执行权限。 低优先级队列可以考虑抢占 优先级队列的方式实现这样每次执行一个时间片段就可以判断一下高优先级的队列中是否有任务。 高优先级队列可以考虑用非抢占每个任务执行完才执行下一个 优先级队列实现这样紧急任务优先级有个区分如果遇到十万火急的情况就可以优先处理这个任务。 上面这个模型虽然解决了任务间的优先级问题但是还是没有解决短任务先行的问题可以考虑再增加一些队列让级别更多。 比如下图这个模型 紧急任务仍然走高优队列非抢占执行。 普通任务先放到优先级仅次于高优任务的队列中并且只分配很小的时间片如果没有执行完成说明任务不是很短就将任务下调一层。 下面一层最低优先级的队列中时间片很大长任务就有更大的时间片可以用。 通过这种方式短任务会在更高优先级的队列中执行完成长任务优先级会下调也就类似实现了最短作业优先的问题。 实际操作中可以有 n 层一层层把大任务筛选出来最长的任务放到最闲的时间去执行要知道大部分时间 CPU 不是满负荷的。 优先级调度 为每个流程分配优先级首先执行具有最高优先级的进程依此类推具有相同优先级的进程以 FCFS 方式执行可以根据内存要求时间要求或任何其他资源要求来确定优先级。 守护进程 守护进程是脱离于终端并且在后台运行的进程脱离终端是为了避免在执行的过程中的信息在终端上显示并且进程也不会被任何终端所产生的终端信息所打断。 守护进程一般的生命周期是系统启动到系统停止运行。 Linux系统中有很多的守护进程最典型的就是我们经常看到的服务进程。 当然我们也经常会利用守护进程来完成很多的系统或者自动化任务。 孤儿进程 父进程早于子进程退出时候子进程还在运行子进程会成为孤儿进程Linux会对孤儿进程的处理把孤儿进程的父进程设为进程号为1的进程也就是由init进程来托管init进程负责子进程退出后的善后清理工作 僵尸进程 子进程执行完毕时发现父进程未退出会向父进程发送 SIGCHLD 信号但父进程没有使用 wait/waitpid 或其他方式处理 SIGCHLD 信号来回收子进程子进程变成为了对系统有害的僵尸进程 子进程退出后留下的进程信息没有被收集会导致占用的进程控制块PCB不被释放形成僵尸进程进程已经死去但是进程资源没有被释放掉 问题及危害 如果系统中存在大量的僵尸进程他们的进程号就会一直被占用但是系统所能使用的进程号是有限的系统将因为没有可用的进程号而导致系统不能产生新的进程 任何一个子进程(init除外)在exit()之后并非马上就消失掉而是留下一个称为僵尸进程(Zombie)的数据结构等待父进程处理这是每个子进程在结束时都要经过的阶段如果子进程在exit()之后父进程没有来得及处理这时用ps命令就能看到子进程的状态是Z。 如果父进程能及时处理可能用ps命令就来不及看到子进程的僵尸状态但这并不等于子进程不经过僵尸状态 产生僵尸进程的元凶其实是他们的父进程杀掉父进程僵尸进程就变为了孤儿进程便可以转交给 init 进程回收处理 死锁 产生原因 系统资源的竞争系统资源的竞争导致系统资源不足以及资源分配不当导致死锁。 进程运行推进顺序不合适进程在运行过程中请求和释放资源的顺序不当会导致死锁。 发生死锁的四个必要条件 互斥条件一个资源每次只能被一个进程使用即在一段时间内某资源仅为一个进程所占有此时若有其他进程请求该资源则请求进程只能等待 请求与保持条件进程已经保持了至少一个资源但又提出了新的资源请求时该资源已被其他进程占有此时请求进程被阻塞但对自己已获得的资源保持不放 不可剥夺条件:进程所获得的资源在未使用完毕之前不能被其他进程强行夺走即只能由获得该资源的进程自己来释放只能是主动释放) 循环等待条件: 若干进程间形成首尾相接循环等待资源的关系 这四个条件是死锁的必要条件只要系统发生死锁这些条件必然成立而只要上述条件之一不满足就不会发生死锁 只要我们破坏其中一个就可以成功避免死锁的发生 其中互斥这个条件我们没有办法破坏因为我们用锁为的就是互斥 对于占用且等待这个条件我们可以一次性申请所有的资源这样就不存在等待了。 对于不可抢占这个条件占用部分资源的线程进一步申请其他资源时如果申请不到可以主动释放它占有的资源这样不可抢占这个条件就破坏掉了。 对于循环等待这个条件可以靠按序申请资源来预防所谓按序申请是指资源是有线性顺序的申请的时候可以先申请资源序号小的再申请资源序号大的这样线性化后自然就不存在循环了。 处理方法 主要有以下四种方法 鸵鸟策略 死锁检测与死锁恢复 死锁预防破坏4个必要条件 死锁避免银行家算法 鸵鸟策略 把头埋在沙子里假装根本没发生问题。 因为解决死锁问题的代价很高因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。 当发生死锁时不会对用户造成多大影响或发生死锁的概率很低可以采用鸵鸟策略。 死锁检测 不试图阻止死锁而是当检测到死锁发生时采取措施进行恢复。 每种类型一个资源的死锁检测 每种类型多个资源的死锁检测 死锁恢复 利用抢占恢复 利用回滚恢复 通过杀死进程恢复 #哲学家进餐问题 五个哲学家围着一张圆桌每个哲学家面前放着食物。 哲学家的生活有两种交替活动吃饭以及思考。 当一个哲学家吃饭时需要先拿起自己左右两边的两根筷子并且一次只能拿起一根筷子。 如果所有哲学家同时拿起左手边的筷子那么所有哲学家都在等待其它哲学家吃完并释放自己手中的筷子导致死锁。 哲学家进餐问题可看作是并发进程并发执行时处理共享资源的一个有代表性的问题。 为了防止死锁的发生可以设置两个条件 必须同时拿起左右两根筷子 只有在两个邻居都没有进餐的情况下才允许进餐。 #银行家算法 银行家算法的命名是它可以用了银行系统当不能满足所有客户的需求时银行绝不会分配其资金。 当新进程进入系统时它必须说明其可能需要的每种类型资源实例的最大数量这一数量不可以超过系统资源的总和。 当用户申请一组资源时系统必须确定这些资源的分配是否处于安全状态如何安全则分配如果不安全那么进程必须等待指导某个其他进程释放足够资源为止。 安全状态 在避免死锁的方法中允许进程动态地申请资源但系统在进行资源分配之前应先计算此次资源分配的安全性若此次分配不会导致系统进入不安全状态则将资源分配给进程否则令进程等待 因此避免死锁的实质在于系统在进行资源分配时如何使系统不进入不安全状态 Fork函数 fork函数用于创建一个与当前进程一样的子进程所创建的子进程将复制父进程的代码段、数据段、BSS段、堆、栈等所有用户空间信息在内核中操作系统会重新为其申请一个子进程执行的位置。 fork系统调用会通过复制一个现有进程来创建一个全新的进程新进程被存放在一个叫做任务队列的双向循环链表中链表中的每一项都是类型为task_struct的进程控制块PCB的结构。 每个进程都由独特换不相同的进程标识符PID通过getpid()函数可获取当前进程的进程标识符通过getppid()函数可获得父进程的进程标识符。 一个现有的进程可通过调用fork函数创建一个新进程由fork创建的新进程称为子进程child processfork函数被调用一次但返回两次两次返回的唯一区别是子进程中返回0而父进程中返回子进程ID。 为什么fork会返回两次呢 因为复制时会复制父进程的堆栈段所以两个进程都停留在fork函数中等待返回因此会返回两次一个是在父进程中返回一次是在子进程中返回两次返回值是不一样的。 在父进程中将返回新建子进程的进程ID 在子进程中将返回0 若出现错误则返回一个负数 因此可以通过fork的返回值来判断当前进程是子进程还是父进程。 fork执行执行流程 当进程调用fork后控制转入内核内核将会做4件事儿 分配新的内存块和内核数据结构给子进程 将父进程部分数据结构内容数据空间、堆栈等拷贝到子进程 添加子进程到系统进程列表中 fork返回开始调度器调度 为什么pid在父子进程中不同呢 其实就相当于链表进程形成了链表父进程的pid指向子进程的进程ID因此子进程没有子进程所以PID为0这里的pid相当于链表中的指针。 为什么pid在父子进程中不同呢 其实就相当于链表进程形成了链表父进程的pid指向子进程的进程ID因此子进程没有子进程所以PID为0这里的pid相当于链表中的指针。 设备管理 磁盘调度算法 读写一个磁盘块的时间的影响因素有 旋转时间寻道时间实际的数据传输时间 其中寻道时间最长因此磁盘调度的主要目标是使磁盘的平均寻道时间最短。 先来先服务 FCFS, First Come First Served 按照磁盘请求的顺序进行调度优点是公平和简单缺点也很明显因为未对寻道做任何优化使平均寻道时间可能较长。 最短寻道时间优先SSTF, Shortest Seek Time First 优先调度与当前磁头所在磁道距离最近的磁道 虽然平均寻道时间比较低但是不够公平如果新到达的磁道请求总是比一个在等待的磁道请求近那么在等待的 磁道请求会一直等待下去也就是出现饥饿现象具体来说两边的磁道请求更容易出现饥饿现象。 电梯算法SCAN 电梯总是保持一个方向运行直到该方向没有请求为止然后改变运行方向 电梯算法扫描算法和电梯的运行过程类似总是按一个方向来进行磁盘调度直到该方向上没有未完成的磁盘 请求然后改变方向因为考虑了移动方向因此所有的磁盘请求都会被满足解决了 SSTF 的饥饿问题 内存管理 逻辑地址和物理地址 我们编程一般只有可能和逻辑地址打交道比如在 C 语言中指针里面存储的数值就可以理解成为内存里的一个地址这个地址也就是我们说的逻辑地址逻辑地址由操作系统决定。 物理地址指的是真实物理内存中地址更具体一点来说就是内存地址寄存器中的地址物理地址是内存单元真正的地址。 编译时只需确定变量x存放的相对地址是100 ( 也就是说相对于进程在内存中的起始地址而言的地址)。 CPU想要找到x在内存中的实际存放位置只需要用进程的起始地址100即可。 相对地址又称逻辑地址绝对地址又称物理地址。 内存管理有哪几种方式 块式管理将内存分为几个固定大小的块每个块中只包含一个进程如果程序运行需要内存的话操作系统就分配给它一块如果程序运行只需要很小的空间的话分配的这块内存很大一部分几乎被浪费了这些在每个块中未被利用的空间我们称之为碎片。页式管理把主存分为大小相等且固定的一页一页的形式页较小相对相比于块式管理的划分力度更大提高了内存利用率减少了碎片页式管理通过页表对应逻辑地址和物理地址。 段式管理 页式管理虽然提高了内存利用率但是页式管理其中的页实际并无任何实际意义 段式管理把主存分为一段段的每一段的空间又要比一页的空间小很多 段式管理通过段表对应逻辑地址和物理地址。段页式管理机制段页式管理机制结合了段式管理和页式管理的优点简单来说段页式管理机制就是把主存先分成若干段每个段又分成若干页也就是说段页式管理机制中段与段之间以及段的内部的都是离散的。 虚拟地址 现代处理器使用的是一种称为**虚拟寻址(Virtual Addressing)**的寻址方式 使用虚拟寻址CPU 需要将虚拟地址翻译成物理地址这样才能访问到真实的物理内存。 实际上完成虚拟地址转换为物理地址转换的硬件是 CPU 中含有一个被称为**内存管理单元Memory Management Unit, MMU**的硬件 为什么要有虚拟地址空间 没有虚拟地址空间的时候程序都是直接访问和操作的都是物理内存。 但是这样有什么问题 1、用户程序可以访问任意内存寻址内存的每个字节这样就很容易破坏操作系统造成操作系统崩溃。 2、想要同时运行多个程序特别困难比如你想同时运行一个微信和一个 QQ 音乐都不行为什么呢举个简单的例子微信在运行的时候给内存地址 1xxx 赋值后QQ 音乐也同样给内存地址 1xxx 赋值那么 QQ 音乐对内存的赋值就会覆盖微信之前所赋的值这就造成了微信这个程序就会崩溃。 通过虚拟地址访问内存有以下优势 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。不同进程使用的虚拟地址彼此隔离一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。 MMU如何把虚拟地址翻译成物理地址的 对于每个程序内存管理单元MMU都为其保存一个页表该页表中存放的是虚拟页面到物理页面的映射。 每当为一个虚拟页面寻找到一个物理页面之后就在页表里增加一条记录来保留该映射关系当然随着虚拟页面进出物理内存页表的内容也会不断更新变化。 虚拟内存 很多时候我们使用点开了很多占内存的软件这些软件占用的内存可能已经远远超出了我们电脑本身具有的物理内存 通过 虚拟内存 可以让程序可以拥有超过系统物理内存大小的可用内存空间。 另外虚拟内存为每个进程提供了一个一致的、私有的地址空间它让每个进程产生了一种自己在独享主存的错觉每个进程拥有一片连续完整的内存空间这样会更加有效地管理内存并减少出错。 虚拟内存是计算机系统内存管理的一种技术我们可以手动设置自己电脑的虚拟内存 虚拟内存的重要意义是它定义了一个连续的虚拟地址空间并且 把内存扩展到硬盘空间 虚拟内存的实现有以下三种方式 请求分页存储管理 请求分页是目前最常用的一种实现虚拟存储器的方法请求分页存储管理系统中在作业开始运行之前仅装入当前要执行的部分段即可运行假如在作业运行的过程中发现要访问的页面不在内存则由处理器通知操作系统按照对应的页面置换算法将相应的页面调入到主存同时操作系统也可以将暂时不用的页面置换到外存中。请求分段存储管理 请求分段储存管理方式就如同请求分页储存管理方式一样在作业开始运行之前仅装入当前要执行的部分段即可运行在执行过程中可使用请求调入中断动态装入要访问但又不在内存的程序段当内存空间已满而又需要装入新的段时根据置换功能适当调出某个段以便腾出空间而装入新的段。请求段页式存储管理 不管是上面那种实现方式我们一般都需要 一定容量的内存和外存在载入程序的时候只需要将程序的一部分装入内存而将其他部分留在外存然后程序就可以执行了 缺页中断 如果需执行的指令或访问的数据尚未在内存称为缺页或缺段则由处理器通知操作系统将相应的页面或段调入到内存然后继续执行程序 在分页系统中一个虚拟页面既有可能在物理内存也有可能保存在磁盘上。 如果CPU发出的虚拟地址对应的页面不在物理内存就将产生一个缺页中断而缺页中断服务程序负责将需要的虚拟页面找到并加载到内存。 缺页中断的处理步骤如下省略了中间很多的步骤只保留最核心的几个步骤 页面置换算法 当发生缺页中断时如果当前内存中并没有空闲的页面操作系统就必须在内存选择一个页面将其移出内存以便为即将调入的页面让出空间。 用来选择淘汰哪一页的规则叫做页面置换算法我们可以把页面置换算法看成是淘汰页面的规则 OPT 页面置换算法最佳页面置换算法 该置换算法所选择的被淘汰页面将是以后永不使用的或者是在最长时间内不再被访问的页面这样可以保证获得最低的缺页率但由于人们目前无法预知进程在内存下的若千页面中哪个是未来最长时间内不再被访问的因而该算法无法实现一般作为衡量其他置换算法的方法。 FIFOFirst In First Out 页面置换算法先进先出页面置换算法 : 总是淘汰最先进入内存的页面即选择在内存中驻留时间最久的页面进行淘汰。 LRU Least Currently Used页面置换算法最近最久未使用页面置换算法 LRU算法赋予每个页面一个访问字段用来记录一个页面自上次被访问以来所经历的时间 T当须淘汰一个页面时选择现有页面中其 T 值最大的即最近最久未使用的页面予以淘汰。 LFU Least Frequently Used页面置换算法最少使用页面置换算法 : 该置换算法选择在之前时期使用最少的页面作为淘汰页。 局部性原理 局部性原理是虚拟内存技术的基础正是因为程序运行具有局部性原理才可以只装入部分程序到内存就开始运行。 局部性原理表现在以下两个方面 时间局部性 如果程序中的某条指令一旦执行不久以后该指令可能再次执行如果某数据被访问过不久以后该数据可能再次被访问产生时间局部性的典型原因是由于在程序中存在着大量的循环操作。空间局部性 一旦程序访问了某个存储单元在不久之后其附近的存储单元也将被访问即程序在一段时间内所访问的地址可能集中在一定的范围之内这是因为指令通常是顺序存放、顺序执行的数据也一般是以向量、数组、表等形式簇聚存储的。 时间局部性是通过将近来使用的指令和数据保存到高速缓存存储器中并使用高速缓存的层次结构实现。 空间局部性通常是使用较大的高速缓存并将预取机制集成到高速缓存控制逻辑中实现。 页表 操作系统将虚拟内存分块每个小块称为一个页Page真实内存也需要分块每个小块我们称为一个 Frame。 Page 到 Frame 的映射需要一种叫作页表的结构。 上图展示了 Page、Frame 和页表 PageTable三者之间的关系。 Page 大小和 Frame 大小通常相等页表中记录的某个 Page 对应的 Frame 编号。 页表也需要存储空间比如虚拟内存大小为 10G Page 大小是 4K那么需要 10G/4K 2621440 个条目。 如果每个条目是 64bit那么一共需要 20480K 20M 页表操作系统在内存中划分出小块区域给页表并负责维护页表。 页表维护了虚拟地址到真实地址的映射。 每次程序使用内存时需要把虚拟内存地址换算成物理内存地址换算过程分为以下 3 个步骤 通过虚拟地址计算 Page 编号 查页表根据 Page 编号找到 Frame 编号 将虚拟地址换算成物理地址。 多级页表 引入多级页表的主要目的是为了避免把全部页表一直放在内存中占用过多空间特别是那些根本就不需要的页表就不需要保留在内存中 一级页表 假如物理内存中一共有1048576个页那么页表就需要总共就是1048576 * 4B 4M。 也就是说我需要4M连续的内存来存放这个页表也就是一级页表。 随着虚拟地址空间的增大存放页表所需要的连续空间也会增大在操作系统内存紧张或者内存碎片较多时这无疑会带来额外的开销。 页表寻址是用寄存器来确定一级页表地址的所以一级页表的地址必须指向确定的物理页否则就会出现错误所以如果用一级页表的话就必须把全部的页表都加载进去。 二级页表 而使用二级页表的话只需要加载一个页目录表(一级页表)大小为4K可以管理1024个二级页表。 可能你会有疑问这1024个二级页表也是需要内存空间的这下反而需要4MB4KB的内存反而更多了。 其实二级页表并不是一定要存在内存中的内存中只需要一个一级页表地址存在存器即可二级页表可以使用缺页中断从外存移入内存。 多级页表属于时间换空间的典型场景 快表 为了解决虚拟地址到物理地址的转换速度操作系统在页表方案基础之上引入了快表来加速虚拟地址到物理地址的转换 我们可以把快表理解为一种特殊的高速缓冲存储器Cache其中的内容是页表的一部分或者全部内容作为页表的 Cache它的作用与页表相似但是提高了访问速率由于采用页表做地址转换读写内存数据时 CPU 要访问两次主存有了快表有时只要访问一次高速缓冲存储器一次主存这样可加速查找并提高指令执行速度。 使用快表之后的地址转换流程是这样的 根据虚拟地址中的页号查快表如果该页在快表中直接从快表中读取相应的物理地址如果该页不在快表中就访问内存中的页表再从页表中得到物理地址同时将页表中的该映射表项添加到快表中当快表填满后又要登记新页时就按照一定的淘汰策略淘汰掉快表中的一个页 内存管理单元 在 CPU 中一个小型的设备——内存管理单元MMU 当 CPU 需要执行一条指令时如果指令中涉及内存读写操作CPU 会把虚拟地址给 MMUMMU 自动完成虚拟地址到真实地址的计算然后MMU 连接了地址总线帮助 CPU 操作真实地址。 在不同 CPU 的 MMU 可能是不同的因此这里会遇到很多跨平台的问题。 解决跨平台问题不但有繁重的工作量更需要高超的编程技巧。 动态分区分配算法 内存分配算法大体来说分为连续式分配 与 非连续式分配 连续式分配就是把所以要执行的程序 完整的有序的 存入内存连续式分配又可以分为固定分区分配 和 动态分区分配 非连续式分配就是把要执行的程序按照一定规则进行拆分显然这样更有效率现在的操作系统通常也都是采用这种方式分配内存 所谓动态分区分配就是指内存在初始时不会划分区域而是会在进程装入时根据所要装入的进程大小动态地对内存空间进行划分以提高内存空间利用率降低碎片的大小 动态分区分配算法有以下四种 首次适应算法First Fit 空闲分区以地址递增的次序链接分配内存时顺序查找找到大小满足要求的第一个空闲分区就进行分配 邻近适应算法Next Fit 又称循环首次适应法由首次适应法演变而成不同之处是分配内存时从上一次查找结束的位置开始继续查找 最佳适应算法Best Fit 空闲分区按容量递增形成分区链找到第一个能满足要求的空闲分区就进行分配 最坏适应算法Next Fit 又称最大适应算法空闲分区以容量递减的次序链接找到第一个能满足要求的空闲分区也就是最大的分区就进行分配 总结 首次适应不仅最简单通常也是最好最快不过首次适应算法会使得内存低地址部分出现很多小的空闲分区而每次查找都要经过这些分区因此也增加了查找的开销。 邻近算法试图解决这个问题但实际上它常常会导致在内存的末尾分配空间分裂成小的碎片它通常比首次适应算法结果要差。 最佳适应算法导致大量碎片最坏适应算法导致没有大的空间 内存覆盖 覆盖与交换技术是在程序用来扩充内存的两种方法。 早期的计算机系统中主存容量很小虽然主存中仅存放一道用户程序但是存储空间放不下用户进程的现象也经常发生这一矛盾可以用覆盖技术来解决。 覆盖的基本思想是 由于程序运行时并非任何时候都要访问程序及数据的各个部分尤其是大程序因此可以把用户空间分成一个固定区和若干个覆盖区。 将经常活跃的部分放在固定区其余部分按调用关系分段。 首先将那些即将要访问的段放入覆盖区其他段放在外存中在需要调用前系统再将其调入覆盖区替换覆盖区中原有的段。 覆盖技术的特点是打破了必须将一个进程的全部信息装入主存后才能运行的限制但当同时运行程序的代码量大于主存时仍不能运行。 内存交换 交换的基本思想 把处于等待状态或在CPU调度原则下被剥夺运行权利的程序从内存移到辅存把内存空间腾出来这一过程又叫换出 把准备好竞争CPU运行的程序从辅存移到内存这一过程又称为换入。 例如有一个CPU釆用时间片轮转调度算法的多道程序环境。 时间片到内存管理器将刚刚执行过的进程换出将另一进程换入到刚刚释放的内存空间中。 同时CPU调度器可以将时间片分配给其他已在内存中的进程。 每个进程用完时间片都与另一进程交换。 理想情况下内存管理器的交换过程速度足够快总有进程在内存中可以执行。 交换技术主要是在不同进程或作业之间进行而覆盖则用于同一个程序或进程中。 由于覆盖技术要求给出程序段之间的覆盖结构使得其对用户和程序员不透明所以对于主存无法存放用户程序的矛盾 现代操作系统是通过虚拟内存技术来解决的覆盖技术则已成为历史而交换技术在现代操作系统中仍具有较强的生命力。 常见面试题 进程、线程的区别 操作系统会以进程为单位分配系统资源CPU时间片、内存等资源进程是资源分配的最小单位。 调度线程作为CPU调度和分配的基本单位进程作为拥有资源的基本单位 并发性不仅进程之间可以并发执行同一个进程的多个线程之间也可并发执行 拥有资源 进程是拥有资源的一个独立单位线程不拥有系统资源但可以访问隶属于进程的资源。 进程所维护的是程序所包含的资源静态资源 如地址空间打开的文件句柄集文件系统状态信号处理handler等 线程所维护的运行相关的资源动态资源如运行栈调度相关的控制信息待处理的信号集等 系统开销 在创建或撤消进程时由于系统都要为之分配和回收资源导致系统的开销明显大于创建或撤消线程时的开销。 但是进程有独立的地址空间一个进程崩溃后在保护模式下不会对其它进程产生影响而线程只是一个进程中的不同执行路径。 线程有自己的堆栈和局部变量但线程之间没有单独的地址空间一个进程死掉就等于所有的线程死掉所以多进程的程序要比多线程的程序健壮但在进程切换时耗费资源较大效率要差一些。 一个进程可以创建多少线程 理论上一个进程可用虚拟空间是2G默认情况下线程的栈的大小是1MB所以理论上最多只能创建2048个线程。 如果要创建多于2048的话必须修改编译器的设置。 在一般情况下你不需要那么多的线程过多的线程将会导致大量的时间浪费在线程切换上给程序运行效率带来负面影响。 外中断和异常有什么区别 外中断是指由 CPU 执行指令以外的事件引起如 I/O 完成中断表示设备输入/输出处理已经完成处理器能够发送下一个输入/输出请求此外还有时钟中断、控制台中断等。 而异常时由 CPU 执行指令的内部事件引起如非法操作码、地址越界、算术溢出等。 解决Hash冲突四种方法 开放定址法 开放定址法就是一旦发生了冲突就去寻找下一个空的散列地址只要散列表足够大空的散列地址总能找到并将记录存入。 链地址法 将哈希表的每个单元作为链表的头结点所有哈希地址为i的元素构成一个同义词链表。即发生冲突时就把该关键字链在以该单元为头结点的链表的尾部。 再哈希法 当哈希地址发生冲突用其他的函数计算另一个哈希函数地址直到冲突不再产生为止。 建立公共溢出区 将哈希表分为基本表和溢出表两部分发生冲突的元素都放入溢出表中。 分页机制和分段机制有哪些共同点和区别 共同点 分页机制和分段机制都是为了提高内存利用率较少内存碎片。页和段都是离散存储的所以两者都是离散分配内存的方式。但是每个页和段中的内存是连续的。 区别 页的大小是固定的由操作系统决定而段的大小不固定取决于我们当前运行的程序。分页仅仅是为了满足操作系统内存管理的需求而段是逻辑信息的单位在程序中可以体现为代码段数据段能够更好满足用户的需要。分页是一维地址空间分段是二维的。 介绍一下几种典型的锁 读写锁 可以同时进行多个读 写者必须互斥只允许一个写者写也不能读者写者同时进行 写者优先于读者一旦有写者则后续读者必须等待唤醒时优先考虑写者 互斥锁 一次只能一个线程拥有互斥锁其他线程只有等待 互斥锁是在抢锁失败的情况下主动放弃CPU进入睡眠状态直到锁的状态改变时再唤醒而操作系统负责线程调度为了实现锁的状态发生改变时唤醒阻塞的线程或者进程需要把锁交给操作系统管理所以互斥锁在加锁操作时涉及上下文的切换。 互斥锁实际的效率还是可以让人接受的加锁的时间大概100ns左右而实际上互斥锁的一种可能的实现是先自旋一段时间当自旋的时间超过阀值之后再将线程投入睡眠中因此在并发运算中使用互斥锁每次占用锁的时间很短的效果可能不亚于使用自旋锁 条件变量 互斥锁一个明显的缺点是他只有两种状态锁定和非锁定。 而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足他常和互斥锁一起使用以免出现竞态条件。 当条件不满足时线程往往解开相应的互斥锁并阻塞线程然后等待条件发生变化。 一旦其他的某个线程改变了条件变量他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。 总的来说互斥锁是线程间互斥的机制条件变量则是同步机制。 自旋锁 如果进线程无法取得锁进线程不会立刻放弃CPU时间片而是一直循环尝试获取锁直到获取为止。 如果别的线程长时期占有锁那么自旋就是在浪费CPU做无用功但是自旋锁一般应用于加锁时间很短的场景这个时候效率比较高。 虽然它的效率比互斥锁高但是它也有些不足之处 自旋锁一直占用CPU在未获得锁的情况下一直进行自旋所以占用着CPU如果不能在很短的时间内获得锁无疑会使CPU效率降低。在用自旋锁时有可能造成死锁当递归调用时有可能造成死锁。 如何让进程后台运行 1.命令后面加上即可实际上这样是将命令放入到一个作业队列中了 2.ctrl z 挂起进程使用jobs查看序号在使用bg %序号后台运行进程 3.nohup 将标准输出和标准错误缺省会被重定向到 nohup.out 文件中忽略所有挂断SIGHUP信号 nohup ping www.ibm.com 4.运行指令前面 setsid使其父进程变成init进程不受SIGHUP信号的影响 [rootpvcent107 ~]## setsid ping www.ibm.com [rootpvcent107 ~]## ps -ef |grep www.ibm.com root 31094 1 0 07:28 ? 00:00:00 ping www.ibm.com root 31102 29217 0 07:29 pts/4 00:00:00 grep www.ibm.com上例中我们的进程 ID(PID)为31094而它的父 IDPPID为1即为 init 进程 ID并不是当前终端的进程 ID。 5.将命令 放在()括号中也可以是进程不受HUP信号的影响 [rootpvcent107 ~]## (ping www.ibm.com )异常和中断的区别 中断 当我们在敲击键盘的同时就会产生中断当硬盘读写完数据之后也会产生中断所以我们需要知道中断是由硬件设备产生的而它们从物理上说就是电信号之后它们通过中断控制器发送给CPU接着CPU判断收到的中断来自于哪个硬件设备这定义在内核中最后由CPU发送给内核有内核处理中断。 下面这张图显示了中断处理的流程 ![](https://img-blog.csdnimg.cn/6c0a43b5915e44bf8d05b6d871fd3b25.png 异常 CPU处理程序的时候一旦程序不在内存中会产生缺页异常当运行除法程序时当除数为0时又会产生除0异常。 异常是由CPU产生的同时它会发送给内核要求内核处理这些异常 下面这张图显示了异常处理的流程 相同点 最后都是由CPU发送给内核由内核去处理处理程序的流程设计上是相似的 不同点 产生源不相同异常是由CPU产生的而中断是由硬件设备产生的 内核需要根据是异常还是中断调用不同的处理程序中断不是时钟同步的这意味着中断可能随时到来异常由于是CPU产生的所以它是时钟同步的当处理中断时处于中断上下文中处理异常时处于进程上下文中
http://www.pierceye.com/news/801925/

相关文章:

  • 域名访问网站的知识站酷网官网进入
  • 6入空间网站免费观看wordpress转盘抽奖源码
  • 手机网站怎么开发工具网站开发中如何实现gps定位
  • sql2008做网站焦作网站建设哪家便宜
  • hao123网站源码制作2015最新仿制作链接怎么做
  • 做网站源代码需要买吗做任务得佣金的网站
  • 网站开发php还是jsp中式风格装修效果图
  • 外贸网站建设优化营销昆明网站建设加q.479185700
  • 正安网站建设推荐友情链接
  • 织梦仿asp网站石家庄市制作网站公司
  • 全国较好的网站建设公司织梦网做网站步骤
  • dedecms小说网站模板自动生成网站地图的工具
  • 中国比较好的设计网站php网站怎么做静态化
  • Dedecms手机网站源码wordpress小程序制作
  • 成都网站建设备案重庆找网站推广
  • 商务网站建设的调研wordpress 视频 全屏
  • 网站策划案例广州高端网站定制开发价格
  • 大连网站制作优选ls15227微信公众号小程序搭建
  • 个人空间网站建设报告网站建设培训哪个好
  • wap微信网站模板在线网络培训平台
  • asp网站怎么做三语网络推广计划书格式
  • 徽州网站建设企业网站备案域名信息
  • 广州建设信息网官方网站火车头采集wordpress发布
  • 易记域名网站大全火车票网站建设多少
  • 怎么将网站权重提上去怎样在网站图片做超级链接
  • 网站建设需求分析报告功能百度官方电话24小时
  • 扬州市城市建设监察支队网站做空比特币网站
  • 石家庄做网站 vtkj网站空间怎样设置用户名和密码
  • 自考网页制作与网站建设网站虚拟主机建设
  • 手机网站建站视频教程阿里云虚拟主机与网站吗