「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」
之前面试给自己准备了10天复习内容,也不知道对大家有没有用,分享下,欢迎大家补充,今天是 Day1 的整理内容。
操作系统的演进
在早期时候是没有操作系统概念的。有一个CPU,和自己编写的一个程序,但是CPU资源很珍贵,每次只运行一个程序,当运行结束后,CPU就闲下来了,这就造成了CPU资源的浪费。为了更好的使用CPU资源,就编写了一个监控程序用来监控CPU。如果发现CPU是空闲的就把它启动起来,去运行当前正在等待执行的程序,这样就充分利用了CPU资源。这种被称为 『多道程序(Multiprogramming)』
有了多道程序之后,还有一个问题就是程序的执行是不分轻重缓急的。举个例子:我们在使用浏览器的时候,希望网页快速加载,同时还可以播放这音乐。对于多道程序来说他需要一个执行完让出CPU资源才可以执行另外一个,显然无法满足要求。于是就改进了多道程序,使每个程序运行一段时间后都会主动让出CPU资源,这样每个程序在一段时间内都可以运行一小段时间,这样就能满足我们的需求了。这种被称为『分时系统(Time-Sharing System)』
有了分时系统后新的问题又出现了。假如某个程序出现了错误,导致死循环内存耗尽等,这样的话不仅是这一个程序出错,整个系统都会死机。为了解决这个问题,又改进了分时系统。使用操作系统从最底层接管所有的硬件资源。所有的应用程序在操作系统之上使用 『进程(Process)』 的方式运行,每个进程都有自己独立的地址空间,相互隔离。CPU由操作系统统一调度,每个程序都有机会得到CPU资源,如果一个进程运行超过一定时间,系统就会把它暂停掉,把CPU让给别的程序。这样就避免了一个出现导致全系统挂掉的情况。这种操作方式就是我们现在使用的系统,被称为 『多任务系统(Multi-tasking System)』
线程和进程的区别
进程(Process):是一个程序对一个数据集的动态执行过程,是分配资源的基本单位。每个进程都会拥有自己的地址空间,一般情况下包括文本区域、数据区域和堆栈。文本区域用来存储处理器执行的代码;数据区域用来存储变量和进程执行期间可使用的动态分配的内存;堆栈区域存储着调用指令和本地变量。
线程(Thread):是一个轻量级的进程(Lightweight Process,LWP),是程序执行流的最小单位。一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成。另外线程不拥有系统资源,但是他必须有一点可以保证独立运行的系统资源。
区别:
1.概念的区别
- 进程:是一个程序对一个数据集动态执行的过程,是分配资源的基本单位。
- 线程:是一个进程内的基本调度单位。线程的划分小于进程,一个进程包含一个或者多个线程。
2.执行过程的区别:
- 进程:拥有独立的内存空间,通过多个线程共享内存的方式来提供程序运行效率。
- 线程:每个独立的线程都拥有自己的程序入口、出口、执行顺序。线程不能独立运行,必须依存在进程中,由应用程序来提供多个线程执行控制。
- 上下文切换:对于进程的上下文切换 CPU的执行过程一般是:加载程序的上下文、执行程序、保存程序上下文这三步。每次程序的切换都要经过这3步,所以对于进程的切换开销是很大的。对于线程的上下文切换就不同了,比如在程序A内有a、b、c 三个线程。线程是共享进程内存的,CPU的执行就变为了:加载程序A的上下文、执行程序A的a小段、执行程序A的b小段、执行程序A的c小段、保存程序A的上下文。 这里a,b,c线程的执行是共享了程序A的上下文,CPU在执行的时候没有进行上下文切换的。所以在同一进程内的线程上下文切换是很快的,开销较小
3.逻辑上的区别:
- 进程:是资源分配的最小单位
- 线程:线程是CPU调度的最小单位。多个线程可以同时执行,但是操作系统并不会把线程看做是多个应用来实现进程的调度来资源管理。
系统调用
系统调用:System Call 是 程序向系统内核 请求获取相关服务,获取相关特权以访问资源。是程序和操作系统之间的重要接口。比如:创建新进程,调度其他进程,访问硬盘等
上下文切换
上下文是什么?
- 因为CPU是在不同的程序之间切换,每次只能一个程序,在每个程序运行之前,CPU需要知道当前执行的程序从哪里加载,从哪里开始运行(上次运行到哪条指令),以保证接着上次的运行状态,不然的话每次都重新运行程序,这样程序永远得不到执行完毕。这些信息会存储在系统的内核中,保证程序原来的状态不受影响,让任务连续运行起来,这些信息就是上下文。
上下文切换是什么?
- 是一种将CPU资源从一个进程分配给另外一个进程的调度机制。他保证了所有的程序在单位时间内都可以得到CPU并运行。在多个程序之间的切换运行,因为CPU的速度太快了从而导致我们看起来像多个程序在同时运行一样。
「进程之间的上下文切换」 和 「同一进程内线程的上下文切换」 区别
进程间的上下文切换:CPU加载程序上下文、CPU运行程序、CPU保存程序上下文。
线程间的上下文切换:CPU运行线程、CPU保存线程私有资源(栈、寄存器等)。因为同一个进程内,多个线程之间是共享虚拟内存的,所以他们不需要加载程序的上下文,在切换进程的时候已经加载过了,他们只需要经过执行和保存线程自己的私有资源即可。所以这样看,在线程之间的切换比进程的切换要消耗更少的资源。
多进程和多线程各自的优势
多进程
优点:
- 每个进程相互独立,不影响子主程序,即使子进程挂掉
- 可以尽量减少线程加锁/解锁的过程,提供性能
- 适用于多核 多机的分布式,一台机器不够扩展到多台很简单
- 编程简单,调试简单
缺点:
- 数据共享复杂
- 占用内存较多,切换开销较大
- 创建销毁,切换复杂,速度慢
进程的使用场景:目标子功能交互少,如果资源和性能许可,可以设计成多个子程序来组合完成。
多线程
优点:
- 因为共享进程数据,数据共享简单
- 占用内存少,切换简单,CPU利用率高
- 创建销毁、切换简单,速度很快
缺点:
- 编程复杂,调试复杂
- 一个线程挂掉将导致整个进程挂掉
- 线程之间同步和加锁控制比较麻烦
线程的使用场景:存在大量IO,网络等耗时操作或者需要和用户交互时,多线程可以更好的提供系统并行性和友好性
进程间的通信方式
一个进程不能直接读写另一个进程的数据,两者之间的通信需要通过进程间通信(inter-process communication, IPC)进行。进程通信的方式通常遵从生产者消费者模型,需要实现数据交换和同步两大功能。 大概有如下4种实现方式
1.消息传递
- 管道:单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起
- 消息队列:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点
2.同步
- 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其它进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段
- 条件变量
- 读写锁
3.共享内存
- 共享内存就是映射一段能被其它进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问
4.远程过程调用Socket
进程的基本状态
- 等待态:等待某个事件的完成
- 就绪态:等待系统分配处理器以便运行
- 运行态:得到处理器正在运行
运行态->等待态:一般是由于等待外设,等待主存等资源分配或人工干预而引起的
等待态->就绪态:等待运行的条件已满足,只需分配到处理器就可以运行
就绪态->运行态:系统按照某种策略选择就绪态队列中的一个进程分配到处理器,就变成了运行态
运行态->就绪态:不是由于自身原因,而是由外界原因使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等
抢占式调度和非抢占式调度
抢占式调度:操作系统将正在运行的进程强行暂停,由调度程序将CPU分配给其他就绪进程的调度方式。
非抢占式调度:分配程序一旦把处理器分配给某个进程后便让他一直运行下去,直到进程完成或者发生进程调度某事件而阻塞时,才会把处理器分配给另一个进程。
进程线程同步、互斥、死锁
互斥:是解决进程之间竞争关系的手段(比如多个进程都想获得同一个资源,比如打印机)。进程互斥指若干个进程要使用同一共享资源时,任何时刻最多允许一个进程去使用,其他要使用该资源的进程必须等待,直到占有资源的进程释放该资源。
同步:是解决进程之间协作关系的手段(比如多个进程一起协作完成一个工作)。进程同步指两个以上进程基于某个条件来协调它们的活动。一个进程的执行依赖于另一个协作进程的消息或信号,当一个进程没有得到来自于另一个进程的消息或信号时则需等待,直到消息或信号到达才被唤醒。
实现同步的手段:
- 临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占
- 互斥: 采用互斥对象机制。
- 信号量:它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目
- 事件:通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作 .
死锁:多个进程因循环等待资源而造成无法执行的现象。死锁会造成进程无法执行,资源无法被回收。
产生死锁的4个条件:
1.互斥使用
- 指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
2.不可抢占
- 指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
3.请求和保持
- 指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放
4.循环等待
- 指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源
如何避免死锁-银行家算法:判断此次请求是否造成死锁若会造成死锁,则拒绝该请求。 银行家算法
理解协程
协程,又称微线程,纤程。英文名 Coroutine。
协程可以理解为用户级线程,协程和线程的区别是:线程是抢占式的调度,而协程是协同式的调度,协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。