文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

面试题:BIO,NIO,AIO 的区别是什么?说说 select 和 epoll 工作机制与差异?为何 epoll 如此高效

2024-11-28 15:17

关注

面试官:说说看你知道的IO模型有哪些?

先说说看什么是IO吧。IO表示输入输出,访问外部设备读取数据或者向外部设备写数据。常见的文件读写操作、网络通信操作,其实都是IO的过程。

这里以阻塞式读取为例,分析IO操作所进行的工作:

因此,我们可以认为,IO = 等待 + 拷贝。只要执行流(进程/线程)参与了等待和拷贝其中之一,那么就认为这个执行流进行了IO操作。

下图展示了在进行写文件操作时系统所进行的工作,可分为三步:

再来说IO模型,经典的IO模型主要包括以下几种:

一、阻塞IO(BIO)

定义:在BIO模型中,当程序进行IO操作时,它会持续等待直到操作完成。在此期间,线程被阻塞,不能执行其他任务。

特点:

二、非阻塞IO(NIO)

定义:为了解决BIO模型的线程阻塞问题,NIO模型引入了非阻塞的概念。在NIO中,当一个线程进行IO操作时,它不会等待操作完成,而是继续执行其他任务。当IO操作完成时,线程会收到通知。非阻塞式IO一般采用轮询检查的方法进行IO操作,即:通过循环,不断检查IO资源是否已经就绪,就绪就读取,不就绪就执行其他的工作。

特点:

三、异步IO(AIO)

定义:异步IO就是发起IO的执行流本身不参与IO工作,而是有另外一个执行流来进行IO操作,发起IO的线程只需要等待进行IO的执行流反馈回结果。这样发起IO的执行流就不需要等到,可以处理其他的工作。当操作完成时,系统会通知该线程。

特点:

四、IO多路复用

定义:使用操作系统提供的select、poll或epoll等多路复用机制,允许应用程序同时监视多个IO事件。

特点:

五、信号驱动IO

定义:通过自定义对SIGIO信号的处理函数来实现信号驱动式IO,当进程收到SIGIO信号的时候,就调用对应的处理函数来进行IO操作,这样保证在调用IO接口的时候数据一定是就绪的,在没有收到信号时不影响进程进行其他的工作,信号驱动式IO避免了阻塞等待资源就绪,提高了IO效率。

特点:

面试官:阻塞IO(BIO)和非阻塞IO(NIO)的区别是什么?NIO和异步IO(AIO)的区别是什么?

一、以下是 BIO 和 NIO 的主要差异:

(1) 阻塞IO(Blocking IO)

(2) 非阻塞IO(Non-blocking IO)

(3) 选择

二、以下是NIO和AIO的差异:

(1) 工作原理

NIO:

AIO:

(2) 性能与资源消耗

NIO:

AIO:

(3) 编程复杂度

NIO:

AIO:

面试官:能详细说一下多路复用中 select 和 epoll 的工作机制和性能差异吗?

先说说多路复用。多路复用(Multiplexing)是一种能够同时监控多个文件描述符(如网络连接、文件、管道等),一旦某个描述符就绪(通常指有数据可读或可写),便能够通知程序进行相应的读写操作的技术。

select和epoll是两种常见的I/O多路复用机制,它们的工作机制和性能差异如下:

一、select的工作机制

基本原理:

使用方法:

性能瓶颈:

二、epoll的工作机制

基本原理:

核心API:

工作流程:

性能优势:

三、select与epoll的性能比较

时间复杂度:

文件描述符数量限制:

系统资源消耗:

可扩展性:

面试官:epoll底层的数据结构什么,请结合epoll的底层结构说说epoll的工作机制。以及为什么epoll如此高效?

epoll底层主要使用了红黑树和就绪队列(双链表)这两种数据结构,其高效性主要源于以下几个方面:

一、epoll底层数据结构

红黑树:

就绪队列(双链表):

再聊聊epoll的具体实现机制。

如下图所示:

当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。

struct eventpoll{
    ....
    
    struct rb_root  rbr;
    
    struct list_head rdlist;
    ....
};

每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,重复添加的事件就可以通过红黑树log(n)的查找复杂度高效的识别出来(其中n为树的高度)。

所有添加到epoll中的事件都会与设备(如网卡)驱动程序建立回调关系。这个回调方法会将就绪的事件添加到rdlist双链表(就绪链表)中。

每一个事件对应一个epitem结构体,如下所示。

struct epitem{
    struct rb_node  rbn;//红黑树节点
    struct list_head    rdllink;//双向链表节点
    struct epoll_filefd  ffd;  //事件句柄信息
    struct eventpoll *ep;    //指向其所属的eventpoll对象
    struct epoll_event event; //期待发生的事件类型
}

当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的就绪队列rdlist中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户进程,最后用户进程根据事件执行相应的读写处理。

二、epoll高效性原因

事件通知机制:

避免数据拷贝:

支持两种工作模式:

动态管理文件描述符:

面试官:你刚刚有提到epoll的两种触发模式,请解释一下边缘触发(ET)和水平触发(LT)的区别?

一、边缘触发(ET)与水平触发(LT)的区别

触发机制:

应用程序响应:

适用场景:

以下是一个使用Python的epoll模块来演示ET和LT触发模式的示例:


import os  
import socket  
import select  
import epoll  

# 创建一个TCP/IP套接字  
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  
server_sock.bind(('localhost', 12345))  
server_sock.listen(5)  
server_sock.setblocking(0)  # 设置为非阻塞模式  

# 创建一个epoll实例  
epoller = epoll.epoll()  

# 为服务器套接字注册事件,LT模式  
epoller.register(server_sock.fileno(), epoll.EPOLLIN | epoll.EPOLLET if False else epoll.EPOLLIN)  # 将if False改为True以使用ET模式  

# 客户端连接处理函数  
def handle_connection(client_sock):  
    data = client_sock.recv(1024)  
    if data:  
        print(f"Received: {data.decode()}")  
        client_sock.sendall(data)  # Echo the data back to the client  
    else:  
        # 没有数据表示客户端已经关闭连接  
        client_sock.close()  
        print("Client disconnected.")  

# 主循环  
try:  
    while True:  
        events = epoller.poll(1000)  # 超时时间为1000毫秒  
        for fileno, event in events:  
            if fileno == server_sock.fileno():  
                # 服务器套接字上有新的连接请求  
                client_sock, client_addr = server_sock.accept()  
                client_sock.setblocking(0)  # 设置为非阻塞模式  
                print(f"Accepted connection from {client_addr}")  

                # 为客户端套接字注册事件,这里我们根据之前的条件选择ET或LT模式  
                epoller.register(client_sock.fileno(), epoll.EPOLLIN | epoll.EPOLLET if False else epoll.EPOLLIN)  
            else:  
                # 客户端套接字上有数据可读或连接关闭等事件  
                client_sock = socket.socket(fileno=fileno)  # 从文件描述符恢复套接字对象  

                # 根据ET或LT模式的不同,处理数据的方式也会有所不同  
                if False:  # 如果为True,则表示使用ET模式  
                    # ET模式下,需要确保一次读取所有可用数据  
                    while True:  
                        try:  
                            data = client_sock.recv(1024)  
                            if not data:  
                                break  # 没有数据表示连接已关闭  
                            print(f"Received (ET): {data.decode()}")  
                            client_sock.sendall(data)  # Echo the data back to the client  
                        except BlockingIOError:  
                            # 在ET模式下,如果没有更多数据可读,会抛出BlockingIOError异常  
                            break  
                else:  
                    # LT模式下,每次有数据可读时都会触发事件  
                    handle_connection(client_sock)  

                # 如果客户端连接已关闭,则注销该套接字  
                if not client_sock.fileno() in [fd for fd, _ in epoller.poll(0)]:  # 使用非阻塞poll检查连接是否仍然活跃  
                    epoller.unregister(client_sock.fileno())  
                    client_sock.close()  
except KeyboardInterrupt:  
    print("Server stopped by user.")  
finally:  
    epoller.close()  
    server_sock.close()

代码中通过epoll.EPOLLIN | epoll.EPOLLET来选择ET模式,而仅通过epoll.EPOLLIN来选择LT模式。你可以通过修改条件(即if False改为if True)来切换模式。

由于epoll模块是Linux特有的,因此这段代码只能在Linux环境下运行。

为了简化示例,错误处理和资源清理可能不是最完善的。在实际应用中,你需要更仔细地处理这些情况。

使用epoll.poll(0)来检查连接是否仍然活跃是一种非阻塞的检查方式,但它可能不是最优雅或最高效的方法。在实际应用中,你可能需要设计更复杂的逻辑来处理连接的状态。面试官:说说看你知道的IO模型有哪些?

来源:程序员阿沛内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯