面试-操作系统
用户态和内核态的区别
内核态:在内核态下,CPU可以执行所有的指令和访问所有的硬件资源。
用户态:在用户态下,CPU只能执行部分指令集,无法直接访问硬件资源。
内核态的底层操作主要包括:内存管理、进程管理、设备驱动程序控制、系统调用等。
分为内核态和用户态的原因:
安全性:通过对权限的划分,用户程序无法直接访问硬件资源,从而避免了恶意程序对系统资源的破坏。
稳定性:用户态程序出现问题时,不会影响到整个系统,避免了程序故障导致系统崩溃的风险。
隔离性:内核态和用户态的划分使操作系统内核与用户程序之间有了明确的边界,有利于系统的模块化和维护。
内核态和用户态的划分有助于保证操作系统的安全性、稳定性和易维护性。
线程和进程的区别是什么?
本质区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。
开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有很大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换开销小。
稳定性:进程中某个线程如果崩溃了,可能会导致整个进程都崩溃。而进程中的子进程崩溃,并不会影响其他进程。
内存分配:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所有使用的资源来自其所属进程的资源),进程组之间只能共享资源。
包含关系:没有线程的进程可以看作是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线。
进程、线程、协程的区别是什么?
类型 | 定义 | 资源特点 | 通信方式 | 上下文切换开销 | 优缺点 |
---|---|---|---|---|---|
进程 | 操作系统中进行资源分配和调度的基本单位 | 拥有独立内存空间和系统资源,有独立的堆和栈 | 通过管道、消息队列、信号量等特定机制 | 较大,需保存和恢复整个进程状态 | 稳定性和安全性相对较高,但上下文切换开销大 |
线程 | 进程内的执行单元,CPU调度和分派的基本单位 | 共享进程的内存空间,包括堆和全局变量 | 可直接读写共享内存 | 较小,只需保存和恢复线程上下文 | 通信高效,上下文切换开销小,但存在数据竞争和线程安全问题 |
协程 | 用户态的轻量级线程 | 拥有自己的寄存器上下文和栈,与其他协程共享堆内存 | 可直接读写共享内存 | 非常小,只需保存和恢复协程上下文,无需内核级切换 | 处理大量并发任务效率高,但需程序员显式调度管理,编程模型复杂 |
为什么进程崩溃不会对其他进程产生很大影响?
隔离性:每个进程都有自己独立的内存空间,当一个进程崩溃时,其内存空间会被操作系统回收,不会影响其他进程的内存空间。
独立性:每个进程都是独立运行的,它们之间不会共享资源。
资源:虚拟内存、文件句柄、信号量等
进程之下为什么还要设计进程?
线程之间可以并发运行且可以共享相同的地址空间,可以解决多进程资源共享和创建开销大等问题。
多线程比单线程的优势,劣势
优势:提高程序的运行速率,可以充分利用多核处理器的资源,同时处理多个任务,加快程序的执行速度。
劣势:存在多线程数据竞争访问的问题,需要通过锁机制来保证线程安全,增加了加锁的开销,并且还会有死锁的风险。多线程会消耗更多资源,因为每个线程都需要占用一定内存和处理时间。
线程是不是越多越好,大多会有什么问题?
切换开销:线程的创建和切换会消耗资源。如果创建太多线程,会占用大量的系统资源,导致系统资源负载过高,某个线程崩溃后,可能会导致进程崩溃。
死锁问题:过多的线程相互竞争可能会导致死锁问题。竞争是指多个线程同时访问和修改共享资源,如果没有合适的同步机制,可能会导致数据不一致或错误的结果。而死锁则是多个线程相互等待对方释放资源,导致程序无法继续运行。
进程切换和线程切换的区别?
进程切换:包括整个进程的地址空间、全局变量、文件描述符等。
线程切换:线程切换只涉及到线程的堆栈、寄存器和程序计数器等,不涉及进程级别资源。
线程切换为什么比进程切换快,节省了什么资源?
线程共享同一进程的地址空间和资源,线程切换时只需要切换堆栈和程序计数器等少量信息,而不需要切换地址空间,避免了进程切换时需要切换内存映射表等大量资源的开销,从而节省了时间和系统资源。
线程切换详细过程时怎么样的?上下文保存在哪里?
步骤:
- 上下文保存:保存当前线程的上下文信息。包括寄存器状态、程序计数器、堆栈指针等,用于保存线程的执行状态。
- 切换到调度器:调度器负责选择下一个要执行的线程,并根据调度算法做出决策。
- 上下文恢复:调度器选择了下一个要执行的线程后,它会从该线程保存的上下文信息中恢复线程的执行状态。
- 切换到新进程:调度器将执行权切换到新进程,使其开始执行。
上下文信息的保存通常由操作系统负责管理,具体保存在哪里取决于操作系统的实现方式。一般情况下,上下文信息会保存在线程的控制块(Thread Control Block,TCB)中。
TCB是操作系统用于管理线程的数据结构,包含了线程的状态、寄存器的值、堆栈信息等。当发生线程切换时,操作系统会通过切换TCB来保存和恢复线程的上下文信息。
进程的状态,如何切换?
- NULL -> 创建状态:一个新进程被创建时的第一个状态;
- 创建状态 -> 就绪状态:当进程被创建完成并初始化后,一切就绪准备运行时,变为就绪状态,这个过程是很快的;
- 就绪态 -> 运行状态:处于就绪状态的进程被操作系统的进程调度器选中后,就分配给CPU正式运行该进程;
- 运行状态 -> 结束状态:当进程已经运行完成或出错时,会被操作系统作结束状态处理;
- 运行状态 -> 就绪状态:处于运行状态的进程在运行过程中,由于分配给它的运行时间片用完,操作系统会把该进程变为就绪态,接着从就绪态选中另外一个进程运行;
- 运行状态 -> 阻塞状态:当进程请求某个事件且必须等待时,例如请求I/O事件;
- 阻塞状态 -> 就绪状态:当进程要等待的事件完成时,它从阻塞状态变到就绪状态;
进程间通讯有哪些方式?
通信方式 | 特点 | 适用场景 |
---|---|---|
管道 | 分为匿名管道和命名管道,匿名管道无名字,存于内存,数据无格式、大小受限、单向通信,用于父子进程;命名管道需在文件系统创建设备文件,突破亲缘关系限制 | 适合简单、对通信速度要求不高,且数据量不大的同主机进程间通信 |
消息队列 | 数据以自定义消息体形式存于内核消息链表,解决管道数据无格式问题,但读写需在用户态与内核态拷贝数据 | 适合同主机进程间,对数据格式有要求,通信速度要求相对不极致的场景 |
共享内存 | 直接分配共享空间,进程可直接访问,速度快,但多进程竞争易致数据错乱 | 对通信速度要求极高,同主机进程间需频繁大量数据交互的场景 |
信号量 | 本质是计数器,通过P、V原子操作实现进程互斥与同步,保护共享资源 | 多进程共享资源访问控制场景 |
信号 | 异步通信机制,用于应用进程和内核交互,通知系统事件,部分信号进程无法捕捉忽略 | 用于通知进程系统事件,如进程结束、停止等控制场景 |
socket | 可用于不同主机或本地主机进程通信,基于TCP、UDP协议或本地进程通信方式 | 不同主机间或本地有网络通信需求的进程间通信 |
管道有几种方式?
匿名管道:是一种在父子进程或者兄弟进程之间进行通信的机制,只能用于具有亲缘关系的进程间通信,通常通过pipe系统创建。
命名管道:是一种允许无关的进程间进行通信的机制,基于文件系统,可以在不相关的进程之间进行通信。
信号和信号量有什么区别?
信号:一种处理异步事件的方式。信号是比较复杂的通信方式,用于通知接收进程有某种事件发生,除了用于进程外,还可以发送信号给进程本身。
信号量:进程间通信处理同步互斥的机制。是在多线程环境下使用的一种设施,它负责协调各个线程,以保证它们能够正确、合理地使用公共资源。
公共内存是怎么实现的?
公共内存的机制:不同进程拿出一块虚拟地址空间来,映射到相同的物理内存中。减少了进程通信来回拷贝问题,提高了通信速率。
线程间通讯有什么方式?
名称 | 原理及特性 |
---|---|
互斥锁(Mutex) | 本质是锁,访问共享资源前加锁,完成后释放。加锁后其他线程尝试加锁会阻塞,直至当前线程释放。释放时有多个阻塞线程,第一个变为运行态可加锁,其余等待 |
条件变量(Condition Variables) | 多线程实现“等待 - 唤醒”逻辑常用方法,利用线程间共享全局变量同步,与互斥锁结合。线程改变条件状态前,先锁互斥量,pthread_cond_wait 操作将自己放等待列表并解锁互斥量(原子操作),返回时重锁 |
自旋锁(Spinlock) | 通过 CPU 的 CAS 函数(Compare And Swap),在用户态完成加解锁,不主动产生线程上下文切换。加锁先查看锁状态,空闲则设置为当前线程持有;竞争时加锁失败线程“忙等待”。CAS 函数将两步合为原子指令,“忙等待”可用 while 循环或 CPU 的 PAUSE 指令实现 |
信号量(Semaphores) | 可命名或无名,用于控制资源访问次数,对应整型变量(sem)。有 P、V 两个原子操作函数:P 操作将 sem 减 1,小于 0 则进程/线程阻塞;V 操作将 sem 加 1,小于等于 0 则唤醒等待进程/线程 |
读写锁(Read-Write Locks) | 由读锁和写锁构成,读资源用读锁加锁,写资源用写锁加锁。写锁未被持有时,多个线程可并发持读锁;写锁被持有时,读、写线程获取锁均会阻塞。适用于读多写少场景 |
除了互斥锁你还知道什么锁?分别应用于什么场景?
读写锁:允许多个线程同时读取共享资源,但只允许一个线程进行写操作。适用于读操作频繁、写操作较少的场景,可以提高并发性能。
自旋锁:是一种忙着等待锁,线程在获取锁时不会进入阻塞状态,而是循环忙等待直到获取到锁。适用于临界区很小且锁的持有时间很短的场景,避免线程频繁切换带来的开销。
条件变量:条件变量用于线程间的同步和通信。它通常与互斥锁一起使用,线程可以通过条件变量等待某个条件满足,当条件满足时,其他线程可以通过条件变量发送信号通知等待线程。
信号量:是一种计数器,用于控制对共享资源的访问。它可以用于限制同时访问资源的线程数量,或者用于线程间的同步。
进程调度算法有哪些?
先来先服务
每次从就绪队列选择最先进入队列的进程,然后一致运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。(利于长作业,不利于短作业)。
最短作业优先
优先选择运行时间最短的进程来运行,有利于提高系统的吞吐量。
不利于长作业:长作业可能不断往后推。
高响应优先
每次进行进程调度时,先计算响应比优先级,然后把响应比优先级最高的进程投入运行。
该算法同时兼顾了短作业和长作业:等待时间相同时,要求的服务时间越短,响应比就越高,兼顾了短作业;要求的服务时间相同时,等待时间越长,响应比越高,兼顾了长作业。
时间片轮调度算法:
最古老、最简单、最公平的且使用最广的算法。
每个进程被分配一个时间段,成为时间片,即允许该进程在该时间段中运行。
- 如果时间片用完,进程还在运行,那么将会把此进程从CPU释放出来,并把CPU分配另外一个进程。
- 如果该进程在时间片结束前阻塞或结束,则CPU立即进行切换。
- 如果时间片设的太短会导致过多的进程上下文切换,降低了CPU效率。
- 如果舍得太长又可能引起对短作业进程的响应时间变长。
最高优先级
从就绪队列中选择最高优先级的进程进行运行。(低优先级的进程可能永远不会运行)
-
静态优先级:创建进程时就已经确定了优先级。
-
动态优先级:根据进程的动态变化调整优先级。随着时间的推移增加等待进程的优先级。
-
非抢占式:当就绪队列中出现优先级高的进程,运行完当前进程,再选择优先级高的进程。
-
抢占式:当就绪队列中出现优先级高的进程,当前进程挂起,调度优先级高的进程运行。
多级反馈队列调度算法
-
多级:表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。
-
反馈:表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列。
工作原理: -
设置多个队列,赋值每个队列不同的优先级,每个队列优先级从高到低,同时优先级越高时间片越短。
-
新的进程会被放入到第一级队列的末尾,按先来先服务的原则排队等待被调度,如果在第一级队列规定的时间片没运行完成,则将其转入到第二级队列的末尾,依次类推,直到完成。
-
当较高优先级的队列为空,才调度较低优先级的队列中的进程运行。如果进程运行时,有新进程进入较高优先级的队列,则停止当前运行的进程并将其移入到原队列末尾,接着让优先级高的进程运行。
对于短作业可能可以在第一级队列很快就被处理完。
对于长作业,如果在第一级队列处理不完,可以移入下次队列等待被执行,虽然等待时间变长了,但是运行时间也会更长了,所以该算法很好的兼顾了长短作业,同时有较好的响应时间。