操作系统面试题整理(附答案,持续更新)

794 阅读14分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

操作系统的面试题已在程序员面试中屡见不鲜,是非常重要的一块内容。

于是,我针操作系统的面试题做了一系列的整理,我参考了大量网络上的资源以及经典参考书,争取能把具体的面试题目给讲明白讲透彻,强调理解记忆,拒绝八股文!!。这一篇内容,既是自我的学习总结,也希望能帮助到正在复习面试的朋友。

本文分为进程和线程篇,内存管理篇,IO篇,Linux篇。

进程和线程篇

什么是进程和线程

  • 进程是对运行时程序的封装,是系统资源调度和分配的基本单位
  • 线程是进程的子任务,CPU调度和分配的基本单位,实现进程内并发
  • 一个进程可以包含多个线程,线程依赖进程存在,并共享进程内存

进程是资源分配的最小单位,线程是CPU调度的最小单位

  • 所有与进程相关的资源,都被记录在PCB中
  • 进程是抢占处理机的调度单位;线程属于某个进程,共享其资源
  • 线程只由堆栈寄存器、程序计数器和TCB组成

总结:

  • 线程不能看做独立应用,而进程可看做独立应用
  • 进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径
  • 线程没有独立的地址空间,多进程的程序比多线程程序健壮
  • 进程的切换比线程的切换开销大

进程有哪⼏种状态?

我们⼀般把进程⼤致分为 5 种状态,这⼀点和线程很像!

  • 创建状态(new) :进程正在被创建,尚未到就绪状态。
  • 就绪状态(ready) :进程已处于准备运⾏状态,即进程获得了除了处理器之外的⼀切所需资源,⼀旦得到处理器资源(处理器分配的时间⽚)即可运⾏。
  • 运⾏状态(running) :进程正在处理器上上运⾏(单核 CPU 下任意时刻只有⼀个进程处于运⾏状态)。
  • 阻塞状态(waiting) :⼜称为等待状态,进程正在等待某⼀事件⽽暂停运⾏如等待某资源为可⽤或等待 IO 操作完成。即使处理器空闲,该进程也不能运⾏。
  • 结束状态(terminated) :进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运⾏。

线程安全

  • 一个操作可以在多线程环境中安全使用,获取正确的结果
  • 线程安全的操作好比线程是顺序执行而不是并发执行的(i+=1)
  • 一般如果涉及到写操作需要考虑如何让多个线程安全访问数据

线程同步的方式

线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使⽤冲突。操作系统⼀般有下⾯三种线程同步的⽅式:

  1. 互斥量(Mutex):采⽤互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有⼀个,所以可以保证公共资源不会被多个线程同时访问。⽐如 Java 中的synchronized 关键词和各种 Lock 都是这种机制。
  2. 信号量(Semphares) :它允许同⼀时刻多个线程访问同⼀资源,但是需要控制同⼀时刻访问此资源的最⼤线程数量
  3. 事件(Event) :Wait/Notify:通过通知操作的⽅式来保持多线程同步,还可以⽅便的实现多线程优先级的比较

进程间通信的方式

Inter-Process Communication进程间传递信号或者数据

  • 管道/匿名管道/有名管道(pipe)
  • 信号(Signal):比如用户使用Ctrl+c产生SIGINT程序终止信号
  • 消息队列(Message)
  • 共享内存(share memory)
  • 信号量(Semaphore)
  • 套接字(socket):最常用的方式,我们的web应用都是这种方式

更详细的版本:

  1. 管道:速度慢,容量有限,只有父子进程能通讯
  2. FIFO:任何进程间都能通讯,但速度慢
  3. 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
  4. 信号量:不能传递复杂消息,只能用来同步
  5. 共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存

参考资料:

进程调度算法

为了确定首先执行哪个进程以及最后执行哪个进程以实现最大 CPU 利⽤率,计算机科学家已经 定义了⼀些算法,它们是:

  • 先到先服务(FCFS)调度算法 : 从就绪队列中选择⼀个最先进入该队列的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用CPU 时再重新调度。
  • 短作业优先(SJF)的调度算法 : 从就绪队列中选出⼀个估计运行时间最短的进程为之分配资源,使它立即执行并⼀直执行到完成或发生某事件而被阻塞放弃占用CPU 时再重新调度。
  • 时间片轮转调度算法 : 时间片轮转调度是⼀种最古老,最简单,最公平且使用最广的算法,又称 RR(Round robin)调度。每个进程被分配⼀个时间段,称作它的时间片,即该进程允许运行的时间。
  • 多级反馈队列调度算法 :前面介绍的几种进程调度的算法都有⼀定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程 。多级反馈队列调度算法既能使高优先级的作业得到响应⼜能使短作业(进程)迅速完成。因而它是目前被公认的⼀种较好的进程调度算法,UNIX 操作系统采取的便是这种调度算法。
  • 优先级调度 : 为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以 FCFS 方式执行。可以根据内存要求,时间要求或任何其他资源要求来确定优先级。

python中如何使用多线程

  • threading.Thread类用来创建线程
  • start()方法启动线程
  • 可以用join()等待线程结束

python中如何使用多进程

Python有GIL,可以用多进程实现CPU密集程序

  • multiprocessing多进程模块
  • multiprocessing.Process类实现多进程
  • 一般用在cpu密集程序里,避免GIL的影响

内存管理篇

什么是分页机制

todo

什么是分段机制

分段是为了满足代码的一些逻辑需求

分页和分段的区别

todo

什么是虚拟内存

通过把一部分暂时不用的内存信息放到硬盘上

  • 局部性原理,程序运行时候只有部分必要的信息装入内存
  • 内存中暂时不需要的内容放到硬盘上
  • 系统似乎提供了比实际内存大得多的容量,称之为虚拟内存

什么是内存抖动(颠簸)

本质是频繁的页调度行为

  • 频繁的页调度,进程不断产生缺页中断
  • 置换一个页,又不断再次需要这个页
  • 运行程序太多;页面替换策略不好。终止进程或者增加物理内存

Python的垃圾回收机制原理

  • 引用技术为主(缺点:循环引用无法解决)
  • 引入标记清除和分代回收解决引用计数的问题
  • 引用计数为主+标记清除和分代回收为辅

IO篇

如何提升并发能力

一些常见的提升并发能力的方式

  • 多线程模型,创建新的线程处理请求
  • 多进程模型,创建新的进程处理请求
  • IO多路复用,实现单进程同时处理多个socket请求
  • 线程/进程创建开销比较大,可以用线程池方式解决
  • 线程和进程比较占用资源,难以同时创建太多

什么是IO多路复用

操作系统提供的同时监听多个socket的机制

  • 为了实现高并发需要一种机制并发处理多个socket
  • Linux常见的是select/poll/epoll
  • 可以使用单线程单进程处理多个socket

两个过程:

  • 内核等待数据
  • 数据从内核拷贝到用户进程

BIO,NIO,AIO

  • BIO:同步阻塞式调用
  • NIO:同步非阻塞。(Java中广泛应用)
  • AIO:异步IO。

BIO(blockio):InputStream和OutputStream,Reader和writer

NIO(nonblock-io):构建多路复用的,同步非阻塞的IO操作

  • nio-channels
  • nio-buffer
  • nio-selector
  • nio的底层用了IO多路复用

AIO:基于事件和回调机制

BIO、NIO、AIO对比

属性\模型阻塞BIO非阻塞NIO异步AIO
blocking阻塞并同步非阻塞但同步非阻塞并异步
线程数1:11:N0:N
复杂度简单较复杂复杂
吞吐量

操作系统的五种IO模型

前四种都是同步

同步阻塞IO: 去饭馆吃饭 去了之后都在门口等着不能离开 同步非阻塞IO: 去了饭馆吃饭 就可以去干别的了 时不时回来看看 同步阻塞IO复用:去了饭馆门口,饭馆有个服务员,付服务员 有位了就让你进去 同步非阻塞信号驱动: 去了饭馆吃饭 去了之后领个号 就可以去干别的了 到你了 直接微信给你弹消息让你来 异步IO模型: 你去签个到 就去干别的了 做好了 直接找你去送过来

select和epoll的区别

  • 数量上限
  • 轮询 or 回调

select模型

  • 应用程序——select——解除阻塞——循环遍历查找收到读信号的连接——socket recv
  • select缺陷:1024数组个数的限制,遍历轮询触发找到真正有信号的socket连接

epoll模型

  • epoll改进:没有1024数组个数的限制.异步回调的方式去执行handler操作
  • 应用程序——epoll,socket连接handler——触发handler回调机制——handler

epoll性能比select强很多,nginx就是epoll模型

select、epoll、epoll的区别

  • 支持一个进程所能打开的最大连接数。
    • select:单个进程所能打开的最大连接数由FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小是3232,64位机器上FD_SETSIZE为3264),我们可以对其进行修改,然后重新编译内核,但是性能无法保证,需要做进一步测试
    • poll:本质上与select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的
    • epoll:虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接
  • FD剧增后带来的IO效率问题
    • select:因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度的线性下降的性能问题
    • poll:同上
    • epoll:由于epoll是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll不会有线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。
  • 消息传递方式
    • select:内核需要将消息传递到用户空间,需要内核的拷贝动作
    • poll:同上
    • epoll:通过内核和用户空间共享一块内存来实现,性能较高

Linux篇

什么是操作系统

  1. 操作系统(Operating System,简称 OS)是管理计算机硬件与软件资源的程序,是计算机的基⽯。
  2. 操作系统本质上是⼀个运⾏在计算机上的软件程序 ,⽤于管理计算机硬件和软件资源。 举例:运⾏在你电脑上的所有应⽤程序都通过操作系统来调⽤系统内存以及磁盘等等硬件。
  3. 操作系统存在屏蔽了硬件层的复杂性。 操作系统就像是硬件使⽤的负责⼈,统筹着各种相关事项。
  4. 操作系统的内核(Kernel)是操作系统的核⼼部分,它负责系统的内存管理,硬件设备的管理,⽂件系统的管理以及应⽤程序的管理。 内核是连接应⽤程序和硬件的桥梁,决定着系统的性能和稳定性。

Linux体系结构

  • 体系结构主要分为用户态(用户上层活动)和内核态
  • 内核:本质是一段管理计算机硬件设备的程序(cpu,内存,硬盘,网络)
  • 系统调用:内核的访问接口,是一种能再简化的操作
  • 公用函数库:系统调用的组合拳
  • shell:命令解释器,可编程

什么是系统调用

介绍系统调⽤之前,我们先来了解⼀下⽤户态和系统态。

根据进程访问资源的特点,我们可以把进程在系统上的运行分为两个级别:

  1. 用户态(user mode) : 用户态运行的进程或可以直接读取用户程序的数据。
  2. 系统态(kernel mode):可以简单的理解系统态运行的进程或程序几乎可以访问计算机的任何资源,不受限制。

说了用户态和系统态之后,那么什么是系统调用呢?

我们运行的程序基本都是运行在用户态,如果我们调用操作系统提供的系统态级别的子功能咋办呢?那就需要系统调用了!

也就是说在我们运行的用户程序中,凡是与系统态级别的资源有关的操作(如文件管理、进程控制、内存管理等),都必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。

这些系统调用按功能大致可分为如下几类:

  • 设备管理。完成设备的请求或释放,以及设备启动等功能。
  • 文件管理。完成文件的读、写、创建及删除等功能。
  • 进程控制。完成进程的创建、撤销、阻塞及唤醒等功能。
  • 进程通信。完成进程之间的消息传递或信号传递等功能。
  • 内存管理。完成内存的分配、回收以及获取作业占用内存区大小及地址等功能。

Linux常用命令

  • top
  • ps -ef
  • jobs
  • pgrep
  • meminfo
  • free
  • vmstat
  • df
  • du
  • netstat
  • route
  • lsof -i: 8000 占用8000端口的进程

学习技巧

  • --help
  • man:查看帮助手册

常用shell命令

如何查找特定的文件

find

find path [options] params

作用:在指定目录下查找文件

find -name "target3.java"

当前目录递归寻找该目录

find / -name "target3.java"

从根目录递归寻找该文件

find ~ -name "target*"

模糊查询

find ~ -iname "target*"

忽略大小写

man find

检索文件内容

grep

grep [options] pattern file

查找文件里符合条件的字符串

grep "mooc" target*

管道操作符 |

可将指令连接起来,前一个指令的输出作为后一个指令的输入

find ~ | grep "target"

使用管道注意点:

  • 只处理前一个命令正确输出,不处理错误输出
  • 右边命令必须能够接收标准输入流,否则传递过程中数据会被抛弃
  • sed,awk,grep,cur,head,top,less,more,wc,join,sort,split等

grep 'xxx[true]' xx.log | grep -o 'engine[[0-9a-z]*]'

ps -ef | grep tomcat

ps -ef | grep tomcat | grep -v "grep" 过滤掉包含grep的内容

对文件内容做统计

awk

数据抽取和统计

批量替换文件内容

sed

适用于对文本的行内容进行处理

参考资料