IO多路复用

I/O多路复用

先了解几个基本概念。

基本概念

同步与异步

同步:发起一个调用后,被调用者未处理完请求之前,调用不返回。

异步:发起一个调用后,被调用者立刻回应表示已接收到请求,但被调用者没有返回结果,此时调用者可以处理其它请求。

同步和异步最大的区别是:异步的调用者不需要等待结果,被调用者会通过回调等机制来通知调用者返回结果。

阻塞与非阻塞

阻塞:发起一个请求,调用者一直在等待返回结果,当前线程被挂起,无法干其它事。

非阻塞:发起一个请求,调用者不用一直等待结果,可以先去干其它事。

通过例子理解同步与异步、阻塞与非阻塞

1.老张把水壶放到火上,立等水开。(同步阻塞)

2.老张把水壶放到火上,去看电视,时不时去查看水开了没有。(同步非阻塞)

3.老张把响水壶放到火上,立等水开。(异步阻塞)

4.老张把响水壶放到火上,去看电视,等响了再去拿壶。(异步非阻塞)

BIO(Blocking IO)模型

同步阻塞IO模式,数据的读取写入必须阻塞在一个线程内等待完成(一个客户端连接对应一个处理线程)。

在这里插入图片描述

Acceptor线程监听客户端的连接,在while(true)循环中,服务端调用accept()方法监听请求,一旦接收到一个连接请求,就建立通信套接字并进行读写操作。此时,不再接收其它客户端的连接请求,只能等待当前连接的客户端的操作执行完成,不过可以通过多线程来支持多个客户端的连接,如上图所示。

多线程处理也有弊端:有大量客户端连接请求时,线程数急剧膨胀,导致堆栈溢出、创建新线程失败等问题,最终导致进程宕机。

且线程是宝贵的资源:

1.线程的创建和销毁的成本很高。

2.线程本身占用较大的内存,像Java的线程栈,一般至少分配512k~1M的空间,如果系统中线程数过千,恐怕整个JVM内存都会被吃掉一半。

3.线程的切换成本很高。在切换线程时,需要保留线程的上下文,如果线程数过高,会导致系统负载升高,CPU sy使用率升高。

4.容易造成锯齿状的系统负载。一旦线程数高但外部网络环境不稳定,很容易造成大量请求的结果同时返回,激活大量阻塞线程,从而使系统负载压力过大。

为了解决上述问题,提出了伪异步IO模型:后端通过一个线程池来处理多个客户端的请求,线程池设置最大线程数,所以即使客户端请求远大于线程池中线程的个数,也可以通过线程池灵活调配线程资源,防止由于海量并发导致线程耗尽。

在这里插入图片描述

BIO适用于连接数小且固定的架构,这种方式对服务器资源要求比较高,但程序简单易于理解。

NIO(Non Blocking IO)

同步非阻塞,服务器实现模式为一个线程可以处理多个连接,客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有IO请求就处理。

NIO有三大核心组件:

1.Channel(通道)

2.Buffer(缓冲区)

3.Selector(选择器)

在这里插入图片描述

channel类似于流,每个channel对应一个buffer,buffer底层是个数组。

channel会注册到selector上,由selector根据channel读写事件的发生将其交由某个空闲的线程处理(类似于上帝视角)。

selector可以对应一个或多个线程。

NIO中,所有的数据都是用缓冲区处理的。读数据时,直接读到缓冲区;写数据时,写入缓存中。

NIO中的IO都是从channel开始的:

1.读取数据:创建一个缓冲区,请求通道读取数据

2.写入数据:创建一个缓冲区,要求通道写入数据

在这里插入图片描述

NIO适用于连接数多且连接比较短(清操作)的架构,如弹幕系统。

AIO(Asynchronous IO)

异步非阻塞,由操作系统完成后回调通知服务端程序启动线程去处理,适用于连接数多且连接时间长的应用。

用户空间/内核空间

操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核,保证内核的安全,操作系统将虚拟存储空间分为两部分,一部分为内核空间,一部分供各个进程使用,称为用户空间。

文件描述符fd

File description用于表述指向文件的引用的抽象化概念,形式上是一个非负整数,实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。

当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。

什么是IO多路复用?

1.IO多路复用是一种同步IO模型,一个线程可以监视多个文件句柄

(文件句柄:就是给一个文件、设备、套接字或管道的一个名字,以便记住正在处理的名字,隐藏缓存等的复杂性。)

2.一旦某个文件句柄就绪,就能通知应用程序进行相应的读写操作

3.没有文件句柄就绪就会阻塞应用程序,交出CPU

多路是指网络连接,复用是指同一个线程。

服务器采用单线程通过select/poll/epoll等系统调用获取fd列表,遍历有事件的fd进行accept/recv/send,使其能支持更多的并发连接请求。

IO多路复用的三种实现

select

poll

epoll

select

它仅仅知道有IO事件发生了,但不知道是哪几个流,所以只能无差别轮询所有流,找出能读出数据,或者写入数据的流。select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

select调用过程

在这里插入图片描述

1.使用copy_from_user从用户空间拷贝fd_set到内核空间

2.注册回调函数_pollwait

3.遍历所有fd,调用其对应的poll方法

4._pollwait的主要工作是把当前进程挂到设备的等待队列中,在设备收到一条消息(网络设备)或填写完文件数据(磁盘设备)后,会唤醒设备等待队列上睡眠的进程,这时当前进程就被唤醒了

5.poll方法会返回一个描写读写操作是否就绪的mask掩码,根据这个mask掩码给fd_set赋值

6.如果遍历完所有的fd,还没有返回一个可读写的mask掩码,就会使调用select的进程进入睡眠。当设备发生资源可读写后,会唤醒等待队列上睡眠的进程。如果超过一定时间,还是没有唤醒(就是说没有发生资源可读写),调用select的进程会重新被唤醒获得CPU,重新遍历fd,判断有没有就绪的fd

7.把fd_set从内核空间拷贝到用户空间

select的缺点

1.单个进程所打开的fd是有限制的

2.每次调用select,都需要把fd_set从用户态拷贝到内核态,这个开销在fd_set很大时会很大

3.对socket扫描时是线性扫描,采用轮询的方法,效率较低(高并发)。

poll

poll本质上和select没有区别,也是将用户传入的数组拷贝到内核空间,然后轮询,但是它没有最大连接数的限制,因为它是基于链表存储的

poll的缺点和select的第2,3个缺点是一样的

epoll

epoll可以理解为event poll,不同于无差别轮询,epoll会把哪个流发生了怎样的IO事件通知我们。所以epoll实际上是**事件驱动(每个事件关联上fd)**的,此时我们对这些流的操作都是有意义的。(复杂度降到了O(1))

每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,这样,重复添加的事件就可以通过红黑树高效地识别出来。

而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,当相应的事件发生时就会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。

当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,就把发生的事件复制到用户态,同时将事件数量返回给用户。

通过红黑树和双链表的数据结构,结合回调机制,造就了epoll的高效。

epoll的用法

1.epoll_create():返回一个句柄,之后所有的使用都依靠这个句柄来标识

2.epoll_ctl():通过此调用向epoll对象中添加、删除、修改感兴趣的事件,返回0表示成功,返回-1表示失败。

3.epoll_wait():此调用收集在epoll监控中已经发生的事件。

epoll的优点

1.没有最大并发连接数的限制,能打开的fd的上限远大于1024

2.效率提升。只有活跃可用的fd才会调用callback函数,就不用轮询了

3.内存拷贝开销减少,利用mmap()文件映射内存加速与内核空间的消息传递

epoll的缺点

只能在linux下工作

epoll LT与ET模式的区别

EPOLLLT(水平触发):只要这个fd还有数据可读,每次epoll_wait都会返回它的事件,提醒用户操作

EPOLLET(边缘触发):它只会提示一次,无论fd中是否还有数据可读,直到下次再有数据流入之前都不会再提示了。所以在ET模式下,read一个fd的时候一定要把它的buffer读完

select、poll、epoll的区别


EPOLLLT(水平触发):只要这个fd还有数据可读,每次epoll_wait都会返回它的事件,提醒用户操作

EPOLLET(边缘触发):它只会提示一次,无论fd中是否还有数据可读,直到下次再有数据流入之前都不会再提示了。所以在ET模式下,read一个fd的时候一定要把它的buffer读完

select、poll、epoll的区别

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值