目录
5. 简述epoll和select的区别,epoll为什么高效?⭐⭐⭐⭐
1.请你说说CPU工作原理⭐⭐
- 取指令(Instruction Fetch):CPU 从内存中获取当前要执行的指令。CPU 会根据指令寄存器中的指令地址,将指令从内存中读取到指令缓存(Instruction Cache)中。
- 解码指令(Instruction Decode):CPU 解析指令,确定指令的操作类型(如加载、存储、运算等),以及操作的操作数(如寄存器、内存地址等)。
- 执行指令(Execute):CPU 根据指令的操作类型和操作数执行相应的操作。这可能涉及数据的加载、存储、算术运算、逻辑运算、分支跳转等操作。
- 访问内存(Memory Access):如果指令需要访问内存(如加载、存储操作),CPU 将计算出需要读取或写入的内存地址,并将数据从内存中读取或写入。
- 写回结果(Write Back):如果执行的指令产生了结果,CPU 将结果写回到寄存器或内存中,以便后续的指令可以使用这些结果。
2.死锁的原因、条件?以及如何预防⭐⭐⭐
死锁: 是指多个进程在执行过程中,因争夺资源而造成了互相等待。此时系统产生了死锁。比如两只羊过独木桥,若两只羊互不相让,争着过桥,就产生死锁。
产生的条件:死锁发生有四个必要条件:
- 互斥条件:进程对已分配的资源独享,其他进程需要等待。
- 请求保持条件:进程需要持有已分配的资源,并继续请求其他资源,但他们无法满足请求,导致进程阻塞并保持对已有资源的持有。
- 不可剥夺条件:进程已经获得的资源不能被其他进程强制性剥夺。
- 环路等待条件:存在多个进程之间形成循环等待资源的关系。
对于预防死锁的方法,可以采取以下措施:
- 破坏互斥条件:尽可能地共享资源,而不是互斥地独占资源。
- 破坏请求保持条件:请求资源时先释放已占有的资源,再请求所需的资源,以避免持有资源的阻塞情况。
- 破坏不可剥夺条件:引入资源抢占机制,使得系统可以对进程已获取的资源进行剥夺。
- 破坏环路等待条件:通过合理的资源分配策略,避免形成循环等待的资源关系。
3.死锁与活锁⭐⭐
死锁(Deadlock)和活锁(Livelock)是多线程并发编程中的两种不同类型的问题。它们之间的区别如下:
死锁:
- 死锁指的是两个或多个线程彼此等待对方释放所持有的资源,从而导致所有线程都无法继续执行,程序无法继续运行的情况。
- 死锁是一种静止状态,线程被无限阻塞,直到外部干预,如强制终止某些线程。
- 死锁发生时,线程无法自行解锁,需要外部的干预来打破循环依赖,释放资源以解决死锁。
活锁:
- 活锁指的是多个线程不断重试,但最终无法取得进展的情况。线程们在不断改变自己的状态,但总是无法成功完成所需的操作。
- 活锁是一种动态状态,线程不断重试,但无法使程序向前推进。
- 活锁通常是由于竞争条件、过度的自旋等问题引起的。
解决活锁问题的一般策略包括:
- 引入随机性:通过引入随机因素,使线程的行为具有一定的不确定性,避免线程们不断重复相同的操作。
- 使用策略:在活锁发生时,采用某种策略,例如放弃一部分工作、转让任务或等待一段随机时间等。
- 调整线程优先级:适当调整线程的优先级,以改变线程的竞争行为。
- 重新设计算法和协调:如果活锁是由于设计问题引起的,需要重新设计算法或协调机制,以避免竞争条件。
4.说说sleep和wait的区别?⭐⭐⭐
sleep和wait的区别:
- 所属类别:sleep是Thread类的方法,而wait是Object类的方法。
- 锁的释放:在调用wait时,线程会释放它持有的锁,进入等待状态,并等待其他线程通过notify或notifyAll来唤醒它。而sleep方法不会释放锁,线程会保持对锁的持有。
- 唤醒方式:调用wait的线程必须依赖其他线程的notify或notifyAll来唤醒它,而sleep方法可以设定一个固定的时间,时间到后线程会自动唤醒。
- 使用场景:wait通常用于线程间的同步和协作,例如等待其他线程的信号或共享资源的通知。sleep适用于线程的暂时休眠,例如实现定时任务或控制线程执行间隔。
5. 简述epoll和select的区别,epoll为什么高效?⭐⭐⭐⭐
epoll:
- epoll是Linux提供的一种I/O事件通知机制,通过epoll系统调用实现。
- 它能够监视大量的文件描述符,并且在文件描述符就绪时触发相应的事件。
- epoll使用了边缘触发模式(EPOLLET),只有当文件描述符状态发生变化时才触发事件通知,避免了频繁的轮询操作,提高了I/O效率。
- epoll适用于大规模并发连接,具有较好的扩展性和性能优势。
Select:
- Select是一种传统的多路复用机制,通过select系统调用实现。
- 它能够监听一组文件描述符,并在其中任意一个文件描述符就绪时返回。
- 在使用select时,需要将文件描述符集合加入到fd_set中,并通过轮询操作检查是否有文件描述符就绪,然后进行相应的操作。
- Select适用于较小规模的连接,但在连接数较大时,效率会下降。
epoll为什么高效?
拷贝开销:
- 每次调用select,都需要将文件描述符集合从用户态拷贝到内核态,这个开销在文件描述符很多时会很大。
- epoll保证了每个文件描述符在整个过程中只会拷贝一次,因此不会有拷贝开销的增加。
遍历开销:
- 每次调用select,需要在内核遍历传递进来的所有文件描述符,即使只有很少的文件描述符就绪。
- epoll只需要轮询一次文件描述符集合,然后查看就绪链表中有没有就绪的文件描述符即可,避免了遍历所有文件描述符的开销。
文件描述符数量限制:
- select有一个固定的限制,它支持的文件描述符数量较小,默认情况下是1024。
- epoll没有这个限制,它所支持的文件描述符上限取决于系统最大可以打开的文件数目,一般远大于1024。
6.简述互斥锁的机制,互斥锁与读写的区别?⭐⭐
互斥锁(Mutex)是一种用于线程同步的机制,用于保护共享资源在多个线程间的互斥访问。互斥锁的特点如下:
- 互斥原理:当一个线程获得互斥锁时,其他线程需要等待该线程释放锁才能继续执行。通过互斥锁的加锁和解锁操作,可以确保同一时间只有一个线程能够访问共享资源,从而保证数据的一致性和正确性。
- 加锁操作:当线程想要访问共享资源时,首先需要尝试获得互斥锁。如果互斥锁已被其他线程占用,则当前线程会被阻塞,直到互斥锁被释放。一旦线程成功获得互斥锁,它就可以安全地访问共享资源。
- 解锁操作:线程使用完共享资源后,应该释放互斥锁,以便其他线程可以继续访问。释放互斥锁将导致等待该锁的线程中的一个或多个线程恢复执行。
互斥锁与读写锁(读写互斥锁)的区别如下:
- 互斥锁(Mutex):互斥锁保证了在任意时刻只有一个线程能够获得锁。当一个线程获得互斥锁后,其他线程必须等待该线程释放锁才能继续执行。互斥锁适用于对共享资源的互斥访问,既包括读操作也包括写操作。
- 读写锁(ReadWrite Lock):读写锁允许多个线程同时读共享资源,但在写共享资源时需要互斥访问。多个线程可以同时获取读写锁的读锁,只有当没有线程持有读锁时,写锁才能被获取。读写锁适用于读多写少的场景,可以提高并发性能。
7.中断怎么发生,中断处理大概流程⭐⭐
中断的发生过程大致分为以下几个步骤:
- 硬件触发中断:中断可以通过硬件设备(如时钟、外部设备、异常等)发出中断请求信号。
- 中断请求处理:当硬件设备发出中断请求信号后,CPU会检查中断请求线。如果存在中断请求,CPU会立即响应并暂停当前正在执行的程序。
- 中断处理程序调用:当中断发生时,CPU会根据中断号或中断向量表中的映射关系,找到对应的中断处理程序的入口地址。CPU将当前执行的指令的状态保存(通常是保存到栈或特定的寄存器中),然后跳转到中断处理程序的入口地址。
- 中断处理:中断处理程序根据中断类型或中断号,执行相应的处理逻辑。处理逻辑可以包括保存寄存器状态、处理中断事件、与硬件设备交互、更新数据结构等操作。
- 恢复执行:中断处理程序执行完毕后,会根据之前保存的状态信息(通常是从栈或特定的寄存器中恢复)恢复被中断的程序的执行。CPU从保存的状态信息中获取之前被中断的程序的状态,然后继续执行被中断的程序。
8.说说多路IO复用技术有哪些,区别是什么?⭐⭐
Select:
- select 是最古老的多路 I/O 复用技术之一,在多个文件描述符上执行 I/O 事件的轮询。
- 它使用一个位掩码(fd_set)来表示文件描述符集合,并提供了三个集合用于监视读、写和异常条件。
- 当调用 select 函数时,内核会遍历指定的文件描述符集合,检查是否有就绪的 I/O 事件,并进行相应的处理。
- 但是 select 有一些限制,如文件描述符数量限制和效率问题。
Poll:
- poll 是相对于 select 的改进,解决了 select 的一些限制问题。
- 它基于一个结构数组(struct pollfd)来存放需要监听的文件描述符和事件,通过调用 poll 函数进行监视。
- 类似于 select,当调用 poll 函数时,内核会遍历指定的文件描述符数组,检查是否有就绪的 I/O 事件,并进行相应的处理。
- 相比于 select,poll 没有文件描述符数量的限制,但在大量文件描述符上轮询时性能较差。
epoll:
- epoll 是基于事件驱动的多路 I/O 复用技术,在 Linux 系统中提供更高效的 IO 事件通知机制。
- 它使用回调式监听机制,可以在较大的文件描述符集合上快速发现和应答就绪的 I/O 事件。
- epoll 使用三个接口函数(epoll_create、epoll_ctl 和 epoll_wait)来创建、添加和等待 I/O 事件。
- 相比于 select 和 poll,epoll 避免了在每次调用时都需要遍历整个文件描述符集合的开销,具有更高的性能。
9.epoll水平触发与边缘触发的区别?⭐⭐⭐
在 epoll 中,有两种触发模式:水平触发(Level-Triggered,LT)和边缘触发(Edge-Triggered,ET)。
- 水平触发(LT):在水平触发模式下,当一个文件描述符上有数据可读或可写时,epoll_wait 函数会立即返回,并通知用户程序进行 I/O 操作。如果文件描述符上的数据没有被完全读取或写入,下次调用 epoll_wait 时仍然会通知用户程序。水平触发是默认的触发模式,在 epoll_ctl 函数中不指定 EPOLLET 标志即可使用水平触发。
- 边缘触发(ET):在边缘触发模式下,当一个文件描述符上的状态发生变化时(如从无数据变为有数据可读),epoll_wait 函数会通知一次,并且只通知一次。边缘触发模式要求用户程序持续地读取或写入数据,直到发生 EAGAIN 错误,才能确保不会错过任何事件。边缘触发需要在 epoll_ctl 函数中指定 EPOLLET 标志来开启。
区别:
- 触发方式:水平触发:只要文件描述符上有数据可读或可写,就会触发事件,不管之前是否已经通知过。边缘触发:只有在文件描述符的状态发生变化时才会触发一次事件通知。
- 通知次数:水平触发:如果数据没有被完全读取或写入,下次调用 epoll_wait 时仍然会通知用户程序。边缘触发:只会在文件描述符的状态发生变化时通知一次,需要用户程序持续读取或写入数据。
- I/O 处理方式:水平触发:不需要特别处理,可以使用常规的阻塞或非阻塞方式进行 I/O 操作。边缘触发:需要保持持续读取或写入数据,以免错过事件,一般需要使用非阻塞方式进行 I/O 操作。
10.请介绍一下5种IO模型⭐⭐⭐
阻塞式 IO 模型:
- 在阻塞式 IO 模型中,当应用程序发起一个 IO 操作时,它会一直等待,直到操作完成并返回结果。
- 在这个过程中,应用程序无法进行其他任务,会一直阻塞在该 IO 操作上。
- 阻塞式 IO 模型简单易用,但会导致应用程序的整体性能受限,因为在等待 IO 完成期间无法进行其他工作。
非阻塞式 IO 模型:
- 在非阻塞式 IO 模型中,应用程序通过设置文件描述符为非阻塞模式进行 IO 操作。
- 当进行非阻塞 IO 操作时,如果操作不能立即完成,系统会立即返回,而不会阻塞应用程序。
- 应用程序可以继续进行其他任务,然后通过轮询或其他手段来查询IO操作的状态,直到操作完成。
IO 复用模型:
- IO 复用模型使用 select、poll 或 epoll 等函数,可以同时监听多个文件描述符上的 IO 事件。
- 当任何一个文件描述符上有 I/O 事件发生时,应用程序会被通知,然后可以执行相应的读写操作。
- IO 复用模型避免了阻塞操作,可以同时处理多个文件描述符上的 I/O 事件,提高了系统的并发性能。
信号驱动式 IO 模型:
- 在信号驱动式 IO 模型中,应用程序发起一个 IO 操作后,可以继续进行其他任务而不会阻塞。
- 当 IO 操作完成时,系统会向应用程序发送一个信号,应用程序通过信号处理函数处理完成的 IO 事件。
异步 IO 模型:
- 在异步 IO 模型中,应用程序发起一个 IO 操作后,可以继续进行其他任务而不会阻塞。
- 当 IO 操作完成时,系统会通知应用程序,应用程序可以直接处理完成的 IO 事件