操作系统知识点梳理

1、 什么是操作系统?操作系统由什么组成?

操作系统是用户和计算机之间的界面。一方面操作系统管理着所有计算机系统资源,另一方面操作系统为用户提供了一个抽象概念上的计算机。在操作系统的帮助下,用户使用计算机时,避免了对计算机系统硬件的直接操作。

操作系统的主要组成部分:进程和线程的管理,存储管理,设备管理,文件管理。

2、 select、 poll和epoll的区别?

select、poll、epoll都是IO多路复用的机制
FD:socket描述符

IO多路复用适用如下场合:
(1)当客户处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用
(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用
(4)如果一个服务器既要处理TCP,又要处理UDP,一般要使用I/O复用
(5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。

与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

目前支持I/O多路复用的系统调用有select, pselect, poll, epoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select、pselect、poll、epoll本质上都是同步I/O,因为他们都需要在读写时间就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

一、select的特点:
(1)select目前几乎在所有的平台上支持,良好跨平台支持是它的一个优点,
(2)select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,它由FD_SETSIZE设置,默认值是1024。
一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max查看。32位机默认是1024个,64位机默认是2048。
(3)对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低。
当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个socket来完成调度,不管哪个socket是活跃的还是不活跃的,通通都遍历一遍,这会浪费很多CPU时间。(epoll做了改进,只轮询活跃的)
(4)需要维护一个用来存放大量FD的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。

二、poll的特点
基本原理
:poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个FD对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有FD后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历FD,这个过程经历了多次无谓的遍历(跟select一样不管socket活跃不活跃,均会遍历)

与select的区别:它没有最大连接数的限制,原因是它是基于链表来存储的
缺点:
(1)大量的FD数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
(2)poll还有一个特点是“水平触发”,如果报告了FD后,没有被处理,那么下次poll时会再次报告该FD

总结:select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一个时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

三、epoll特点
epoll相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些FD刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

epoll的优点
(1)没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)
(2)效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数。即epoll最大的优点就在于它只管“活跃”的连接,而根连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll
(3)内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
(4)在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。(此处去掉了遍历文件描述符,而是通过监听回调的机制,这正是epoll的魅力所在)

注意:如果没有大量的idle-connection或者dead-connection(即不活跃或者空闲的线程)时,epoll的效率并不会比select/poll高很多,但是当遇到大量的idle-connection,就会发现epoll的效率大大高于select/poll。

直观的比较:
1、支持一个进程所能打开的最大连接数

select 单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是32*32,同理64位机器上FD_SETSIZE为32*64),当然我们可以进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试
poll poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的
epoll 虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,

2、消息传递方式

select 内核需要将消息传递到用户空间,都需要内核拷贝动作
poll 同上
epoll epoll通过内核和用户空间共享一块内存来实现的

综上,在选择select, poll, epoll时要根据具体的使用场合以及这三种方式的自身特点:
(1) 表面上看epoll的性能最好,但是在连接少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
(2) select低效是因为每次它都要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。

epoll的lt et模式
LT(Level Triggered)水平触发模式
:(是标准模式)当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部度写完(如读写缓冲区大小),那么下次调用epoll_wait()时,它还会通知你在上次没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你。如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率。

ET(Edge Triggered)边缘触发模式:(也称高效模式)当被监控的文件描述符上有可读写时间发生时,epoll_wait()会通知处理程序去读写,如果这次没有把数据全部度写完(如读写缓冲区大小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你。这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符。

使用Linux epoll模型,水平触发模型;当socket可写时,会不停的触发socket可写的事件,如何处理?
第一种最普通的方式:
需要向socket写数据的时候才把socket加入epoll,等待可写事件
接受到可写事件后,调用write或者send发送数据
当所有数据都写完后,把socket移出epoll
这种方式的缺点是,即使发送很少的数据,也要把socket加入epoll,写完后再移出epoll,有一定操作代价

一种改进的方式:
开始不把socket加入epoll,需要向socket写数据的时候,直接调用write或者send发送数据。如果返回EAGAIN,把socket加入epoll,在epoll的驱动下写数据,全部数据发送完毕后,再移出epoll
这种方式的优点是:数据不多的时候可以避免epoll的事件处理,提高效率。

EAGAIN:如果你连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返回,read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。

3、 什么是轮询?

轮询是一种CPU决策如何提供周边设备服务的方式,又称“程控输出入”
轮询法的概念是由CPU定时发出询问,依序询问每一个周边设备是否需要其服务,有即给予服务,服务结束后再问下一个周边,接着不断周而复始。

4、 进程的调度算法有哪些?

(1)先来先服务(FCFS,first come first serve)
(2)短作业优先(SJF,shortest job first)
(3)最高优先权调度(Priority Scheduling)
(4)时间片轮转(RR,round robin)
(5)多级反馈队列调度(multilevel feedback queue)

实时调度算法:
(1)最高截止时间优先EDF
(2)最低松弛度优先LLF

5、 什么是死锁?

四个必要条件:
(1)互斥条件:系统存在着临界资源,资源不能被共享,只能由一个进程使用。
(2)请求与保持条件:进程已获得了一些资源,但因请求其他资源被阻塞时,对已获得的资源保持不放。
(3)不可抢占条件:有些系统资源是不可抢占的,当某个进程已获得这种资源后,系统不能强行收回,只能由进程使用完时自己释放。
(4)循环等待条件:若干进程之间行为一种头尾相接的循环等待资源关系。

以上四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

解决死锁的基本方法
(1)预防死锁:
- 资源一次性分配:(即破坏请求和保持条件)
- 可剥夺资源:即当某进程新的资源未满足时,释放已占有的资源(破坏不可剥夺条件)
- 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏循环等待条件)

(2)避免死锁
最具有代表性的避免死锁算法是银行家算法

(3)解除死锁
当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常用的方法有:
- 剥夺资源:从其他进程剥夺足够数量的资源给死锁进程,以解除死锁状态
- 撤销进程:可以直接撤销死锁进程或撤销代价最小的进程,直至有足够的资源可用,死锁状态消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。

6、 程序编译与链接的过程是怎样的?

以c语言为例

build过程可以分解为4个步骤:预处理,编译,汇编,链接

(1)编译预处理
预处理过程主要处理那些源文件中的以”#”开始的预编译指令,主要处理规则有:
1. 将所有的”#define”删除,并展开所用的宏定义
2. 处理所有条件预编译指令,比如”#if”、”#ifndef”、”#elif”、”#endif”。
3. 处理”#include”预编译指令,将所包含的文件插入到该编译指令的位置,注:此过程是递归进行的。
4. 删除所有注释
5. 添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或警告时可显示行号。
6. 保留所有的#pragma编译器指令。

(2)编译
编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分。
词法分析->语法分析->语义分析->中间代码生成->代码优化->目标代码生成->符号表管理->…

(3)汇编
汇编器是将汇编代码转化为机器可以执行的指令,每一条汇编语句几乎都是一条机器执行。经过编译、链接、汇编输出的文件称为目标文件。

(4)链接
链接的主要内容就是把各个模块之间相互引用的部分处理好,使各个模块可以正确的拼接。链接的主要过程包括地址和空间的分配、符号决议和重定位等步骤。

7、 静态链接和动态链接的区别

静态库里的代码在编译期就会嵌入可执行文件。
动态库在程序运行时,动态加载到内存,或者按照需要,随时被加载到内存。

动态库链接时,会在运行时选择需要的部分进行编译,生成的可执行文件会比较小,而且容易后续的更新。
静态编译会把所有的函数等都嵌入到可执行文件中,可以直接在任何电脑上直接运行,同样文件会远远大于动态链接的。

特点
动态库
(1)共享:多个应用程序可以使用同一个动态库,启动多个应用程序的时候,只需要将动态库加载到内存一次即可。
(2)开发模块好:要求设计者对功能划分的比较好
静态库:代码的装载速度快,执行速度也比较快,因为编译时它只会把你需要的那部分链接进去,应用程序相对比较大。但是如果多个应用程序使用的话,会被装载多次,浪费内存。

8、 分页存储和分段存储有什么区别?

(1)定义
分页
:用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。
分段:将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。

(2)分页和分段的主要区别

  1. 页式内容的物理单位,分页式为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要。段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现共享,满足用户的需要。
  2. 页的大小固定,由系统确定,将逻辑地址划分为页号和页内地址是由机器硬件实现的。而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时根据信息的性质来划分。
  3. 分页的作业地址空间是一维的。分段的地址空间是二维的。

(3)Windows内存管理方式:段存储,页存储,段页存储
分页存储管理基本思想
用户程序的逻辑地址空间被划分成若干固定大小的区域,称为“页”或者“页面”,相应地,内存物理空间也分成相对应的若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。

分段存储管理基本思想
将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。

段页式存储管理基本思想
分页系统能有效地提高内存的利用率,而分段系统能反映程序的逻辑结构,便于段的共享与保护,将分页与分段两种存储方式结合起来,就形成了段页式存储管理方式。在段页式存储管理系统中,作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号,然后再将每段分成若干个大小相等的页。对于主存空间也分成大小相等的页,主存的分配以页为单位。

9、 进程和线程的区别?

(1)线程是进程中的一个实体,线程是CPU调度和分派的最小单位。线程自己基本上不拥有系统资源,只拥有一点运行时必不可少的资源(比如程序计数器、一组寄存器和栈)。一个进程中的所有线程共享该进程所拥有的全部资源。
(2)线程是属于进程的,当进程退出后,该进程中所产生的线程都会被强制退出并清除。线程占用的资源要少于进程所占用的资源。进程和线程都可以有优先级。
(3)进程间可以通过IPC通信,但线程不可以。
(4)一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
(5)同一个进程中的两段代码不能够同时执行,除非引入线程。

10、 怎么理解线程安全?

线程安全是指多线程访问同一代码,不会产生不确定的结果(即不会存在二义性)。编写线程安全的代码时依靠线程同步。

线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作而无写操作,一般来说,这个全局变量时线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

11、 多线程如何同步?

线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。

线程互斥是对于共享的进程系统资源,在各单个线程访问时的排他性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其他要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。

线程间的同步方法大体可分为两类:用户模式和内核模式
内核模式:指利用系统内核对象的单一性来进行同步,使用时需要切换内核态和用户态。
用户模式:就是不要切换到内核态,只在用户态完成操作。

用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区
内核模式下的方法有:事件,信号量,互斥量

(1)临界区
保证在某一时刻只有一个线程能访问数据的简便方法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。
(2)事件
事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。
(3)互斥量
互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应该将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。
互斥量比临界区复杂,因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的完全共享,而且可以在不同应用程序的线程之间实现对资源的完全共享。
(4)信号量
信号允许多个线程同时使用共享资源
,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。

总结
综上所述,当在同一进程中的多线程同步时,临界区是效率最高的,基本不需要什么开销。而内核对象由于要进行用户态和内核态的切换,开销较大,但是内核对象由于可以命名,因此它们同时可以用于进程间的同步。另外,值得一提的是,信号量可以设置允许访问资源的线程或进程个数,而不仅仅是只允许单个线程或进程访问资源。

12、 堆和栈有什么区别?

一个由c/c++编译的程序占用的内存分为以下几个部分
(1)栈区:由编译器自动分配释放,存放函数的参数值,局部变量的值等。
(2)堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。比如new, malloc等申请的内存就在堆区。
(3)全局区(静态区):全局变量和静态变量的存储是放在一块的。
(4)文字常量区:常量字符串就是放在这里的。程序结束后由系统释放。
(5)程序代码区:存放函数体的二进制代码。

举个栗子:

#include<iostream>
using namespace std;
int a = 0;  //全局初始化区 
char *p1;  //全局未初始化区 
int main(){
    int b;  //栈    
    char s[] = "abc";  //栈   
    char *p2;  //栈    
    char *p3 = "123456";  //123456在常量区,p3在栈上    
    static int c = 0;  //全局(静态)初始化区    
    p1 = (char *)malloc(10);
    p2 = (char *)malloc(20);
    //分配得来的10和20字节的区域就在堆区    
    strcpy(p1, "123456");  //123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

申请大小的限制:
(1)在windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。所以栈顶的地址和栈的最大容量是系统预先规定好的。在windows下,栈的大下是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
(2)堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。

因此,堆获得的空间比较灵活,也比较大

申请效率的比较

  • 栈是由系统自动分配,速度较快。但程序员是无法控制的。
  • 堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

13、 进程间通讯的方式有哪些?各有什么优缺点?

进程间通信又称IPC(Inter-Process Communication),指多个进程之间相互通信交换信息的方法。

根据通信时信息量大小的不同,可以将进程通信划分为两个类型。
(1)低级通信:控制信息的通信(主要用于进程间的同步、互斥、终止和挂起等控制信息的传递)
(2)高级通信:大批数据信息的通信(主要用于进程间数据块数据的交换和共享,常见的高级通信有管道,消息队列,共享内存等)

通讯的方式有:
(1)管道:是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
(2)有名管道:也是一种半双工的通信方式,但是它允许无亲缘关系进程间的通信。
(3)信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。不是用来交换大批数据,而用于多线程之间的同步,常作为一种锁机制,防止某进程在访问资源时其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
(4)信号:是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
(5)消息队列:是由消息的链表存放在内核中并由消息队列标识符表示。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
(6)共享内存:就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的通信方式,它是针对其他进程间通信。
(7)套接字:也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

优缺点总结
(1)管道简单方便,但局限于单向通信的工作方式,并且只能在创建它的进程及其子孙进程之间实现管道的共享。
(2)有名管道虽然可以提供给任意关系的进程使用,但是由于其长期存在于系统之中,使用不当容易出错,所以普通用户一般不建议使用。
(3)消息队列可以不再局限于父子进程,而允许任意进程通过共享消息队列来实现进程间通信,并由系统调用函数来实现发送和接收之间的同步,从而使得用户在使用消息队列进行通信时不再需要考虑同步问题,使用方便。但是消息队列中信息的复制需要额外消耗CPU的时间,不适宜于信息量大或操作频繁的场合。
(4)共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息。

14、 进程的状态有哪些?

进程的五个状态:创建状态,就绪状态,阻塞状态,运行状态,终止状态
(1)创建状态:进程在创建时需要申请一个空白PCB(进程控制块),向其中填写控制和管理进程的信息,完成资源分配。如果创建工作无法完成,比如资源无法满足,就无法被调度运行,把此时进程所处状态称为创建状态。
(2)就绪状态:进程已经准备好,已分配到所需资源,只要分配到CPU就能够立即运行。
(3)运行状态:进程处于就绪状态被调度后,进程进入运行状态。
(4)阻塞状态:正在执行的进程由于某些事件(I/O请求,申请缓存区失败)而暂时无法运行。进程受到阻塞。在满足请求时进入就绪状态等待系统调用。
(5)终止状态:进程结束,或出现错误,或被系统终止,进入终止状态,无法再执行。

状态转换分为六种情况:
(1)运行->就绪:
1、主要是进程占用CPU的时间过长,而系统分配给该进程占用CPU的时间是有限的。2、在采用抢先式优先级调度算法的系统中,当有更高优先级的进程要运行时,该进程就被迫让出CPU,该进程便由运行状态转变为就绪状态。
(2)就绪->运行: 运行的进程的时间片用完,调度就转到就绪队列中选择合适的进程分配CPU。
(3)运行->阻塞: 正在执行的进程因发生某等待事件而无法执行,则进程由执行状态变为阻塞状态,如发生了I/O请求。
(4)阻塞->就绪: 进程所等待的事件已经发生,就进入就绪队列。

以下两种状态是不可能发生的:
(5)阻塞->运行:
即使给阻塞进程分配CPU,也无法执行,操作系统在进行调度时不会从阻塞队列进行挑选,而是从就绪队列中选取。
(6)就绪->阻塞: 就绪态根本就没有执行,谈不上进入阻塞态。

15、 什么是虚拟内存技术?

虚拟存储器是指具有请求调入功能和置换功能,能从逻辑上对内容容量加以扩充的一种存储系统。

16、 关于虚拟内存、虚拟内存地址、物理内存、物理内存地址

关于虚拟内存和物理内存的区别:
正在运行的一个进程,他所需的内存是可能大于内存条容量之和的,比如你的内存条是256M,你的程序却要创建一个2G的数据区,那么不是所有数据都能一起加载到内存(物理内存)中,势必有一部分数据要放到其他介质中(比如硬盘),等待进程需要访问那部分数据时,再通过调度进入物理内存。
所以,虚拟内存是进程运行时所有内存空间的总和,并且可能有一部分不在物理内存中,而物理内存就是我们平时所了解的内存条。有的地方,也叫虚拟内存为内存交换区。

关于虚拟内存地址和物理内存地址的区别:
假设计算机是32位的,那么它的地址总线是32位,也就是它可以寻址0~0xFFFFFFFF(4G)的地址空间,但如果你的计算机只有256M的物理内存0x~0x0FFFFFF(256M),同时你的进程产生了一个不在这256M地址空间中的地址时,怎么处理?

先了解一下计算机的内存分页机制
计算机会对虚拟内存地址空间(32位为4G)分页产生页(page),对物理内存地址空间(假设256M)分页产生页帧(page frame),这个页和页帧的大小是一样大的,所以,虚拟内存页的个数势必要大于物理内存页帧的个数。在计算机上有一个页表(page table),就是映射虚拟内存页到物理内存页的,更确切的说是页号到页帧号的映射,而且是一对一的映射。

但是虚拟内存页的个数 > 物理内存页帧的个数,岂不是有些虚拟内存页的地址永远没有对应的物理内存地址空间?
不是的,操作系统是这样处理的。操作系统有个页面失效功能(page fault),操作系统找到一个最少使用的页帧,让他失效,并把它写入磁盘,随后把需要访问的页放到页帧中,并修改页表中的映射,这样就保证所有的页都有被调度的可能了。这就是处理虚拟内存地址到物理内存的步骤。

虚拟内存地址由页号(与页表中的页号关联)和偏移量组成。页号对应的映射到一个页帧。偏移量是页(或者页帧)的大小,即这个页(或者页帧)到底能存多少数据。

例子:有一个虚拟地址的页号是4,偏移量是20,那么它的寻址过程是这样的:首先到页表中找到页号4对应的页帧号(比如未8),如果页不在内存中,则用失效机制调入页,否则把页帧号和偏移量传给MMC(CPU的内存管理单元)组成一个屋里上真正存在的地址,接着就是访问物理内存中的数据了。
总结:虚拟内存地址的大小是地址总数位数相关,物理内存地址的大小跟物理内存条的容量相关。

关于虚拟内存和物理内存的一些理解:
1、每次访问内存空间的某个地址,都需要把地址翻译为实际物理内存地址
2、所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。
3、进程要知道哪些内存地址上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,需要用页表来记录
4、页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)
5、当进程访问某个虚拟地址,去看页表,如果发现对应的数据不在物理内存中,则缺页异常
6、缺页异常的处理过程,就是把进程需要的数据从磁盘上拷贝到物理内存中,如果内存已经满了,没有空地方了,那就找一个页覆盖,当然如果被覆盖的页曾经被修改过,需要将此页写回磁盘。

猜你喜欢

转载自blog.csdn.net/JQ210245253/article/details/79771653