坚持7天,短期内快速完成C++后端面试突击。每天10题,弥补后端八股知识缺漏,熟练掌握后端的高频考点,后端面试更有把握。
1. 进程和线程的区别
进程(Process)和线程(Thread)是操作系统中用于管理程序执行的基本单位,它们之间有以下区别:
-
定义:
- 进程是程序执行的一个实例,是一个正在运行的程序的执行环境,拥有自己的地址空间、内存、文件描述符和其他系统资源。
- 线程是进程内的一个独立执行流程,是程序执行的最小单位,由线程ID、程序计数器、寄存器集合和栈组成,共享进程的资源。
-
资源拥有:
- 进程拥有独立的资源,包括内存空间、文件描述符、设备、进程控制块等。
- 线程共享所属进程的资源,包括内存空间、文件描述符、设备等。
-
并发性:
- 进程是系统进行资源分配和调度的一个独立单位,不同进程之间通常是相互独立运行的,各自拥有独立的地址空间和资源。
- 线程是进程内的并发执行单元,同一进程内的多个线程共享相同的地址空间和资源,可以并发执行,相互之间可以通过共享内存进行通信。
-
创建开销:
- 创建进程的开销比较大,需要为进程分配独立的地址空间、资源等,因此进程的创建和销毁通常较为耗时。
- 创建线程的开销相对较小,因为线程共享了进程的资源,不需要分配独立的地址空间,因此线程的创建和销毁通常较为轻量级。
-
通信与同步:
- 不同进程之间的通信和同步需要通过进程间通信(IPC)机制来实现,例如管道、消息队列、共享内存等。
- 线程之间的通信和同步较为简单,因为它们共享进程的地址空间,可以直接通过共享内存或者信号量等方式进行通信和同步。
总的来说,进程是操作系统进行资源分配和调度的基本单位,线程是程序执行的最小单位,是进程内的并发执行单元。它们之间的区别主要体现在资源拥有、并发性、创建开销、通信与同步等方面。在设计和编写程序时,应根据具体需求和应用场景选择合适的进程和线程模型。
2. 进程线程都怎么通信
进程和线程之间通信的方式主要有以下几种:
-
进程间通信(Inter-Process Communication,IPC):
- 管道(Pipe):管道是一种半双工的通信方式,通常用于具有父子关系的进程之间进行通信。
- 消息队列(Message Queue):消息队列是一种存放在内核中的消息链表,多个进程可以通过读写消息队列来进行通信。
- 共享内存(Shared Memory):共享内存是一块由操作系统管理的共享区域,允许多个进程将同一块内存映射到各自的地址空间,从而实现数据共享。
- 信号量(Semaphore):信号量是一个计数器,用来控制对共享资源的访问,多个进程可以通过对信号量的操作来实现互斥和同步。
- 套接字(Socket):套接字是一种网络通信方式,用于不同计算机上的进程之间进行通信,可以实现跨网络的进程通信。
-
线程间通信(Inter-Thread Communication,ITC):
- 锁(Lock):线程可以使用互斥锁、读写锁等锁机制来实现对共享资源的访问控制,确保多个线程对共享资源的互斥访问。
- 条件变量(Condition Variable):条件变量是一种线程同步机制,用于线程之间的等待和通知,可以通过条件变量实现线程的等待和唤醒。
- 信号量(Semaphore):信号量也可以用于线程之间的同步和互斥,多个线程可以通过对信号量的操作来实现对共享资源的控制和访问。
- 屏障(Barrier):屏障是一种同步机制,用于多个线程在同一个点上进行等待,直到所有线程都到达这个点后才能继续执行。
3. 计算机内核态和用户态概念
计算机内核态(Kernel Mode)和用户态(User Mode)是操作系统中的两种不同的运行模式,它们之间的主要区别在于对系统资源的访问权限和能力。
-
内核态(Kernel Mode):
- 内核态是操作系统核心代码运行的特权模式,也称为特权模式或监管模式。
- 在内核态下,操作系统拥有对系统硬件和资源的完全访问权限,可以执行特权指令(如读写系统寄存器、访问硬件设备等)。
- 内核态中运行的代码具有更高的权限级别,可以直接操作系统核心数据结构和执行敏感操作。
- 只有在内核态下,才能够执行一些关键的操作,如创建和销毁进程、分配和释放内存、管理设备驱动等。
-
用户态(User Mode):
- 用户态是普通用户程序运行的一种受限模式,也称为非特权模式。
- 在用户态下,用户程序只能访问受限的资源和硬件设备,不能直接访问操作系统的核心部分。
- 用户态下的程序不能执行特权指令,也不能直接访问硬件设备,需要通过系统调用(System Call)来请求内核态执行相关操作。
- 在用户态下,程序的运行受到较多的限制,如不能直接访问操作系统内核数据结构、不能直接控制硬件设备等。
4. 虚拟地址怎么映射到物理地址
虚拟地址到物理地址的映射是操作系统中的重要概念,用于实现虚拟内存管理。这个过程通常涉及到内存管理单元(MMU)的介入,MMU负责在CPU执行指令时将虚拟地址转换为物理地址。
虚拟地址到物理地址的映射过程大致如下:
-
分页机制:
- 操作系统将虚拟内存空间和物理内存空间分成固定大小的页(Page),通常是4KB或者4MB大小。
- 对于每个进程,操作系统维护一个页表(Page Table),用来记录虚拟页和物理页之间的映射关系。
-
地址转换:
- 当CPU执行指令时,产生的地址是虚拟地址,例如指令访问某个变量时的地址。
- MMU根据虚拟地址的高位(页号)在页表中查找对应的页表项(Page Table Entry,PTE)。
-
页表查询:
- 根据页表项中的物理页号(Page Frame Number,PFN),找到对应的物理页。
- 如果页表中没有对应的页表项,或者虚拟地址所对应的物理页不在内存中,则触发页面错误(Page Fault)异常。
-
物理地址生成:
- 将虚拟地址的低位偏移量(Offset)与物理页中的相应位置相加,得到最终的物理地址。
-
访问内存:
- CPU将得到的物理地址发送给内存控制器,从物理地址对应的位置读取数据或者将数据写入到这个位置。
5. 死锁的解决方法
死锁(Deadlock)是指两个或多个进程在执行过程中,因竞争系统资源而造成的一种僵局,导致各个进程都在等待其他进程释放资源,从而无法继续执行。死锁的解决方法通常包括以下几种:
-
预防死锁:
- 预防死锁是在系统设计和实现阶段采取的一系列措施,旨在防止死锁的发生。
- 如避免使用多个资源、使用资源有序分配、使用资源抢占等方法。
-
避免死锁:
- 避免死锁是在运行时采取的一系列措施,通过谨慎的资源分配和调度来避免死锁的发生。
- 常用的方法包括银行家算法(Banker’s Algorithm)、资源分配图(Resource Allocation Graph)等。
-
检测死锁:
- 检测死锁是在系统运行时定期检查资源分配情况,发现死锁后采取相应的措施解除死锁。
- 常用的方法包括死锁检测算法、等待图(Wait-for Graph)等。
-
解除死锁:
- 一旦检测到死锁,系统需要采取措施解除死锁,以恢复系统的正常运行。
- 常见的解除死锁的方法包括资源剥夺(Resource Preemption)、进程回退(Process Termination)等。
-
防止死锁:
- 防止死锁是在系统设计和实现阶段通过一些策略来降低死锁发生的概率,例如尽量减少资源竞争、避免循环等。
在实际应用中,死锁是一个非常复杂和常见的问题,解决起来需要综合考虑系统的性能、可靠性和复杂性等因素。通常采用预防、避免、检测和解除死锁相结合的方式来应对死锁问题。
6. 简述进程切换的流程
进程切换是操作系统中的重要操作,用于实现多任务调度和并发执行。下面是简要的进程切换流程:
-
上下文保存(Context Saving):
- 当操作系统决定要切换到另一个进程时,当前运行进程的上下文(Context)需要保存到内存中,以便稍后恢复。
- 上下文包括处理器寄存器的状态(如程序计数器、堆栈指针、通用寄存器等)、进程控制块(Process Control Block,PCB)中的进程状态(如进程ID、程序计数器、栈指针、寄存器值等)、栈和堆的状态以及其他相关信息。
-
调度器选择新进程(Scheduler Selection):
- 操作系统调度器选择下一个要执行的进程,通常根据调度算法(如先来先服务、最短作业优先、轮转调度等)从就绪队列中选择一个进程。
- 调度器的选择可能受到各种因素的影响,如进程的优先级、调度策略等。
-
加载新进程上下文(Context Loading):
- 当调度器选择了新的进程后,需要将该进程的上下文从内存中加载到处理器寄存器和内存中,以便开始执行。
- 这包括将新进程的寄存器状态、堆栈指针、程序计数器等恢复到处理器寄存器中,并更新内存管理单元(MMU)等。
-
进程控制转移(Process Control Transfer):
- 当新进程的上下文加载完毕后,控制权从操作系统内核转移到新进程的用户态代码中,使其开始执行。
- 新进程从上次中断或者上次被抢占的地方继续执行,即恢复到其上次的执行状态。
-
执行新进程(Execution of New Process):
- 新进程开始执行其用户态代码,直到它完成其任务、主动放弃CPU、发生中断或者被操作系统抢占等。
- 进程切换过程完成,新进程成为当前运行的进程。
7. 简述 IO 多路复用
I/O多路复用(I/O Multiplexing)是一种高效的I/O模型,它允许单个线程同时监视多个I/O操作,以实现并发I/O处理。这种技术在网络编程中非常常见,可以通过少量的线程来处理大量的并发连接。
-
创建文件描述符集合:
- 程序通过调用特定的系统调用(如
select()
、poll()
、epoll()
等)创建一个文件描述符集合,将需要监视的文件描述符(如套接字、文件等)添加到集合中。
- 程序通过调用特定的系统调用(如
-
阻塞等待事件:
- 调用系统调用,将线程阻塞在文件描述符集合上,等待任何一个或多个文件描述符上发生事件(如可读、可写、异常等)。
- 在阻塞期间,线程不需要执行忙等待或者轮询,而是将控制权交给操作系统。
-
事件就绪通知:
- 当任何一个文件描述符上发生了需要监视的事件,操作系统将通知调用线程。
- 线程被唤醒后,可以通过查询文件描述符集合,确定哪些文件描述符上发生了事件。
-
事件处理:
- 线程根据文件描述符上发生的事件类型(如可读、可写等)执行相应的I/O操作。
- 如果需要,线程可以调用非阻塞I/O操作来处理事件。
-
重复步骤2-4:
- 线程处理完当前的事件后,可以再次调用系统调用等待下一组事件发生,从而实现持续的事件监视和处理。
I/O多路复用的优点在于可以使用少量的线程来处理大量的I/O操作,从而减少了线程的创建和销毁开销,提高了系统的性能和资源利用率。常见的I/O多路复用机制包括select、poll和epoll等。
8. 计算机网络输入URL到看到网页
-
DNS解析:
- 当用户在浏览器中输入URL时,浏览器首先会通过DNS解析将域名解析成对应的IP地址。如果浏览器本地缓存中没有对应的IP地址,将向DNS服务器发送查询请求。
-
建立TCP连接:
- 浏览器通过TCP/IP协议栈向目标服务器发送连接请求,进行三次握手,确立与服务器的TCP连接。这个过程包括:客户端向服务器发送SYN(同步)报文段,服务器收到后回应ACK(确认)和SYN,客户端再次回应ACK。
-
发起HTTP请求:
- 浏览器向服务器发送HTTP请求,请求特定URL对应的资源(如HTML文档、图片、CSS文件等)。HTTP请求包含请求头部、请求体等信息。
-
服务器处理请求:
- 服务器收到HTTP请求后,会根据请求的URL和方法进行相应的处理,查找请求的资源并返回给浏览器。服务器可能需要访问数据库、调用后端程序等。
-
返回HTTP响应:
- 服务器将处理后的资源通过HTTP响应返回给浏览器。HTTP响应包括响应头部、响应状态码和响应体等信息。
-
浏览器渲染页面:
- 浏览器接收到服务器返回的HTML文档后,开始解析HTML文档,并逐步加载其中引用的其他资源(如图片、CSS文件、JavaScript文件等)。
- 浏览器根据HTML结构和CSS样式渲染页面,将页面内容呈现给用户。
-
显示页面:
- 浏览器将渲染后的页面内容显示在用户的屏幕上,用户可以查看和与页面进行交互。
9. TCP 三次握手的目的是什么?为什么不用两次和四次?
TCP的三次握手是建立TCP连接时的重要步骤,其主要目的包括:
-
确认双方的通信能力:
- 第一次握手:客户端向服务器发送SYN(同步)报文段,请求建立连接。通过这个步骤,客户端可以确定服务器是否处于可达状态,是否能够接收连接请求。
- 第二次握手:服务器收到SYN报文段后,回应ACK(确认)和SYN,表明已收到连接请求,并且同意建立连接。通过这个步骤,服务器可以确认客户端的可达性和通信能力。
- 第三次握手:客户端收到服务器的确认后,再次回应ACK,表明已收到服务器的回应。通过这个步骤,客户端可以确认服务器的可达性和通信能力。
-
防止失效的连接请求导致资源浪费:
- 如果客户端发送了SYN报文段但未收到服务器的回应,客户端将重新发送SYN报文段。这样可以避免因为网络中的丢包或者延迟等原因导致的失效连接请求。
-
防止网络中重复的连接建立:
- 在网络中可能存在多条路径,客户端和服务器可能会收到重复的连接建立请求。通过三次握手,可以避免因为重复的连接建立请求导致资源的浪费和不必要的连接。
为什么不使用两次握手或四次握手呢?
-
两次握手:两次握手的话,客户端发送SYN报文段后,服务器接收到后立即回应ACK和SYN,就建立连接。但这样的话,如果这个ACK和SYN报文段在网络中延迟了很长时间,客户端可能会误认为连接已经建立,但实际上连接并没有建立。因此,需要第三次握手来确认连接确实已经建立。
-
四次握手:四次握手是在关闭连接时使用的,用于确保双方都已经完成了关闭操作。在建立连接时,多余的确认步骤会增加连接建立的延迟和网络负载。因此,使用三次握手足以满足建立连接的需求,而不需要额外的握手步骤。
10. 加问:那挥手为什么需要四次呢?三次不行吗?
挥手过程需要四次握手的原因如下:
-
客户端发起关闭请求:
- 客户端首先向服务器发送FIN(结束)报文段,表示客户端已经完成了数据的发送,希望关闭连接。
-
服务器确认关闭请求:
- 服务器收到客户端的FIN报文段后,确认收到了关闭请求,但可能仍有数据需要发送给客户端,因此服务器发送ACK报文段作为确认。
-
服务器发起关闭请求:
- 当服务器也完成了数据的发送后,需要向客户端发送FIN报文段,表示服务器也准备关闭连接。
-
客户端确认关闭请求:
- 客户端收到服务器的FIN报文段后,确认收到了关闭请求,发送ACK报文段作为确认。至此,双方都确认关闭请求,连接正式关闭。
这样的挥手过程可以保证双方都能够完成数据的发送和接收,并且确认对方已经准备好关闭连接。如果只使用三次握手,可能会导致一方在收到对方的关闭请求后直接关闭连接,而另一方还有数据需要发送,从而导致数据丢失或者连接异常关闭的情况发生。因此,四次握手是确保连接安全关闭的必要步骤。