I/O 多路复用使用背景梳理

内容总结自 《UNIX 环境高级编程》 高级 I/O 部分

1. 问题背景描述

在终端使用 telnet 命令连接到一个 TCP server,假设 server 会返回你在终端输入的内容。

那么现在终端的这个 telnet 进程,会从两个文件描述符读取内容,一个是标准输入,一个是网络连接(server 返回的数据),注意只有一个进程。

打开一个文件时我们可以以阻塞或非阻塞方式打开,假设此时我们使用阻塞的方式打开一个文件,并使用 read 来读取数据,再假设此时 read 阻塞在标准输入的文件描述符上,且标准输入无数据,而网络连接有数据达到时,由于程序此时阻塞在标准输入,因此无法处理到达的网络数据。

如果需要输入源有多个,并且是阻塞的,那么先考虑以多进程(父子进程)的方式实现;每个进程处理一个输入源;虽然看似解决了阻塞的问题,但引出了结束进程的复杂性,比如父进程结束时通知子进程,子进程结束时需要通知父进程。

再尝试以多线程的方式实现;数据从标准输入读取再从网络连接发送出去,不可避免存在数据竞态问题,因此需要做线程同步,又引入了较高的复杂性。

再考虑使用非阻塞的方式实现,并且仅用一个进程;那么当标准输入无数据时,read 会立即返回,因此可以继续判断网络连接是否有数据,如果此时网络连接也没数据,可以立即判断标准输入,但此时会耗费极高的 CPU,因此在所有文件描述符都没有数据的情况下,可以让进程睡眠一段时间进而减少 CPU 消耗,但合适的睡眠时间很难确定。

以上的实现方案都基于同步方式,同步阻塞,同步非阻塞,再来看看异步的方式,即异步 I/O 技术。进程告诉内核(fnctl、ioctl):当描述符准备好 I/O 时,通过信号来通知进程。

但异步 I/O 的使用也有困难,比如不同系统提供的异步 I/O API 不一致,即可移植性差;异步 I/O 在使用上受限,比如 System V 提供 SIGPOLL 来支持异步 I/O,但只有描述符引用 STREAMS 设备(什么是 STREAMS 设备,有哪些?)时才可用,BSD 提供了 SIGIO 信号,但只有描述符引用终端设备或网络时才可用;另外,一个进程只能使用一个信号来处理异步 I/O,如果多个描述符共用一个信号,那么信号发生时进程无法判断是哪一个描述符准备好了。

由于上述方案的种种缺点,引出了 I/O 多路复用技术(I/O multiplexing)。复用是指在同一个进程(线程)中,处理多路 I/O,多路指多个文件描述符。它的思想是,收集进程感兴趣的全部描述符,然后调用一个函数,当这些描述符中的一个或多个准备好 I/O 时,函数返回并告知进程是哪些描述符准备好了。此时进程只需要去这些准备好的描述符上操作即可。

2、常用的 I/O 多路复用模型

2.1 select
2.2 poll
2.3 epoll

只能在 Linux 平台使用

  • 为什么性能优越?与 select 的区别是什么?
    1、减少了不必要的用户态到内核态的数据拷贝
    2、不用轮询所监听的套接字,减少了无谓的 CPU 消耗

  • 水平触发模式(edge-triggered)
    读缓冲区从空到非空,会一直触发可读事件,直到又变为非空。
    写缓冲区从满到非满,会一直触发可写事件,直到又变为满。

  • 边缘触发模式(level-triggered)
    读缓冲区从空到非空,或者写缓冲区从满变为非满时会且仅会触发一次事件。

  • 哪些开源项目使用 epoll
    nginx,libevent,redis 等

  • 使用 epoll 的限制有什么?
    只能在单进程(线程)中使用,必须使用非阻塞 I/O。

2.4 kqueue

只能在 BSD 平台使用,例如 MacOS

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值