本文转载自微信公众号「潜行前行」,作者cscw 。转载本文请联系潜行前行公众号。
前言
网络I/O,可以理解为网络上的数据流。通常我们会基于socket与远端建立一条TCP或者UDP通道,然后进行读写。单个socket时,使用一个线程即可高效处理;然而如果是10K个socket连接,或者更多,我们如何做到高性能处理?
基本概念介绍
所有系统都有调度进程的能力,它可以挂起一个当前正在运行的进程,并恢复之前挂起的进程
运行中的进程,有时会等待其他事件的执行完成,比如等待锁,请求I/O的读写;进程在等待过程会被系统自动执行阻塞,此时进程不占用CPU
在Linux,文件描述符是一个用于表述指向文件引用的抽象化概念,它是一个非负整数。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符
Linux进程运行中可以接受来自系统或者进程的信号值,然后根据信号值去运行相应捕捉函数;信号相当于是硬件中断的软件模拟
在零拷贝机制篇章已介绍过 用户空间和内核空间和缓冲区,这里就省略了
网络IO的读写过程
linux五种网络IO模型
阻塞式I/O (blocking IO)
- ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socket_t *fromlen);
非阻塞式I/O (nonblocking IO)
多路复用I/O (IO multiplexing)
信号驱动式I/O (SIGIO)
异步IO (POSIX的aio_系列函数)
多路复用IO深入理解一波
select
- int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
1)使用copy_from_user从用户空间拷贝fd_set到内核空间
2)注册回调函数__pollwait
3)遍历所有fd,调用其对应的poll方法(对于socket,这个poll方法是sock_poll,sock_poll根据情况会调用到tcp_poll,udp_poll或者datagram_poll)
4)以tcp_poll为例,其核心实现就是__pollwait,也就是上面注册的回调函数
5)__pollwait的主要工作就是把current(当前进程)挂到设备的等待队列中,不同的设备有不同的等待队列,对于tcp_poll来说,其等待队列是sk->sk_sleep(注意把进程挂到等待队列中并不代表进程已经睡眠了)。在设备收到一条消息(网络设备)或填写完文件数据(磁盘设备)后,会唤醒设备等待队列上睡眠的进程,这时current便被唤醒了
6)poll方法返回时会返回一个描述读写操作是否就绪的mask掩码,根据这个mask掩码给fd_set赋值
7)如果遍历完所有的fd,还没有返回一个可读写的mask掩码,则会调用schedule_timeout是调用select的进程(也就是current)进入睡眠
8) 当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的进程。如果超过一定的超时时间(timeout指定),还是没人唤醒,则调用select的进程会重新被唤醒获得CPU,进而重新遍历fd,判断有没有就绪的fd
9)把fd_set从内核空间拷贝到用户空间
select的缺点
每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
select支持的文件描述符数量太小了,默认是1024
epoll
- int epoll_create(int size);
- int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
epoll的两种触发模式
epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式(只支持no-block socket)
epoll相比select的优点
解决select三个缺点
epoll的高性能
关于epoll的IO模型是同步异步的疑问
概念定义
异步IO的概念是要求无阻塞I/O调用。前面有介绍到I/O操作分两阶段:R1等待数据准备好。R2从内核到进程拷贝数据。虽然epoll在2.6内核之后采用mmap机制,使得其在R2阶段不需要复制,但是它在R1还是阻塞的。因此归类到同步IO
Reactor模型
Reactor的中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程/进程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪,多路复用器返回,并将事先注册的相应I/O事件分发到对应的处理器中
相关概念介绍:
Reactor的一般流程
单线程 + Reactor
多线程 + Reactor
多线程 + 多个Reactor
Proactor模型的一般流程
1)应用程序在事件分离器注册读完成事件和读完成事件处理器,并向系统发出异步读请求
2)事件分离器等待读事件的完成
3)在分离器等待过程中,系统利用并行的内核线程执行实际的读操作,并将数据复制进程缓冲区,最后通知事件分离器读完成到来
4)事件分离器监听到读完成事件,激活读完成事件的处理器
5)读完成事件处理器直接处理用户进程缓冲区中的数据
Proactor和Reactor的区别
Proactor是基于异步I/O的概念,而Reactor一般则是基于多路复用I/O的概念
Proactor不需要把数据从内核复制到用户空间,这步由系统完成
欢迎指正文中错误
参考文章
聊聊Linux 五种IO模型[1]
网络io模型[2]
网络IO[3]
5种网络IO模型[4]
epoll原理详解及epoll反应堆模型[5]
参考资料
[1]聊聊Linux 五种IO模型: https://www.jianshu.com/p/486b0965c296[2]网络io模型: https://www.jianshu.com/p/a95bcb116765[3]网络IO: https://www.cnblogs.com/hesper/p/11547263.html[4]5种网络IO模型: https://www.cnblogs.com/findumars/p/6361627.html[5]epoll原理详解及epoll反应堆模型: https://blog.csdn.net/daaikuaichuan/article/details/83862311
一、简介 本设计为硬币图像识别统计装置通过数码相机获取平铺无重叠堆积的硬币的...
git工作区,暂存区,版本库之间的关系: 我们建立的项目文件夹就是工作区,在初...
本文实例讲述了jsp中page指令用法。分享给大家供大家参考。具体如下: 一、JSP ...
今日国内领先的智能数据服务运营商觉非科技完成近亿元A轮融资。本轮融资由和高资...
从功能测试、性能测试、界面测试、安全性测试、易用性、兼容性测试、震动测试七...
一、MVC MVC模式的意思是,软件可以分成三个部分。 视图(View):用户界面。 控...
我们知道微软将会在今年给Windows10更换全新设计的UI,让Windows10的界面更加整...
前言 关于Window,你了解多少呢?看看下面这些问题你都能答上来吗。 如果你遇到这...
首先给扑克牌中每张牌设定一个编号,下面算法实现的编号规则如下: u 红桃按照从...
大家好,今天我们来简单的聊一聊缓存问题。什么是缓存呢?它在系统设计中是在一个...