IO模型详解(select、poll、epoll伪代码剖析)

IO模型

操作系统,把内存分成了两个区域:

  • 内核空间,这个内存空间只有内核程序可以访问;
  • 用户空间,这个内存空间专门给应用程序使用;

用户空间的程序不能直接访问内核空间。当想要执行 IO 操作时,由于没有执行这些操作的权限,只能发起系统调用请求操作系统帮忙完成。因此,用户进程想要执行 IO 操作的话,必须通过 系统调用 来间接访问内核空间。

当应用程序发起 I/O 调用后,会经历两个步骤

  1. 内核等待 I/O 设备准备好数据
  2. 内核将数据从内核空间拷贝到用户空间

BIO(Blocking I/O)

在同步阻塞IO模型中,应用程序发起 read 调用后,线程会一直阻塞,直到内核把数据拷贝到用户空间。在阻塞 I/O 模型中,每个连接都需要一个线程来处理。因此,对于大量并发连接的场景,阻塞 I/O 模型的性能较差。

伪代码如下:

listenfd = socket();   // 打开一个网络通信套接字
bind(listenfd);         // 绑定
listen(listenfd);       // 监听

while(true) {
    buf = new buf[1024]; // 读取数据容器
    connfd = accept(listenfd); // 阻塞等待建立连接
    new Thread() {
        int n = read(connfd, buf);  // 检测 connfd 是否可读
        if (n != -1) {
            doSomeThing(buf);  // 处理数据  
        }
    }
    close(connfd);     // 关闭连接
}

int n = read(connfd, buf)这里会发生阻塞,执行以上所提到的两个步骤。
在这里插入图片描述

NIO(Non-blocking I/O)

同步非阻塞 IO 模型中,应用程序发起 read 调用后,会先询问内核数据是否已准备好,若没有准备好,可以立即返回-1,线程可以去执行其他的任务,这种模型允许一个线程去处理多个连接;应用程序通过轮询的方式来确认数据是否准备好,但是十分消耗CPU资源的。

arr = new Arr[];          // 创建连接数组
listenfd = socket();      // 打开一个网络通信套接字
bind(listenfd);           // 绑定
listen(listenfd);         // 监听

while(true) {
    connfd = accept(listenfd); // 阻塞等待建立连接
    arr.add(connfd);           // 添加连接到数组
}

// 异步线程检测连接是否可读
new Thread() {
    for (connfd : arr) {
        buf = new buf[1024];  // 读取数据容器
        // 非阻塞 read,最重要的是提供了我们在一个线程内管理多个文件描述符的能力
        int n = read(connfd, buf);  // 检测 connfd 是否可读
        if (n != -1) {
            newThreadDeal(buf);   // 创建新线程处理
            close(connfd);        // 关闭连接
            arr.remove(connfd);   // 移除已处理的连接
        }
    }
}

// 处理数据的函数
newThreadDeal(buf) {
    doSomeThing(buf);  // 处理数据
}

逻辑如图所示:

在这里插入图片描述

IO多路复用模型

IO多路复用是指使用操作系统提供的多路复用功能(如selectpollepoll等),使得单个线程可以同时处理多个IO事件。当某个连接的数据准备好时,操作系统会通知应用程序。

select

创建一个文件描述符集fd_set(基于位图实现,一个fd对应一个bit),使得一个线程可以监控多个文件描述符的状态变化(遍历整个集合),select监控的文件描述符有数量的限制(1024个)。跟NIO的区别在于NIO是用户线程去遍历,select是内核线程去遍历(可以减少大量的系统调用)

select伪代码示例:

arr = new Arr[];                // 创建连接数组
listenfd = socket();            // 打开一个网络通信套接字
bind(listenfd);                 // 绑定
listen(listenfd);               // 监听

while (true) {
   
   
    connfd = accept(listenfd);  // 阻塞等待建立连接
    arr.add(connfd);            // 添加连接到数组
}

// 异步线程检测,通过 select 判断是否有连接可读
new Thread() {
   
   
    while (select<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liubo666_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值