[操作系统]常见面试题--无序版
操作系统64位与32位的区别
内存寻址能力:64位操作系统通常最大可支持16EB的物理内存,32位由于地址空间的限制,最多只有4GB的物理内存
运算性能:64位CPU有更宽的通用寄存器和更大的指令集,一次可以处理更多数据
进程和线程的区别
- 进程是系统资源分配和调度的最小单元,线程是CPU调度和分配的最小单位
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程
- 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
- 资源分配给进程,同一进程的所有线程共享该进程的所有资源
- 进程的创建、销毁、切换的开销大于线程
- 线程不能独立执行,必须依存在进程中
线程快在哪?
- 线程创建时有些资源不需要自己管理,直接从进程取用,线程只需要管理寄存器和栈的声明周期
- 同一进程内多个线程共享数据,所以进程数据传输可以用零拷贝技术,不需要经过内核
- 进程使用一个虚拟内存跟页表,然后多个线程共用这些虚拟内存,如果同进程内两个线程进行上下文切换比进程快很多
线程分为用户态线程和内核态线程,二者有什么区别?
用户态线程工作在用户空间,内核态线程工作在内核空间。用户态线程调度完全由进程负责,通常就是由进程的主线程负责。相当于进程主线程的延展,使用的是操作系统分配给进程主线程的时间片段。内核线程由内核维护,由操作系统调度。
用户态线程无法跨核心,一个进程的多个用户态线程不能并发,阻塞一个用户态线程会导致进程的主线程阻塞,直接交出执行权限。这些都是用户态线程的劣势。内核线程可以独立执行,操作系统会分配时间片段。因此内核态线程更完整,也称作轻量级进程。内核态线程创建成本高,切换成本高,创建太多还会给调度算法增加压力,因此不会太多。
实际操作中,往往结合两者优势,将用户态线程附着在内核态线程中执行。
说说协程
为了处理高并发、多连接下的数据读写,以往有两种解决方案。
- 多进程:存在频繁调度切换的问题,同时HIA存在每个进程资源不共享的问题,需要额外引入进程间通信机制来解决。
- 多线程:高并发场景的大量IO等待会导致多线程被频繁挂起和切换,非常消耗系统资源,同时多线程访问共享资源存在竞争问题
协程是一种比线程更加轻量级的微线程,一个线程可以拥有多个协程
协程运行在线程之上,当一个协程执行完成之后,可以选择主动出让,让另一个协程运行在当前线程之上。
协程并没有增加线程 数量,只是在线程的基础之上通过分时复用的方式运行多个协程,而且协程的切换在用户态完成,切换的代价比线程从用户态到内核态的代价小很多。
因此,使用协程替换线程适用于有大量IO操作业务的情况下。一是降低了系统内存,二是减少了系统切换开销,因此系统的性能也会提升。
由于在协程中调用阻塞IO的方法会导致在该线程之上的所有协程都陷入阻塞。因此协程只有和异步IO结合起来才能发挥出最大的威力
进程通信
进程的用户地址空间是相互独立的,不可以互相访问,但内核空间是进程都共享的,所以进程之间要通信必须通过内核。进程间通信主要有管道、消息队列、共享内存、信号量、信号、Socket编程
管道分为匿名管道和有名管道,管道是半双工通信的。管道的本质就是内核在内存中开辟了一个缓冲区,这个缓冲区与管道文件相关联,对管道文件的操作,被内核转换成对这块缓冲区的操作。
- 匿名管道只能用于父子进程间的通信,有名管道只要可以访问路径就可以通过有名
信号是进程通信机制中唯一的异步通信机制,它可以在任何时候发送信号给某个进程。通过发送指定信号来通知进程某个异步事件的发送,以迫使进程执行信号处理程序。信号处理完毕后,被中断进程将恢复执行。用户、内核和进程都能生成和发送信号。
信号的来源有硬件来源和软件来源,常见的有Ctrl+9 1111
- 匿名管道只能用于父子进程间的通信,有名管道只要可以访问路径就可以通过有名
Socket
用于跨网络与不同主机上的进程进行通信,当然也可以完成同主机上的进程通信
Socket的本质是一个编程接口(API)
是应用层与TCP/IP协议的网络通信的抽象层。它对TCP/IP进行了封装,它把复杂的TCP/IP协议簇隐藏在Socket接口后面。对于用户来说,只需要进行一组简单的API就可以实现网络的连接C产生SIGINT信号,表示终止该进程;软件来源如kill -管道进行通信- 管道这种进程通信方式虽然使用简单,但是效率比较低,不适合进程间频繁地交换数据,并且管道只能传输无格式的字节流
消息队列的本质就是存放在内存中的消息的链表,而消息本质上是用户自定义的数据结构。如果进程从消息队列中读取了某个消息,这个消息就会从消息队列中删除
- 消息队列可以实现消息的随机查询,不一定要以先进先出的次序读取消息,也可以按消息类型读取,比有名管道的先进先出更有优势
- 用户进程写入数据到消息队列时,会发生从用户态拷贝数据到内核态,用户进程读也会发生拷贝。如果数据量较大,,使用消息队列会造成频繁系统调用,会降低性能
共享内存可以避免消息队列的拷贝消息、进行系统调用,允许不相干的进程将同一段物理内存连接到它们各自的地址空间中,使得这些进程可以访问同一个物理内存,这个物理内存就成为共享内存。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
- 共享内存仅在建立共享内存区域时需要系统调用,一旦建立共享内存,所有的访问都可作为常规内存访问,无需借助内核。这样,数据就不需要在进程之间来回拷贝,所以这是最快的一种进程通信方式。但是却容易造成数据冲突
信号量,信号量是 一个整型计数器,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据,保证共享内存在任何时刻只有一个进程在访问(保证互斥),并使得进程们能够按照某个特定的程序访问共享内存(同步)
- 利用PV操作实现
- P操作:将信号量值减一,表示申请占用一个资源。如果结果小于 0,表示已经没有可用资源,则执行 P 操作的进程被阻塞。如果结果大于等于 0,表示现有的资源足够你使用,则执行 P 操作的进程继续执行。
- V操作:将信号量值加一,表示释放一个资源,即使用完资源后归还资源。
信号
信号与信号量完全不同!!!
信号是进程通信机制中唯一的异步通信机制,它可以在任何时候发送信号给某个进程。通过发送指定信号来通知进程某个异步事件的发送,以迫使进程执行信号处理程序。信号处理完毕后,被中断进程将恢复执行。用户、内核和进程都能生成和发送信号。
信号的来源有硬件来源和软件来源,常见的有Ctrl+C产生SIGINT信号,表示终止该进程;软件来源如kill -9 1111
- Socket
用于跨网络与不同主机上的进程进行通信,当然也可以完成同主机上的进程通信
Socket的本质是一个编程接口(API)
是应用层与TCP/IP协议的网络通信的抽象层。它对TCP/IP进行了封装,它把复杂的TCP/IP协议簇隐藏在Socket接口后面。对于用户来说,只需要进行一组简单的API就可以实现网络的连接
信号量的值 大于 0 表示有共享资源可供使用,这个时候为什么不需要唤醒进程?
所谓唤醒进程是从就绪队列(阻塞队列)中唤醒进程,而信号量的值大于 0 表示有共享资源可供使用,也就是说这个时候没有进程被阻塞在这个资源上,所以不需要唤醒,正常运行即可。
信号量的值 等于 0 的时候表示没有共享资源可供使用,为什么还要唤醒进程?
V 操作是先执行信号量值加 1 的,也就是说,把信号量的值加 1 后才变成了 0,在此之前,信号量的值是 -1,即有一个进程正在等待这个共享资源,我们需要唤醒它。
使用信号量和 PV 操作实现进程的同步也非常方便,三步走:
- 定义一个同步信号量,并初始化为当前可用资源的数量
- 在优先级较高的操作的后面执行 V 操作,释放资源
- 在优先级较低的操作的前面执行 P 操作,申请占用资源
虚拟内存的布局
虚拟内存是如何实现的
虚拟内存技术的实现是建立在离散分配的内存管理的基础上的
目前最常用的三种实现虚拟内存技术的方法:
请求分页存储管理
请求分段存储管理
请求段页式存储管理
以上三种方式,无论哪种,都需要以下三个条件:一定容量的内存和外存
程序执行时,只需要将程序的一部分装入内存,就可以运行了缺页中断
若需要执行的程序未在内存中(即“缺页/段”),则处理器会通知操作系统将相应的页/段调入到内存中,与此同时也会将不常用的页/段调出到外外存中虚拟地址空间
逻辑地址转化为物理地址
用什么命令查看内存使用情况
top
这个命令会动态显示系统中各个进程的资源占用状况,包括CPU使用率和内存使用情况。按Shift + M可以按照内存使用量进行排序。
ps aux
该命令可以显示所有进程的详细信息,包括进程ID(PID)、用户、CPU和内存使用百分比等
- top
- pidstat
- ps
- pmap
select poll epoll
IO多路复用是用一个进程来维护多个Socket
而select poll epoll就是内核提供给用户态的多路复用系统调用,进程可以通过一个系统调用函数从内核获取多个事件
这三者都是在获取事件时,先把所有连接(文件描述符fd)传给内核,再由内核返回产生了事件的连接,然后在用户态中再处理这些连接对应的请求即可
select
将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生。检查的方式就是暴力的遍历
当经检查到有事件产生后,将次Socket标记为可读或者可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。
所以,对于select这种方式,需要进行2次遍历文件描述符集合,一次在内核态、一次在用户态。还要发生2次拷贝文件描述符集合,先从用户空间传入内核空间中,再由内核修改后,再传出到用户空间中
select 使用固定长度的 BitsMap,表示文件描述符集合,而且所支持的文件描述符的个数是有限制的,在 Linux 系统中,由内核中的 FD_SETSIZE 限制, 默认最大值为 1024,只能监听 0~1023 的文件描述符。poll
poll 不再用 BitsMap 来存储所关注的文件描述符,取而代之用动态数组,以链表形式来组织,突破了 select 的文件描述符个数限制,当然还会受到系统文件描述符限制。
但是 poll 和 select 并没有太大的本质区别,都是使用「线性结构」存储进程关注的 Socket 集合,因此都需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n),而且也需要在用户态与内核态之间拷贝文件描述符集合,这种方式随着并发数上来,性能的损耗会呈指数级增长。epoll
- epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字,红黑树的增删改一般时间复杂度是O(logn),由于有了红黑树来保存所有待检测的Socket,所有epoll机制不必要每次操作都传入整个Socket集合给内核,只需要传入一个待检测的Socket,减少了内核和用户空间大量数据拷贝和内存分配
- epoll使用事件驱动的机制,其内核维护了一个链表来记录就绪事件,当某个Socket有事件发生时,通过回调函数,内核会将这个就绪事件加入到就绪时间列表当中,当用户调用epoll_wait函数时,只会返回有事件发生的文件描述符的个数,不需要轮询整个Socket集合,大大提高可检测效率
[操作系统]常见面试题--无序版
https://woshizhangmingyu.github.io/2024/04/16/操作系统-常见面试题-无序版/
install_url
to use ShareThis. Please set it in _config.yml
.