操作系统常见问题
调度相关
调度算法
-
先来先服务(First Come First Serve, FCFS)算法,从就绪队列选择最先进入队列的进程,然后一直运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。
-
最短作业优先(Shortest Job First, SJF)调度算法同样也是顾名思义,它会优先选择运行时间最短的进程来运行,这有助于提高系统的吞吐量。
-
高响应比优先(Highest Response Ratio Next, HRRN)调度算法主要是权衡了短作业和长作业。每次进行进程调度时,先计算 “响应比优先级”,然后把 “响应比优先级” 最高的进程投入运行。
-
时间片轮转(Round Robin, RR)调度算法,每个进程被分配一个时间段,称为时间片(Quantum),即允许该进程在该时间段中运行。
-
最高优先级(Highest Priority First,HPF)调度算法,调度程序能从就绪队列中选择最高优先级的进程进行运行,也有两种处理优先级高的方法,非抢占式和抢占式。
-
多级反馈队列(Multilevel Feedback Queue)调度算法是 “时间片轮转算法” 和 “最高优先级算法” 的综合和发展。
「多级」表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。
「反馈」表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列工作原理:
- 设置了多个队列,赋予每个队列不同的优先级,每个队列优先级从高到低,同时优先级越高时间片越短;
- 新的进程会被放入到第一级队列的末尾,按先来先服务的原则排队等待被调度,如果在第一级队列规定的时间片没运行完成,则将其转入到第二级队列的末尾,以此类推,直至完成;
- 当较高优先级的队列为空,才调度较低优先级的队列中的进程运行。如果进程运行时,有新进程进入较高优先级的队列,则停止当前运行的进程并将其移入到原队列末尾,接着让较高优先级的进程运行;
可以发现,对于短作业可能可以在第一级队列很快被处理完。对于长作业,如果在第一级队列处理不完,可以移入下次队列等待被执行,虽然等待的时间变长了,但是运行时间也变更长了,所以该算法很好的兼顾了长短作业,同时有较好的响应时间。
进程、线程、协程
- 进程(Process):进程是操作系统中的一个执行实例,它拥有独立的内存空间和资源。每个进程都是独立运行的,拥有自己的地址空间、文件句柄、环境变量等。进程间通信需要通过特定的机制,如管道、消息队列、共享内存等。
- 线程(Thread):线程是进程的一部分,是在同一进程内并发执行的执行单元。不同线程共享同一进程的内存空间和资源,包括全局变量、堆、文件描述符等。线程可以更轻量级地创建、切换和销毁,相对于进程而言,线程间的切换开销较小。线程之间可以通过共享内存等机制进行通信。
- 协程(Coroutine):协程是一种用户级的轻量级线程。协程由用户控制,而不是由操作系统内核控制。在协程中,执行流可以在不同协程之间进行切换,切换由程序员手动控制,而不需要内核介入。协程可以在一个线程内实现并发,但无法利用多核心处理器。协程通常用于实现高效的异步编程和协作任务。
进程和线程的区别
- 资源分配:进程是操作系统中的一个执行实体,拥有独立的地址空间、文件描述符、打开的文件等资源。每个进程都被分配了独立的系统资源。而线程是进程中的一个执行单元,多个线程共享同一个进程的地址空间和其他资源,包括文件描述符、打开的文件等。
- 调度和切换:进程的调度是由操作系统内核进行的,切换进程需要进行上下文切换,涉及用户态和内核态之间的切换,开销相对较大。而线程的调度是在用户程序中完成,切换线程可以在用户态下快速切换,减少了系统调用的开销。
- 并发性:进程是独立的执行实体,不同进程之间通过进程间通信(IPC)来进行数据交换和共享。进程间通信的方式包括管道、信号量、共享内存等。而线程是在同一个进程中执行的,多个线程之间共享同一进程的资源,可以通过共享内存的方式进行数据交换和共享。
进程切换比线程慢
- 操作系统会给每个进程分配一个虚拟地址空间(vitural address),每个进程包含的栈、堆、代码段这些都会从这个地址空间中被分配一个地址,这个地址就被称为虚拟地址。底层指令写入的地址也是虚拟地址。
- 每个进程都拥有一个自己的虚拟地址空间,并且独立于其他进程的地址空间。
- 进程切换会涉及到虚拟地址空间的切换,而这正是导致进程切换比线程切换慢的原因所在!
协程与我们普通的线程有区别
协程和线程区别
-
调度方式:线程的调度是由操作系统内核进行的,而协程的调度是由程序员或者特定的协程调度器进行的。线程的调度是由操作系统内核控制,切换线程需要进行系统调用,涉及用户态和内核态之间的切换,相对较为耗时。而协程的调度是在用户程序中完成,切换协程可以在用户态下快速切换,减少了系统调用的开销。
-
并发性:线程是操作系统提供的轻量级进程,多个线程之间可以并发执行,但在多核处理器上,线程的并发性是通过操作系统的线程调度实现的。而协程是在单个线程中执行的,多个协程之间通过协程调度器进行切换,实现了更细粒度的并发性。
-
系统资源消耗:线程是操作系统管理的实体,它占用系统资源比较大,包括内存、线程栈、CPU 时间片等。而协程则是在用户空间中实现的,不需要操作系统的支持,因此占用的资源比较少。线程的创建和销毁需要操作系统进行一系列的资源分配和回收,包括线程的栈空间和线程控制块等。而协程在单个线程中执行,不需要额外的系统资源分配,只需要协程调度器保存和恢复协程的上下文。
-
同步方式:线程之间的通信和同步需要使用锁、条件变量等机制来进行,这些机制需要进行加锁和解锁的操作,容易引发死锁和竞态条件等问题。而协程可以使用更轻量级的方式进行通信和同步,如使用通道(Channel)来实现协程之间的消息传递。
linux进程创建线程的流程
linux把所有线程都当做进程来实现,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都有唯一的task_struct。
用户程序调用 fork(),主线程调用clone()产生系统调用,陷入内核,, clone()调用do_fork(),完成创建工作的大部分, 调用copy_process()让进程开始运行。
同步相关
进程间通信方式
-
管道(Pipe):管道是一种半双工的通信方式,可以在具有亲缘关系的进程之间进行通信。它可以分为匿名管道(使用pipe函数创建)和命名管道(使用mkfifo函数创建)。匿名管道只能在具有共同祖先的进程之间使用,而命名管道可以在不具有亲缘关系的进程之间使用。
匿名管道顾名思义,它没有名字标识,匿名管道是特殊文件只存在于内存,没有存在于文件系统中,shell 命令中的「|」竖线就是匿名管道,通信的数据是无格式的流并且大小受限,通信的方式是单向的,数据只能在一个方向上流动,如果要双向通信,需要创建两个管道,再来匿名管道是只能用于存在父子关系的进程间通信,匿名管道的生命周期随着进程创建而建立,随着进程终止而消失。
命名管道需要在文件系统创建一个类型为p的设备文件,那么毫无关系的进程就可以通过这个设备文件进行通信。另外,不管是匿名管道还是命名管道,进程写入的数据都是缓存在内核中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持lseek之类的文件定位操作。
优点:简单易用,无需额外的系统调用和复杂的设置。
缺点:只能在具有亲缘关系的进程之间进行通信,且只能实现单向通信,如果要双向通信,需要创建两个管道。 -
信号(Signal):信号是一种异步的通信方式,用于通知进程发生了某种事件。一个进程可以向另一个进程发送信号,接收信号的进程可以选择采取相应的行动。进程可以通过系统调用signal或sigaction来注册信号处理函数,当接收到特定信号时,会调用相应的处理函数进行处理。信号可以在应用进程和内核之间直接交互,内核也可以利用信号来通知用户空间的进程发生了哪些系统事件,信号事件的来源主要有硬件来源(如键盘)和软件来源(如 kill 命令),一旦有信号发生,进程有三种方式响应信号 1. 执行默认操作、2. 捕捉信号、3. 忽略信号。有两个信号是应用进程无法捕捉和忽略的,即 SIGKILL 和 SIGSTOP,这是为了方便我们能在任何时候结束或停止某个进程。
优点:简单、快速