UNIX的网络I/O模型

目录

前言:

网络编程:

linux内核中文件的读写操作

1.同步阻塞I/O模型:sync blocking I/O 简称BIO

BIO通信模型图

概念:

缺点:

伪异步I/O通信模型

优点:

缺点:

2.非阻塞-同步I/O模型:nonblocking sync I/O

概念:

缺点:

3.I/O多路复用(非阻塞)-同步I/O模型:I/O multiplexing

概念:

I/O多路复用通信模型图:

NIO服务端通信序列图:

NIO客户端通信序列图:

触发方式:

优点:

适用场景:

举例:

selector类型:

select调用:

poll调用:

epoll调用:

4.信号驱动I/O模型:SIGIO

5.异步I/O模型:POSIX定义的异步IO函数


前言:

网络编程:

  1. 网络编程的基本模型是Client/Server模型,也就是两个进程间的相互通信,其中服务端提供位置信息(绑定的IP地址+端口),客户端通过连接操作向服务端监听的位置(服务端绑定的IP地址+端口)发起连接请求,通过三次握手建立连接,连接建立后,双方就可以通过网络套接字(socket)进行通信了。
  2. 传统的同步阻塞模型中,ServerSocket负责绑定ip地址和端口,Socket负责发起连接操作。连接建立后,双方通过输入和输出流进行同步阻塞式通信。

linux内核中文件的读写操作

  1. linux内核将所有的外部设备都看做一个文件来操作,对一个文件的读写操作会调用内核提供的系统命令,返回一个文件描述符(file discriptor,简称fd),对socket的读写会返回一个socket 文件描述符(socketfd)。文件描述符是一个数字,它指向内核中的一个结构体,这个结构体包含了文件路径等信息。
  2. 默认情况下,所有的文件操作都是阻塞的。
  3. 一次⽹络I/O的读操作会包含下面这两个阶段:
    1. 等待数据准备就绪
    2. 数据从内核复制到用户空间(用户进程)


1.同步阻塞I/O模型:sync blocking I/O 简称BIO

BIO通信模型图

概念:

用户进程(中的I/O线程)从阻塞的socket中读取数据时,只有当数据包到达该socket的接收缓存区且数据被复制到服务器进程空间的缓存区中 或 发生错误时 才返回,在此期间用户进程(中处理请求的线程)会一直阻塞等待。

缺点:

  1. 同步IO的缺点:当I/O线程处理(客户端发送请求、服务器应答消息)的速度较慢 或 网络传输较慢时,读取输入流一方的通信线程将被长时间阻塞。
  2. 阻塞IO的缺点:对于客户端的每一个请求,服务端都会去创建一个新的I/O线程来处理客户端的请求。当客户端的并发访问量比较高时,服务端需要创建大量的线程来响应请求,当线程的数量膨胀后,服务器的性能会急剧下降甚至宕机。

伪异步I/O通信模型

优点:

由于线程池和等待队列都是有界的,所以无论客户端并发连接数多大,都不会导致线程数量过于膨胀,从而可以避免服务器因线程膨胀而导致的奔溃。

缺点:

底层仍然采用的是同步IO模型,因此同步IO本身的缺点并没有解决。


2.非阻塞-同步I/O模型:nonblocking sync I/O

概念:

用户进程(中的I/O线程)从非阻塞的socket中读取数据时,若该套接字的接收缓存区中没有数据,则内核会直接返回一个错误。

缺点:

一般当socket设为非阻塞状态时,用户进程会轮询内核(eg:循环调用recvfrom函数),直到内核有数据返回为止,因此会导致大量cpu资源被占用。


3.I/O多路复用(非阻塞)-同步I/O模型:I/O multiplexing

概念

I/O多路复用技术通过把多个IO的阻塞复用到同一个 [select | poll | epoll] 系统调用的阻塞上。

即:将BIO模式中 [n个IO同步阻塞调用] 变为 [n个请求阻塞在一个select 调用上,当要访问的资源准备好后,select才发起同步IO操作(每次执行IO的线程可以是同一个线程,故IO线程可以复用多次),因为资源已经准备好了,所以发起的同步IO操作并不会被阻塞]。

这样一来,就不存在阻塞的IO调用了,也就不用为每个请求专门创建一个线程来执行阻塞的IO调用了。所以I/O多路复用模型下,I/O是非阻塞的。

I/O多路复用通信模型图:

NIO服务端通信序列图:

NIO客户端通信序列图:

服务端进程(中处理请求的线程)将每个客户端请求要访问的fd传递给selector,selector触发 [select | poll | epoll] 系统调用,服务端进程(中处理请求的线程)阻塞在selector上。

selector ([select | poll | epoll]系统调用) 可以帮我们侦测多个fd是否就绪。当fd就绪后,服务端进程(中处理请求的线程)将I/O操作任务提交到一个专门用于处理IO请求到线程池中

I/O多路复用可以看作是Reactor线程模型的一种具体实现。

触发方式:

  • 水平触发:若就绪的fd未被用户进程处理,则该fd在下一次查询时依旧会返回。
  • 边缘触发:无论就绪的fd是否被用户进程处理,该fd在下一次查询时将不再返回。

优点:

与传统的多线程/多进程模型相比,I/O多路复用的最大优势是系统开销小,系统不需要创建额外的线程,也不需要管理这些线程的运行。

适用场景:

  • 服务器需要同时处理多个处于监听状态或多个连接状态的套接字。

举例:

java中NIO

selector类型:

select调用:

  • 概念:多路复用器(selector)由select函数实现。
  • 监听机制:
    • 轮询注册的fd,并根据fd的状态做相应的处理:
      • 读就绪状态             ->    读数据 并 删除该读就绪事件
      • 写就绪状态             ->    写数据 并 删除该写就绪事件
      •  接收(accept)就绪状态     ->    注册新的读就绪事件 并 删除该接收就绪事件
      • ...      
    • 获取fd的状态:内核把所有监听的fd的信息整体复制到用户空间。
  • 触发方式:水平触发。
  • 优点:与非阻塞式I/O模式相比,select调用不需要客户端不断地发出请求。
  • 缺点:单个进程可以打开的fd数量有限,默认1024个,如果要修改这个默认值,需要重新编译内核。每次select调用都会线性地扫描所有监听的fd(即:无论fd是否就绪,select都会去检查它的状态),当监听的fd数量比较多时,I/O效率呈线性下降。

poll调用:

  • 概念:多路复用器(selector)由poll函数实现。
  • 监听机制:同select调用。
  • 触发方式:水平触发。
  • 优点: 单个进程可以打开的fd数量不受限制。
  • 缺点:线性地扫描所有监听的fd,当监听的fd数量比较多时,I/O效率呈线性下降。

epoll调用:

  • 概念:selector由一系列epoll_函数实现。

  • 监听机制:

    • 当fd就绪时,fd会立即回调rollback函数,而那些没有就绪的fd则不会回调rollback函数。

    • 获取fd的状态:使用内存映射,不需要把fd的信息从内核复制到用户空间。

  • 触发方式:默认是水平触发,支持边缘触发。
  • 优点:
    1. 单个进程可以打开的fd数量不受限制。(仅受限于操作系统的最大文件句柄数,1g内存的最大文件句柄数为10w左右)
    2. 由于epoll采用的是回调函数的方式,而不是线性扫描的方式,故I/O效率不会随着fd数量的增加而线性下降。
    3. 使用内存映射,避免了内存复制的开销,加速了内核与用户空间的消息传递。
  • 缺点:在连接数少并且连接都十分活跃的情况下,epoll的性能可能比select和poll的性能差,毕竟epoll的通知机制需要很多函数回调。


4.信号驱动I/O模型:SIGIO

  • 概念:开启套接字信号驱动IO功能,并通过sigaction系统调用执行一个信号处理函数(此系统调用立即返回,用户进程继续工作,故该系统调用是非阻塞的),当数据准备好时,内核就为该进程产生一个SIGIO信号,用户进程收到这个信号后,就可以开始进行I/O操作了。

        


5.异步I/O模型:POSIX定义的异步IO函数

  • 概念:调用异步IO函数,让内核在整个I/O操作(包括将数据从内核复制到用户自己的缓冲区)完成后通知用户进程。
  • 信号驱动I/O和异步I/O的区别:
    • 信号驱动I/O:内核通知用户进程何时可以开始一个I/O操作,此时I/O操作还未执行。
    • 异步I/O:内核通知用户进程一个I/O操作在何时已经操作完成。
  • 举例:java中的AIO


        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值