网络编程之IO多路复用

Posted Chris_166

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程之IO多路复用相关的知识,希望对你有一定的参考价值。

目录

一. 同步与阻塞

1.1 同步阻塞

1.2 同步非阻塞

1.3 异步阻塞

1.4 异步非阻塞

1.5 I/O多路

二.多路复用的技术

2.1 UNIX I/O Models

2.1.1 blocking I/O

2.1.2 nonblocking I/O

2.1.3 I/O Multiplexing Model

2.1.4 SIGIO

2.1.5 asynchronous I/O

2.2 IO多路复用

2.2.1 从同步阻塞到同步非阻塞

2.2.2 select

2.2.3 poll

2.2.4 epoll

REF


一. 同步与阻塞

同步是针对调用者的操作行为来说的,阻塞是针对这个行为所使用的接口来说的。

例如你是某场竞赛的主考官,需要监考考生A、B、C、D、E五位考生现场答题。你的操作就是走到每位考生面前去收试卷,而每位考生的答题情况(例如是否答完试卷)是不同的,有的可能在你走到的时候很快就交卷了,有的可能需要很长时间才能完成。

1.1 同步阻塞

到了收卷的时间,你要依次去收A,B,C,D,E考生的试卷。假说收到C考生的时候,他还未能答完试卷且他想答完题了再交给你,你又必须得等C交卷了才能去收D和E考生的试卷,那么这时候就是同步阻塞的。

对应Java中BIO(Block IO)。

1.2 同步非阻塞

到了收卷的时间,你要依次去收A,B,C,D,E考生的试卷。假说收到C考生的时候,他还未能答完试卷,你跳过C直接去收 D、E 的试卷,那么这时候就是同步非阻塞的。

1.3 异步阻塞

暂时没听说有这种场景。

1.4 异步非阻塞

你收卷的时候不用刻意去等某个学生交卷,学生交卷又迅速。

1.5 I/O多路

select/poll:考生做完了试卷,大喊了一声“我要交卷”,但是你不知道谁喊的,这时候你需要一个个地去询问,这个就是select/poll。

epoll:考生做完了试卷,大喊了一声“我要交卷”而且还举手了,你直接去收卷。

二.多路复用的技术

2.1 UNIX I/O Models

https://masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch06lev1sec2.html

  • blocking I/O:阻塞式I/O -- BIO

  • nonblocking I/O:非阻塞式I/O  -- NIO,AIO(AIO是在BIO的包里)

  • I/O multiplexing (select and poll):I/O复用

  • signal driven I/O (SIGIO):信号驱动式I/O

  • asynchronous I/O (the POSIX aio_functions):异步I/O  --AIO
     

UNIX I/O Models阻塞/非阻塞对应JAVA IO说明
blocking I/O阻塞BIO
nonblocking I/O非阻塞NIO
I/O multiplexing阻塞AIO和NIO的底层都是用epoll,这是JDK又进行了一层封装使之成为了非阻塞式的。
asynchronous I/O非阻塞AIO
SIGIO非阻塞

在介绍I/O models前,先对Socket的读取操作做简单说明,通常来说包括如下两个操作:

1. wait for data:等待数据从网络中到达,当数据到达后就会将器复制到内核中的某个缓冲区;

2. copy data from kernel to user:把数据从内核缓冲区复制到应用进程的缓冲区,这个过程虽然阻塞的,但是这个内存的拷贝是及其快的

2.1.1 blocking I/O

 

 由上图可知,应用进程从调用recvfrom()到它返回的这整段时间内都是阻塞的,主要阻塞在wait for data这个过程。当应用进程有返回值(return OK)的时候,应用进程已经读完数据了。

当然也可能因系统调用被信号终端导致调用发生错误。

2.1.2 nonblocking I/O

由上图可知,应用进程调用recvfrom的时候没有数据可返回则立即返回EWOULDBLOCK,而不是一直等着。

2.1.3 I/O Multiplexing Model

IO多路复用也是阻塞式的,应用进程阻塞在select调用,等待数据报套接字变为可读后,应用进程才会立即调用recvfrom读取数据。

从这里看I/O多路复用还不如BIO,因为它比BIO多一次select的系统调用,BIO只有一次recvfrom的系统调用。从后面的IO多路复用的进一步描述可知select的优势在于可以等待多个fd描述符就绪。

2.1.4 SIGIO

 非阻塞式IO。应用进程通过sigaction的系统调用告知内核在数据就绪发送SIGIO信号来通知下,这个sigaction调用完就立马返回了。等内核数据就绪后,内核在通过信号告知应用进程来取数据。

2.1.5 asynchronous I/O

AIO和SIGIO一样也是非阻塞的,与SIGIO的差别在于:SIGIO是由内核通知应用进程什么时候去启动recvfrom这个IO操作,而AIO是由内核通知应用进程I/O操作何时完成(即这时候数据已经从内核拷贝到了用户态),可以仔细对比下两张图。

2.2 IO多路复用

以TCP socket通信为例来介绍从"BIO"到"I/O多路复用"的引进过程。

2.2.1 从同步阻塞到同步非阻塞

如上示例为"单线程+BIO"。由于accept()和read()都会阻塞,所以当client1在与server交互的过程中,client2就会被阻塞住。例如client1在connect()连接握手耗时或者是client1一直在write发数据到server,这时候client2就会一直阻塞等待。

Q:那么是否可以通过多线程来解决多client被阻塞的问题呢?

A:可以但不完全可以。因为多线程可能会存在线程浪费,线程调度也是个麻烦事。例如来一个client连接就建立一个线程,如果有1000个client就得创建1000个线程,但是实际上可能只有两三个client和server端在通信,这时候就会浪费很多线程资源。

 那么我们再来看看,不阻塞会怎样呢?

 不阻塞accept(),client1调用connect()的时候client2也可以调用,当他们connect()成功了就将socket fd放到fd_list集合里,后面再去轮询这个list集合,通过系统调用去读每个fd看是否有数据到达(在上面“UNIX I/O Models”中介绍过socket数据读取的两个主要过程)。

这种不阻塞的方式虽然能解决“单个 socket 阻塞影响其他socket的问题”,但是不断的遍历,不断的进行系统调用是会有一定的开销的,特别是在没有数据到来却一直在进行系统调用的时候,这种方式的缺点表现地尤为明显。

如何优雅的解决呢?这时候就引入了I/O多路复用。

2.2.2 select

(TODO)

2.2.3 poll

(TODO)

2.2.4 epoll

(TODO)

REF

1.《UNIX Network Programming Volume 1, Third Edition: The Sockets Networking API》

I/O Models章节

以上是关于网络编程之IO多路复用的主要内容,如果未能解决你的问题,请参考以下文章

python 网络编程 IO多路复用之epoll

IO模型以及多路复用基本原理

socket编程:多路复用I/O服务端客户端之poll

IO多路复用之selectpollepoll

IO多路复用之select总结

unix网络编程——I/O多路复用之epoll