02 | 操作系统相关知识
知识点汇总
图1 操作系统知识点汇总操作系统部分知识有助于对服务问题进行排查定位,主要考察了解和应用,在面试中所占比较小。
1. 进程与线程(♥♥♥)
1.1 进程与线程之间的区别与联系?
- 进程,是系统资源分配的最小单位,使用独立的数据空间
- 线程,是程序执行的最小单位,共享进程的数据空间
1.2 线程的状态
线程一般包含5种状态[1]:创建、就绪、运行、阻塞和终止。有的会把线程状态分为6种[2]:初始、运行、阻塞、等待、超时等待和终止,这种的将原来5种状态中的就绪和运行统称为运行,把原来阻塞细分为了阻塞、等待、超时等待三种。这样-1+2就相当于多增加了1种状态,这里还是采用经典的5种线程状态说法,可以用图1来表示5种线程状态之间的关系。
图1 线程状态转换-
创建状态
继承Thread类和实现Runnable接口都可创建一个线程,此时的线程处于创建状态。(这里扩展一点——「多线程」继承Thread类和实现Runnable接口法的异同) -
就绪状态
当start()方法执行后,线程就进入了就绪状态。这时线程就处在了线程队列当中,等待CPU资源,代表其已经具备运行的资格。 -
运行状态
当处于就绪状态的线程获得CPU资源时,该线程就进入了运行状态,此时run()方法中定义的操作将被执行。 -
阻塞状态
运行状态的线程由于某些原因放弃了对CPU的使用权,这时候的线程就进入了阻塞状态,当引起阻塞的原因被解除时,线程才重新可以进入就绪状态。一般阻塞状态可以分为以下三种[3]:
(1)等待阻塞:执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒;
(2)同步阻塞:在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中,等待其他线程释放同步锁。
(3)其他阻塞:执行sleep()或join()方法,或者发出了耗时的输入/输出请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者输入/输出处理完毕时,线程重新转入就绪状态。 -
终止状态
线程的run()方法结束自然死亡或未捕获的异常导致run()方法终止,都会让该线程进入终止状态也就不具备继续运行能力了。判断线程是否终止,可用isAlive()方法。
1.3 线程调度(调度算法)[4]
在分析各种调度算法前,先简要了解一下分时系统与实时系统[5](典型的分时系统有UNIX和Linux,而对于windows,严格上说它的本质应该是多种集合的操作系统,它在运行过程中,根据不同的进程会有实时响应和分时响应,部分功能中,它也可以实现分布式操作。
)。简单来说分时系统和实时系统都具备多路性(能为多个终端提供服务)和独立性(每个终端之间互不干扰),在及时性方面分时系统考虑的时间是在人可接受的范围内,而实时系统由于军事、工业、多媒体等特定业务的执行需要将时间控制在秒级、百毫秒级直至毫秒级甚至0.1毫秒;在交互性方面,分时系统具备该特性不必说,而实时信息处理系统具备的交互性也仅限于特定的服务程序;至于可靠性,实时系统此方面的要求要比分时系统高的多。
-
时间片轮转调度(Round Robin, RR)
CPU为让每个线程看起来像在并行执行而分配出一个线程运行的时长,也就是时间片。每个就绪状态的线程在排队队列中将按时间片长依次循环执行,直至所有线程运行完毕。 -
先来先服务调度(First Come First Serve, FCFS)
该调度方法仅考虑就绪状态的线程在排队队列中的先后顺序,不考虑其他因素,按照从队首到队尾的顺序依次执行每个线程,只有当该线程运行完毕或因其他原因被阻塞时才运行下一个线程。这种调度简单易行,是一种非抢占式调度策略。
比较有利于长作业,而不利于短作业;有利于CPU繁忙的作业,而不利于I/O繁忙的作业。
[6] -
优先级调度(Task Switching, TS)[7][8][9]
可以通过setPriority()方法来设定线程的优先级,优先级有10个梯度(1~10),即(MIN_PRIORITY【最低1级】---->……---->NORM_PRIORITY【普通5级】……---->MAX_PRIORITY【最高10级】)。处于就绪状态的两个线程,高优先级的线程比低优先级的线程具有更大的概率被CPU执行。至于为什么优先级高的线程不一定先执行,可参见[10]。 -
多级反馈队列调度(Multilevel Feedback Queue Scheduling, MFQS)[11]
UNIX操作系统采取的便是这种调度算法。
NOTE1:假设有三个待调度的队列Q1、Q2和Q3,优先级依次递减。进程在进入待调度的队列等待时,首先进入优先级最高的Q1等待。
NOTE2:首先调度优先级高的队列中的进程。若高优先级中队列中已没有调度的进程,则调度次优先级队列中的进程。
NOTE3:对于同一个队列中的各个进程,按照时间片轮转法调度。比如Q1队列的时间片为N,那么Q1中的作业在经历了N个时间片后若还没有完成,则进入Q2队列等待,若Q2的时间片用完后作业还不能完成,一直进入下一级队列Q3,直至完成。
NOTE4:在低优先级的队列中的进程在运行时,又有新作业达到最高优先级队列,那么在运行完这个时间片后,CPU马上分配给新到达的作业(抢占式)。
-
高响应比优先调度(Highest Response Ratio Next, HRRN)
该调度策略既考虑作业的执行时间也考虑作业的等待时间,综合了先来先服务和最短作业优先两种算法的特点。但每次调度之前都要计算响应比,这也增加了系统开销,响应比公式定义如下:
响应比 =(等待时间+要求服务时间)/ 要求服务时间=1+等待时间/要求服务时间,可以看出响应比是大于1的。
由响应比公式可知:
(1) 如果线程的等待时间相同,则线程要求服务的时间愈短,其优先级愈高,因此该调度类似于短作业优先调度;
(2) 如果线程要求服务的时间相同,则线程等待时间愈短,其优先级愈高,因此该调度类似于先来先服务调度;
(3) 对于要求服务时间长的进程,进程的优先级可以随等待时间的增加而提高,因此也能随着优先级的提高得到服务。
1.4 线程切换步骤[12]
多线程不一定就比单线程程序跑的快,因为多线程应用程序会带来额外的开销和竞争问题,可能会拖慢系统的执行速度。这些因素包括:对IO设备的竞争,对锁的竞争,以及CPU对线程执行上下文的频繁切换等。
-
线程的上下文一般指包括cpu的寄存器和程序计数器在某一时间点的内容等
-
线程的上下文切换与切换代价
当 CPU 从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行。这种切换称为“上下文切换”(“context switch”)。CPU 会在一个上下文中执行一个线程,然后切换到另外一个上下文中执行另外一个线程。 线程在运行的时候除了需要从计算机里面获得【CPU资源】,还需要一些【内存】来维持它本地的【堆栈】,还需要占用操作系统中一些资源来【管理线程】。
1.4 Linux下的进程间通信(InterProcess Communication, IPC)
面试中间件研发时常考察
,了解以下6种进程间通信的相关原理[13]
- 共享内存(适用于进程间数据共享场景)
- UnixSocket(适用于进程间数据交换场景)
- MessageQueue消息队列(适用于进程间数据交换场景)
- Pipe管道
- Signal
- Semaphone信号量
1.5 协程
(1)协程又称为“微线程”,是一种用户态的轻量级线程(协程的调度完全由用户控制)。协程拥有自己的寄存器上下文和栈,调度切换时直接操作栈而基本没有内核切换的开销,所以其切换的代价要比线程上下文切换小很多,另外它可以不加锁的访问全局变量。
(2)java的第三方协程框架,如Kilim和quasar。
2. Linux常用命令
3. 死锁
两个线程都在等待对方先完成,造成了程序的停滞,一般程序的死锁都是在程序运行时出现的。
4. 内存分页管理与Swap
5. 任务队列与CPU Load
任务队列,消息队列和rpc的区别是什么?
CPU负载和 CPU使用率
6. 知识点扩展(了解)
6.1 内存屏障
内存屏障,也称内存栅栏,内存栅障,屏障指令等, 是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。
6.2 指令乱序
6.3 CPU亲和性(affinity)
6.4 Netfilter和iptables
-
李兴华. Java开发实战经典[M]. 北京: 清华大学出版社, 2009, 281-282. ↩