操作系统面试知识点

1.虚拟内存解决了什么问题?

内存在计算机中的作用很大,计算机所有运行的程序都需要经过内存来执行,如果执行的程序很大或很多,就会导致内存消耗殆尽。为了解决这个问题,运用了虚拟内存技术,即拿出一部分硬盘空间来充当内存使用,当内存占用完时,电脑就会自动调用硬盘来充当内存,以缓解内存的紧张。

分页:用户程序的逻辑地址空间被划分成若干固定大小的区域,称为“页”或者“页面”,相应地,内存物理空间也分成相对应的若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。
优点:没有外碎片,每个内碎片不超过页的大小。
缺点:程序全部装入内存,要求有相应的硬件支持。

分段:将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。
优点:可以分别编写和编译,可以针对不同类型的段采用不同的保护,可以按段为单位来进行共享,包括通过动态链接进行代码共享。
缺点:会产生碎片

段页:作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号,然后再将每段分成若干个大小相等的页。对于主存空间也分成大小相等的页,主存的分配以页为单位。
段页式管理是段式管理与页式管理方案结合而成的所以具有他们两者的优点。但反过来说,由于管理软件的增加,复杂性和开销也就随之增加了。

2.进程间通信的七种方式

管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

详解

管道:管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了。当缓冲区读空或者写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或慢的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。

消息队列:就是一个消息的链表,是一系列保存在内核中消息的列表。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。可以把消息看做一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程可以从消息队列中读取消息。

共享内存:共享内存允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取错做读出,从而实现了进程间的通信。
采用共享内存进行通信的一个主要好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝,对于像管道和消息队里等通信方式,则需要再内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次:一次从输入文件到共享内存区,另一次从共享内存到输出文件。

信号量(semaphore):与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。支持信号量组。

套接字Socket:是面向”客户/服务器“(C/S)模型而设计的,针对客户和服务器程序提供不同的Socket系统调用。这种模式巧妙地解决了进程之间建立通信连接的问题。服务器Socket会公告给需要通信的一方。

应用场景

  1. 如果用户传递的信息较少,或者只是为了出发某些行为。信号是一种简洁有效的通信方式。但若是进程间要求传递的信息量较大或者存在数据交换的要求,就需要考虑别的通信方式了。
  2. 消息队列允许任意进程通过共享队列来进行进程间通信。并由系统调用函数来实现消息发送和接收之间的同步。从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题,使用相对方便。但是消息队列中信息的复制需要耗费CPU时间,不适宜信息量大或频繁操作的场合。
  3. 消息队列与管道方式的区别在于,消息队列可以实现多对多,并需要在内存中实现,而管道可以在内存或磁盘上实现。
  4. 共享内存无须复制,信息量大是其最大的优势。但是需要考虑同步问题。

3.死锁产生的必要条件?如何解决死锁的问题?

一般是指由2个或以上的线程互相持有对方所需要的资源,导致这些线程一折处于等待其他线程释放资源的状态,无法继续执行下去,如果线程都不主动释放所占有的资源,将产生死锁。

死锁发生的条件是:1.互斥;2.占有且等待;3.不可抢占;4.循环等待

除了互斥是不能破坏的,只要破坏其他三个之一,就可以避免死锁。

  1. 一次性申请所有资源
  2. 占有一部分资源时其他资源申请不到就主动释放
  3. 按序申请资源,破坏“循环等待条件”

4.linux的各种IO模型?

Linux的五种IO模型:1.阻塞IO模型;2.非阻塞IO模型 ;3.IO复用模型;4.信号驱动IO;5.异步IO模型
注意:前四种都是同步,只有最后一种才是异步IO。

概念详解
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。

非阻塞:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。

异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回

阻塞I/O模型:进程会一直阻塞,直到数据拷贝完成。
应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。

非阻塞IO模型:非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回);在数据拷贝的过程中,进程是阻塞的。

IO复用模型:主要是select和epoll;对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听;
I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。

异步IO模型:数据拷贝的时候进程无需阻塞。
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。

同步IO引起进程阻塞,直至IO操作完成。
异步IO不会引起进程阻塞。
IO复用是先通过select调用阻塞。

select、poll、epoll简介
epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现

select:select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:

  1. 单个进程可监视的fd数量被限制,即能监听端口的大小有限。
  2. 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:
  3. 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

poll:poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:

  1. 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
  2. poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

epoll: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减少复制开销。

总结

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

5.内核态和用户态的区别以及转换?

内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。

用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。

为什么要有用户态和内核态?

由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级 – 用户态和内核态。

所有用户程序都是运行在用户态的, 但是有时候程序确实需要做一些内核态的事情, 例如从硬盘读取数据, 或者从键盘获取输入等. 而唯一可以做这些事情的就是操作系统, 所以此时程序就需要先操作系统请求以程序的名义来执行这些操作.

这时需要一个这样的机制: 用户态程序切换到内核态, 但是不能控制在内核态中执行的指令
这种机制叫系统调用, 在CPU中的实现称之为陷阱指令(Trap Instruction)

流程如下:

  1. 用户态程序将一些数据值放在寄存器中, 或者使用参数创建一个堆栈(stack frame), 以此表明需要操作系统提供的服务.
  2. 用户态程序执行陷阱指令
  3. CPU切换到内核态, 并跳到位于内存指定位置的指令, 这些指令是操作系统的一部分, 他们具有内存保护, 不可被用户态程序访问
  4. 这些指令称之为陷阱(trap)或者系统调用处理器(system call handler). 他们会读取程序放入内存的数据参数, 并执行程序请求的服务
  5. 系统调用完成后, 操作系统会重置CPU为用户态并返回系统调用的结果

用户态与内核态的切换

  1. 系统调用:这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如前例中fork()实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如Linux的int 80h中断。
  2. 异常:当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。
  3. 外围设备的中断:当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

这3种方式是系统在运行时由用户态转到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的。

6.linux底层的零拷贝技术

零拷贝: 指的是CPU不执行拷贝数据从一块内存区域到另一块区域的任务的计算机操作。它通常用于在网络上传输文件时节省CPU周期和内存带宽。简单来说,零拷贝就是一种避免 CPU 将数据从一块存储拷贝到另外一块存储的技术。

零拷贝(zero-copy)技术可以减少数据拷贝和共享总线操作的次数,消除通信数据在存储器之间不必要的中间拷贝过程,有效地提高通信效率,是设计高速接口通道、实现高速服务器和路由器的关键技术之一。数据拷贝受制于传统的操作系统或通信协议,限制了通信性能。采用零拷贝技术,通过减少数据拷贝次数,简化协议处理的层次,在应用和网络间提供更快的数据通路,可以有效地降低通信延迟,增加网络吞吐率。

7.操作系统大内核和微内核之间的区别以及各自的好处?

大内核系统将操作系统的主要功能模块都作为一个紧密联系的整体运行在核心态,从而为应用提供高性能的系统服务。因为各管理模块之间共享信息,能有效利用相互之间的有效特性,所以具有无可比拟的性能优势。

微内核将内核中最基本的功能(如进程管理等)保留在内核,而将那些不需要在核心态执行的功能移到用户态执行,从而降低了内核的设计复杂性。而那些移出内核的操作系统代码根据分层的原则被划分成若干服务程序,它们的执行相互独立,交互则都借助于微内核进行通信。

优缺点:
大内核

  1. 高性能
  2. 内核代码大,结果混乱,难以维护,因为随着体系结构和应用需求的不断发展,需要操作系统提供的服务越来越多,而且接口形式越来越复杂。

微内核

  1. 内核功能少,结构清晰,方便管理,更加稳定
  2. 性能低,因为需要频繁地在核心态和用户态之间进行切换,操作系统的执行开销偏大

8.僵尸进程和孤儿进程产生的原因以及解决方式?

什么是僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait/waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。

危害:如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

产生原因:如果父进程一直比较忙,或者因为其他原因没有去wait/waitpid子进程结束时候留下的状态信息,那这些已经结束的线程遗留的数据结构就无从清理,也就产生了所谓的僵尸进程。

什么是孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程,孤儿进程将被init进程(进程号为1的进程)所收养,并由init进程对它们完成状态收集工作。

产生原因:父进程先于子进程结束,那子进程无父进程了。

孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。因此孤儿进程并不会有什么危害。

解决方式

僵尸进程解决办法

  1. 杀死父进程:一种比较暴力的做法是将其父进程杀死,那么它的子进程,即僵尸进程会变成孤儿进程,由系统来回收。
  2. 通过信号机制:子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。测试程序如下所示:
  3. fork 两次:将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程

9.操作系统的进程调度方式

概述:进程调度方式主要是指具有不同优先级的进程到来时如何分配CPU,调度方式主要有 可剥夺不可剥夺 两种。

可剥夺是当具有更高优先级的进程到来时,会强行的将正在运行进程的CPU资源分配给更高优先级的进程;不可剥夺则是必须等待正在运行的进程自动释放占用的CPU,才会将CPU再次分配。

三级调度

通常在操作系统中,一个作业从提交到完成需要经历三级调度。

  1. 高级调度:又称为长调度、作业调度、接纳调度。它决定处于输入池中的哪个后备作业可以调入主系统做好运行准备,称为一个或一组就绪进程。在系统中每个作业只需经过一次高级调度。
  2. 中级调度:又称为中程调度、对换调度。它决定处于交换区的哪个就绪进程可以调入内存,直接参与对CPU的竞争;而在内存资源不足时,为了将进程调入内存,则必须将内存中处于阻塞状态的进程调出至交换区,这相当于将处于内存的进程与交换区的进程交换位置。
  3. 低级调度:又称为短程调度、进程调度。它决定内存中的哪个就绪进程可以占用CPU,低级调度是操作系统中最活跃最核心的调度程序。

调度算法

调度的方式有很多,但主要有下面这几类:

  1. 先来先服务:这是最简单的方式,即按照作业提交或进程变更为就绪态的次序分配CPU。这种调度方式明显利于需要长作业的情况,但不利于需要频繁中断的作业。主要用于宏观调度。

  2. 时间片轮转:时间片轮转即每个进程都运行一定时间,它主要用于微观调度,目的是提升资源的利用率。时间片的长度可以从几毫秒到数百毫秒不等,又有固定时间片和可变时间片两种。

  3. 优先级调度:这种方式要求进程都有一个优先级,系统调度时总选择优先级更高的进程占用CPU,优先级的分配有两种方式:(1)静态优先级——在创建时就确定进程优先级,直至进程终止优先级也不会发生变化,通常根据三种因素决定:进程类型、资源需求、用户要求。(2)动态优先级——在创建时也赋予一个优先级,但运行过程中可被改变,以便调度更合理。如在就绪队列中等待越长则优先级将提高,而进程被执行一个时间片后则降低。

  4. 多级反馈调度:它是时间片轮转及优先级的综合。优点有三:照顾短进程提高系统吞吐量、缩短平均轮转时间;照顾I/O需求较高的进程,提升I/O设备利用率和缩短响应时间;无需估计进程执行耗时,动态调整优先级。

进程优先级确定
进程优先级考虑如下情况:

  1. I/O型进程——让其进入最高优先级队列,以及时响应I/O交互进程。
  2. 计算型进程——每次执行都会降低优先级,以期最终采用较长时间片执行,减少调度次数。
  3. 少数I/O型进程——I/O完成后则应逐渐降低优先级,不应长期处于高优先队列中。

猜你喜欢

转载自blog.csdn.net/qq_42748009/article/details/111600248