文章目录
- 前言
- 1. 操作系统概述
- 1.1 操作系统的四大特征(并共虚异)
- 1.2 操作系统的主要功能?
- 1.3 动态链接库和静态链接库的区别?
- 1.4 并发和共享之间的关系?
- 1.5 中断和异常的概念?
- 2. 进程与线程
- 2.1 进程和线程,以及他们的区别?
- 2.2 用户态和内核态
- 2.3 进程和程序的区别?
- 2.4 多线程共享什么数据?
- 2.5 线程同步的方式?
- 2.6 死锁及其产生条件?
- 2.7 进程的通信方式?
- 2.8 进程调度策略?
- 2.9 进程的状态和状态之间的转换?
- 2.10 进程同步机制
- 2.11 什么是饥饿?与死锁有什么差别?
- 2.12 描述银行家算法?
- 2.13 操作系统经典问题之哲学家进餐问题
- 2.14 操作系统经典问题之读者-写者问题
- 2.15 介绍一下几种典型的锁?
- 2.16 怎么回收线程?有哪几种方法?
- 3. 内存管理
- 3.1 虚拟内存的目的是什么?
- 3.2 说一下你理解中的内存?他有什么作用呢?
- 3.3 内存的覆盖是什么?有什么特点?
- 3.4 内存交换是什么?有什么特点?
- 3.5 什么时候会进行内存的交换?
- 3.6 什么是快表,你知道多少关于快表的知识?
前言
犹豫了许久,还是打算自己写吧,从各大面经上面摘取下来,写到自己这里,加深一下印象。把别人的巧克力融化了,再凝固,就是自己的巧克力了。
1. 操作系统概述
1.1 操作系统的四大特征(并共虚异)
- 并发:同一时间多段程序执行
- 共享:系统中的资源可被内存中多个并发执行的进程共同使用
- 虚拟:时分复用技术、空分复用技术,把一个物理上的实体变为若干逻辑上的对应物
- 异步:系统中的进程是以走走停停的方式执行的,以一种不可预知的速度向前推进
1.2 操作系统的主要功能?
- 进程控制:完成进程的创建、撤销、阻塞及唤醒等
- 内存管理:完成内存的分配、挥手以及获取作业占用内存区大小及起始地址等功能
- 进程通信:完成进程间消息传递或信号传递等功能
- 文件管理:完成文件的读写、创建和删除等
- 设备管理:管理外围设备,包括IO请求、为用户进程分配IO设备等
1.3 动态链接库和静态链接库的区别?
静态链接库是.lib
、.o
格式的文件,一般再工程的设置界面加入工程。程序编译时,会把.lib
文件加入到程序中,装载速度快,但会增加代码大小,浪费空间。
动态链接库是再程序运行的时候装入内存的模块,格式为.dll
、.so
,在程序运行时可以随时加载和移除,节省内存空间,但速度较慢。
静态库和动态库的优缺点
1.4 并发和共享之间的关系?
并发和共享是操作系统两个最基本的特性,两者互为存在条件。资源共享是以程序的并发为条件的,没有并发就没有共享;如果系统不能对共享资源进行有效的管理,会影响并发的效率。
1.5 中断和异常的概念?
中断:也叫外中断,来自CPU指令以外的时间的发生,例如设备发出的IO结束中断,表示设备输入输出处理已经完成。中断的发生与当前指令无关。
异常:也叫内中断。中断来源于CPU的内部,如非法操作等。对异常的处理一般依赖于当前进程的运行现场,异常不能被屏蔽,一旦出现就要立刻处理。
2. 进程与线程
2.1 进程和线程,以及他们的区别?
进程 | 线程 | 协程 | |
---|---|---|---|
定义 | 资源分配和拥有的基本单位 | 程序执行、调度的基本单位 | 用户态的轻量级线程,线程内部调度的基本单位 |
切换环境 | 进程CPU环境(栈、寄存器、页表和文件句柄等)的保存以及新调度的进程CPU环境的设置 | 保存和设置程序计数器、少量寄存器和栈的内容 | 先将寄存器上下文和栈保存,等切换回来的时候再进行恢复 |
切换者 | 操作系统 | 操作系统 | 用户 |
切换过程 | 用户态->内核态->用户态 | 用户态->内核态->用户态 | 用户态(没有陷入内核) |
调用栈 | 内核栈 | 内核栈 | 用户栈 |
拥有资源 | CPU资源、内存资源、文件资源和句柄等 | 程序计数器、寄存器、栈和状态字 | 拥有自己的寄存器上下文和栈 |
并发性 | 不同进程之间切换实现并发,各自占有CPU实现并行 | 一个进程内部的多个线程并发执行 | 同一时间只能执行一个协程,而其他协程处于休眠状态,适合对任务进行分时处理 |
系统开销 | 切换虚拟地址空间,切换内核栈和硬件上下文,CPU高速缓存失效、页表切换,开销很大 | 切换时只需保存和设置少量寄存器内容,因此开销很小 | 直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快 |
通信方面 | 进程间通信需要借助操作系统 | 线程间可以直接读写进程数据段(如全局变量)来进行通信 | 共享内存、消息队列 |
- 一个进程可以拥有多个线程,多个线程可以并发运行
- 线程基本不拥有系统资源,只拥有一些在运行中必不可少的资源,比如程序计数器、寄存器和栈
- 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位
2.2 用户态和内核态
运行在用户态下的程序,只能受限地访问内存,不允许访问外围设备。占用CPU的能力被剥夺,CPU资源可以被其他程序获取。
运行在内核态下的程序,可以访问内存所有数据,包括外围设备。
2.3 进程和程序的区别?
- 程序是静止的,进程是动态的
- 进程具有并发性,程序没有
- 程序是永存的;进程是暂时的,是程序在数据集上的一次执行
- 进程和程序不是一一对应的: 一个程序可对应多个进程即多个进程可执行同一程序; 一个进程可以执行一个或几个程序
2.4 多线程共享什么数据?
- 进程的公有数据
- 进程代码段
- 进程打开的文件描述符
- 信号的处理器
- 进程的当前目录
- 进程ID与进程组ID
2.5 线程同步的方式?
- 信号量
允许统一时刻多个线程访问同一个资源,但需要控制统一时刻访问此资源的最大线程数量 - 互斥量
实际上是信号量的一种特殊情况,允许统一时刻只有一个线程访问同一个资源 - 信号,也叫事件
通过通知操作的方式来保证多线程同步,还可以方便实现多线程优先级的比较操作
2.6 死锁及其产生条件?
在两个或者多个并发进程中,如果每个进程持有某种资源,又等待其他进程释放它目前持有的资源,在未改变这种状态之前,都不能向前推进。这一情况被称作死锁。
死锁产生的四个条件:
- 互斥条件:一个资源一次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对其自身拥有的资源保持不放
- 不可剥夺条件:进程获得的资源,在未完全使用完之前,不能强行剥夺
- 循环等待条件:若干进程之前形成一种头尾相接的环形等待资源关系
解决死锁的基本方法:预防死锁,避免死锁,检测死锁,解决死锁
- 预防死锁:确保死锁发生的四个必要条件中,至少有一个不成立
- 避免死锁:动态检测资源分配状态,确保循环等待条件不成立,使系统处于安全状态
- 解决死锁:包括进程终止和资源抢占
- 选择一个牺牲品
- 回滚
- 饥饿(使回滚得越多的,越不可能继续作为牺牲品)
2.7 进程的通信方式?
- 管道
- 无名管道(内存文件):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程之间使用。进程的亲缘关系通常是指父子进程关系。
- 有名管道(FIFO文件,借助文件系统):有名管道也是半双工的通信方式,但是允许在没有亲缘关系的进程之间使用,管道是先进先出的通信方式。
- 系统IPC(包括消息队列、信号量、共享内存、信号)
- 共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与信号量,配合使用来实现进程间的同步和通信。
- 消息队列:消息队列是有消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。系统中可能有很多的消息队列,每个消息队列用消息队列描述符(消息队列ID:qid)来区分,qid是唯一的,用来区分不同的消息队列。
- 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,实现进程、线程的对临界区的同步及互斥访问。
- SOCKET: 适用于不同机器间进程通信,在本地也可作为两个进程通信的方式。
2.8 进程调度策略?
先来先服务、短进程优先、优先级、时间片轮转、多级反馈
- 先来先服务 first-come first-serverd(FCFS)
非抢占式的调度算法,按照请求的顺序进行调度。
有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长
- 短作业优先 shortest job first(SJF)
非抢占式的调度算法,按估计运行时间最短的顺序进行调度。
长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度
- 优先级调度
为每个进程分配一个优先级,按优先级进行调度。
为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
- 时间片轮转
将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。
当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
- 多级反馈队列
可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,…。进程在第一个队列没执行完,就会被移到下一个队列。
2.9 进程的状态和状态之间的转换?
进程有新建、就绪、运行、阻塞、终止等五个状态。
2.10 进程同步机制
- 信号量机制
对信号量使用P和V操作,P将信号量值减一,V将信号量值加一。
当信号量值大于0时,表示当前可用资源的数量,当信号量值小于0时,表示当前正在等待该资源的进程数。 - 自旋锁管程
- 会合
- 分布式系统
2.11 什么是饥饿?与死锁有什么差别?
等待时间给进程推进和响应带来明显影响时称为进程饥饿。饥饿并不代表系统已经死锁,但至少有一个程序的执行被无限期地推迟。差别:① 进入饥饿的进程可以只有一个,但是死锁必须大于等于两个;② 出于饥饿状态的进程可以是一个就绪进程,但是死锁状态的进程必定是阻塞进程。
2.12 描述银行家算法?
主要思想是避免系统进入不安全状态,在每次进行资源分配时,它首先检查系统是否有足够的资源满足要求,如果有,则先试行分配,并对分配后的新状态进行安全性检查。如果新状态安全,则正式分配上述资源,否则拒绝分配上述资源。这样就保证系统始终处于安全状态,从而避免死锁现象的发生。
2.13 操作系统经典问题之哲学家进餐问题
五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。
为了防止死锁的发生,可以设置两个条件:
必须同时拿起左右两根筷子;
只有在两个邻居都没有进餐的情况下才允许进餐。
2.14 操作系统经典问题之读者-写者问题
允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。
一个整型变量 count 记录在对数据进行读操作的进程数量,一个互斥量 count_mutex 用于对 count 加锁,一个互斥量 data_mutex 用于对读写的数据加锁。
2.15 介绍一下几种典型的锁?
- 读写锁
- 多个读者可以同时进行读
- 写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
- 写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)
- 互斥锁
一次只能一个线程拥有互斥锁,其他线程只有等待。 - 条件变量
互斥锁一个明显的缺点是他只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,他常和互斥锁一起使用,以免出现竞态条件。当条件不满足时,线程往往解开相应的互斥锁并阻塞线程然后等待条件发生变化。一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。总的来说互斥锁是线程间互斥的机制,条件变量则是同步机制。 - 自旋锁
如果进线程无法取得锁,进线程不会立刻放弃CPU时间片,而是一直循环尝试获取锁,直到获取为止。如果别的线程长时期占有锁,那么自旋就是在浪费CPU做无用功,但是自旋锁一般应用于加锁时间很短的场景,这个时候效率比较高。(不阻塞,一直尝试获取锁)
2.16 怎么回收线程?有哪几种方法?
- 等待线程结束:
int pthread_join(pthread_t tid, void** retval);
- 结束线程:
void pthread_exit(void *retval);
- 分离线程:
int pthread_detach(pthread_t tid);
3. 内存管理
3.1 虚拟内存的目的是什么?
虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。
为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。
这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。
从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序成为可能。
例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。
3.2 说一下你理解中的内存?他有什么作用呢?
内存是存放数据的硬件,程序执行前需要先放到内存才能被CPU处理。
3.3 内存的覆盖是什么?有什么特点?
由于程序运行时并非任何时候都要访问程序及数据的各个部分(尤其是大程序),因此可以把用户空间分成为一个固定区和若干个覆盖区。将经常活跃的部分放在固定区,其余部分按照调用关系分段,首先将那些即将要访问的段放入覆盖区,其他段放在外存中,在需要调用前,系统将其调入覆盖区,替换覆盖区中原有的段。
覆盖技术的特点:是打破了必须将一个进程的全部信息装入内存后才能运行的限制,但当同时运行程序的代码量大于主存时仍不能运行,再而,大家要注意到,内存中能够更新的地方只有覆盖区的段,不在覆盖区的段会常驻内存。
3.4 内存交换是什么?有什么特点?
交换(对换)技术的设计思想:内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)
换入:把准备好竞争CPU运行的外存从辅存移到内存。 换出:把处于等待状态(或CPU调度原则下被剥夺运行权力)的程序从内存移到外存,把内存空间腾出来。
3.5 什么时候会进行内存的交换?
内存交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,此时可以换出一些进程;如果缺页率明显下降,就可以暂停换出。
3.6 什么是快表,你知道多少关于快表的知识?
快表,又称联想寄存器(TLB) ,是一种访问速度比内存快很多的高速缓冲存储器,用来存放当前访问的若干页表项,以加速地址变换的过程。与此对应,内存中的页表常称为慢表。
Reference
阿秀的学习笔记