精华内容
下载资源
问答
  • IO多路复用概念介绍

    2021-11-21 19:13:55
  • 概述 当我们要编写一个echo服务器程序的时候,需要对用户从标准输入键入的交互命令做出响应。...针对这种困境的一个解决办法就是I/O多路复用技术。基本思路就是使用select函数,要求内核挂起进程,只有在一个或多个I

    概述

    当我们要编写一个echo服务器程序的时候,需要对用户从标准输入键入的交互命令做出响应。在这种情况下,服务器必须响应两个相互独立的I/O事件:1)网络客户端发起网络连接请求,2)用户在键盘上键入命令行。我们先等待哪个事件呢?没有哪个选择是理想的。如果在acceptor中等待一个连接请求,我们就不能响应输入的命令。类似地,如果在read中等待一个输入命令,我们就不能响应任何连接请求。针对这种困境的一个解决办法就是I/O多路复用技术。基本思路就是使用select函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。 --《UNIX网络编程》

    mysql线程池,就是I/O多路复用的体现。

    超详细的I/O多路复用概念、常用I/O模型、系统调用等介绍

    参考:https://blog.csdn.net/wangxindong11/article/details/78591308
    

    一、I/O多路复用概述

    I/O多路复用,I/O就是指的我们网络I/O,多路指多个TCP连接(或多个Channel),复用指复用一个或少量线程。串起来理解就是很多个网络I/O复用一个或少量的线程来处理这些连接。

    超详细的I/O多路复用概念、常用I/O模型、系统调用等介绍

    多路复用的本质是同步非阻塞I/O,多路复用的优势并不是单个连接处理的更快,而是在于能处理更多的连接。

    I/O编程过程中,需要同时处理多个客户端接入请求时,可以利用多线程或者I/O多路复用技术进行处理。

    I/O多路复用技术通过把多个I/O的阻塞复用到同一个select阻塞上,一个进程监视多个描述符,一旦某个描述符就位, 能够通知程序进行读写操作。因为多路复用本质上是同步I/O,都需要应用程序在读写事件就绪后自己负责读写。

    最大的优势是系统开销小,不需要创建和维护额外线程或进程。

    • 应用场景
    • 服务器需要同时处理多个处于监听状态或者多个连接状态的套接字
    • 需要同时处理多种网络协议的套接字
    • 一个服务器处理多个服务或协议

    目前支持多路复用的系统调用有select, poll, epoll。


    二、几种常用I/O模型

    BIO

    阻塞同步I/O模型,服务器需要监听端口号,客户端通过IP和端口与服务器简历TCP连接,以同步阻塞的方式传输数据。服务端设计一般都是 客户端-线程模型,新来一个客户端连接请求,就新建一个线程处理连接和数据传输

    当客户端连接较多时就会大大消耗服务器的资源,线程数量可能超过最大承受量

    伪异步I/O

    与BIO类似,只是将客户端-线程的模式换成了线程池,可以灵活设置线程池的大小。但这只是对BIO的一种优化手段,并没有解决线程连接的阻塞问题。

    NIO

    同步非阻塞I/O模型,利用selector多路复用器轮询为每一个用户创建连接,这样就不用阻塞用户线程,也不用每个线程忙等待。只使用一个线程轮询I/O事件,比较适合高并发,高负载的网络应用,充分利用系统资源快速处理请求返回响应消息,是和连接较多连接时间I/O任务较短

    AIO

    异步非阻塞,需要操作系统内核线程支持,一个用户线程发起一个请求后就可以继续执行,内核线程执行完系统调用后会根据回调函数完成处理工作。比较适合较多I/O任务较长的场景。


    三、select

    监视多个文件句柄的状态变化,程序会阻塞在select处等待,直到有文件描述符就绪或超时。

    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
    

    可以监听三类文件描述符,writefds(写状态), readfds(读状态), exceptfds(异常状态)。

    我们在select函数中告诉内核需要监听的不同状态的文件描述符以及能接受的超时时间,函数会返回所有状态下就绪的描述符的个数,并且可以通过遍历fdset,来找到就绪的描述符。

    缺陷

    • 每次调用select,都需要把待监控的fd集合从用户态拷贝到内核态,当fd很大时,开销很大。
    • 每次调用select,都需要轮询一遍所有的fd,查看就绪状态。
    • select支持的最大文件描述符数量有限,默认是1024

    四、poll

    与select轮询所有待监听的描述符机制类似,但poll使用pollfd结构表示要监听的描述符。

    int poll(struct pollfd *fds, nfds_t nfds, int timeout)
     
    struct pollfd
    {
     short events;
     short revents;
    };
    

    pollfd结构包括了events(要监听的事件)和revents(实际发生的事件)。而且也需要在函数返回后遍历pollfd来获取就绪的描述符。

    相对于select,poll已不存在最大文件描述符限制。


    五、epoll

    epoll针对以上select和poll的主要缺点做出了改进,

    主要包括三个主要函数,epoll_create, epoll_ctl, epoll_wait。

    • epoll_create:创建epoll句柄,会占用一个fd值,使用完成以后,要关闭。

    int epoll_create(int size)

    • epoll_ctl:提前注册好要监听的事件类型,监听事件(文件可写,可读,挂断,错误)。不用每次都去轮询一遍注册的fd,而只是通过epoll_ctl把所有fd拷贝进内核一次,并为每一个fd指定一个回调函数。
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
    

    当就绪,会调用回调函数,把就绪的文件描述符和事件加入一个就绪链表,并拷贝到用户空间内存,应用程序不用亲自从内核拷贝。类似于在信号中注册所有的发送者和接收者,或者Task中注册所有任务的handler。

    • epoll_wait:监听epoll_ctl中注册的文件描述符和事件,在就绪链表中查看有没有就绪的fd,不用去遍历所有fd。
    • 相当于直接去遍历结果集合,而且百分百命中,不用每次都去重新查找所有的fd,用户索引文件的事件复杂度为O(1)
    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
    

    六、select & poll & epoll比较

    1. 每次调用select都需要把所有要监听的文件描述符拷贝到内核空间一次,fd很大时开销会很大。epoll会在epoll_ctl()中注册,只需要将所有的fd拷贝到内核事件表一次,不用再每次epoll_wait()时重复拷贝
    2. 每次select需要在内核中遍历所有监听的fd,直到设备就绪;epoll通过epoll_ctl注册回调函数,也需要不断调用epoll_wait轮询就绪链表,当fd或者事件就绪时,会调用回调函数,将就绪结果加入到就绪链表。
    3. select能监听的文件描述符数量有限,默认是1024;epoll能支持的fd数量是最大可以打开文件的数目,具体数目可以在/proc/sys/fs/file-max查看
    4. select, poll在函数返回后需要查看所有监听的fd,看哪些就绪,而epoll只返回就绪的描述符,所以应用程序只需要就绪fd的命中率是百分百。

    表面上看epoll的性能最好,但是在连接数少并且链接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。

    select效率低是一位每次都需要轮询,但效率低也是相对的,也可通过良好的设计改善


    七、阻塞、非阻塞

    超详细的I/O多路复用概念、常用I/O模型、系统调用等介绍

    这张图可以看出阻塞式I/O、非阻塞式I/O、I/O复用、信号驱动式I/O他们的第二阶段都相同,也就是都会阻塞到recvfrom调用上面就是图中“发起”的动作。异步式I/O两个阶段都要处理。这里我们重点对比阻塞式I/O(也就是我们常说的传统的BIO)和I/O复用之间的区别。

    阻塞式I/O和I/O复用,两个阶段都阻塞,那区别在哪里呢?

    虽然第一阶段都是阻塞,但是阻塞式I/O如果要接收更多的连接,就必须创建更多的线程。I/O复用模式下在第一个阶段大量的连接统统都可以过来直接注册到Selector复用器上面,同时只要单个或者少量的线程来循环处理这些连接事件就可以了,一旦达到“就绪”的条件,就可以立即执行真正的I/O操作。这就是I/O复用与传统的阻塞式I/O最大的不同。也正是I/O复用的精髓所在。

    从应用进程的角度去理解始终是阻塞的,等待数据和将数据复制到用户进程这两个阶段都是阻塞的。这一点我们从应用程序是可以清楚的得知,比如我们调用一个以I/O复用为基础的NIO应用服务。调用端是一直阻塞等待返回结果的。

    从内核的角度等待Selector上面的网络事件就绪,是阻塞的,如果没有任何一个网络事件就绪则一直等待直到有一个或者多个网络事件就绪。但是从内核的角度考虑,有一点是不阻塞的,就是复制数据,因为内核不用等待,当有就绪条件满足的时候,它直接复制,其余时间在处理别的就绪的条件。这也是大家一直说的非阻塞I/O。实际上是就是指的这个地方的非阻塞。


    总结

    我们通常说的NIO大多数场景下都是基于I/O复用技术的NIO,比如jdk中的NIO,当然Tomcat8以后的NIO也是指的基于I/O复用的NIO。注意,使用NIO != 高性能,当连接数<1000,并发程度不高或者局域网环境下NIO并没有显著的性能优势。如果放到线上环境,网络情况在有时候并不稳定的情况下,这种基于I/O复用技术的NIO的优势就是传统BIO不可同比的了。那么使用select的优势在于我们可以等到网络事件就绪,那么用少量的线程去轮询Selector上面注册的事件,不就绪的不处理,就绪的拿出来立即执行真正的I/O操作。这样我们就能够用极少量的线程去HOLD住大量的连接。

    原文:https://www.toutiao.com/i6731329797806359048/

    展开全文
  • python IO多路复用

    2021-01-28 20:59:02
    多路复用概念:监听多个描述符(文件描述符(windows下暂不支持)、网络描述符)的状态,如果描述符状态改变 则会被内核修改标志位,进而被进程获取进而进行读写操作I/O多路复用是用于提升效率,单个进程可以同时监听多...

    python之IO多路复用

    1. 多路复用概念:

    监听多个描述符(文件描述符(windows下暂不支持)、网络描述符)的状态,如果描述符状态改变 则会被内核修改标志位,进而被进程获取进而进行读写操作

    I/O多路复用是用于提升效率,单个进程可以同时监听多个网络连接IO

    I/O是指Input/Output

    I/O多路复用,通过一种机制,可以监视多个文件描述符,一旦描述符就绪(读就绪和写就绪),能通知程序进行相应的读写操作。

    I/O多路复用避免阻塞在io上,原本为多进程或多线程来接收多个连接的消息变为单进程或单线程保存多个socket的状态后轮询处理.

    2. 多路复用两种触发方式(epool):

    水平触发 level-triggered,epoll对于fd的默认事件模型就是水平触发,即监控到fd可读写时,就会触发并且返回fd,例如fd可读时,但是使用recv没有全部读取完毕,那下次还会将fd触发返回,相对而言,这个更安全一些

    边缘触发 edge-triggered, epoll可以对某个fd进行边缘触发,边缘触发的意思就是每次只要触发一次我就会给你返回一次,即使你处理完成一半,我也不会给你返回了,除非他下次再次发生一个事件。

    使用例子:epoll.register(serversocket.fileno(), select.EPOLLIN | select.EPOLLET)

    3. 阻塞/非阻塞 模式:

    阻塞:

    如果阻塞模式则等待数据

    非阻塞:

    如果非阻塞模式有数据返回数据、无数据直接返回报错

    4. I/O模型:

    同步I/O:

    一问一答 等待数据(阻塞模式)或 不管有没有数据都返回(非阻塞模式)

    异步I/O:

    用户进程问完之后干别的处理结果出来之后告知用户进程

    > SELECT

    select是通过系统调用来监视一组由多个文件描述符组成的数组,通过调用select()返回结果,数组中就绪的文件描述符会被内核标记出来,然后进程就可以获得这些文件描述符,然后进行相应的读写操作

    select的实际执行过程如下:

    select需要提供要监控的数组,然后由用户态拷贝到内核态

    内核态线性循环监控数组,每次都需要遍历整个数组

    内核发现文件描述符状态符合操作结果,将其返回

    所以对于我们监控的socket都要设置为非阻塞的,只有这样才能保证不会被阻塞

    优点:基本各个平台都支持

    缺点:

    每次调用select,都需要把fd集合由用户态拷贝到内核态,在fd多的时候开销会很大

    单个进程能够监控的fd数量存在最大限制,因为其使用的数据结构是数组

    每次select都是线性遍历整个数组,当fd很大的时候,遍历的开销也很大

    使用:

    r, w, e = select.select( rlist, wlist, errlist [,timeout] )

    rlist,wlist和errlist均是waitable object; 都是文件描述符,就是一个整数,或者一个拥有返回文件描述符的函数fileno()的对象。

    rlist: 等待读就绪的文件描述符数组

    wlist: 等待写就绪的文件描述符数组

    errlist: 等待异常的数组

    在linux下这三个列表可以是空列表,但是在windows上不行

    当rlist数组中的文件描述符发生可读时(调用accept或者read函数),则获取文件描述符并添加到r数组中。

    当wlist数组中的文件描述符发生可写时,则获取文件描述符添加到w数组中

    当errlist数组中的文件描述符发生错误时,将会将文件描述符添加到e队列中

    当超时时间没有设置时,如果监听的文件描述符没有任何变化,将会一直阻塞到发生变化为止

    当超时时间设置为1时,如果监听的描述符没有变化,则select会阻塞1秒,之后返回三个空列表。 如果由变化,则直接执行并返回。

    3个list中可接收的参数,可以是Python的file对象,例如sys.stdin,os.open,open返回的对象等等。socket对象将会返回socket.socket(),也可以自定义类,只要由合适的fileno函数即可,前提是真实的文件名描述符

    1 #sync_server

    2 importsocket, select3 importqueue4

    5 server =socket.socket()6 server.bind(('localhost', 9000))7 server.listen(100)8

    9 server.setblocking(False) #不阻塞

    10

    11 #监控的队列

    12 input_list =[server, ]13 output_list =[]14

    15 #返回的消息词典

    16 message_dic ={}17

    18 whileTrue:19 #开始监听

    20 rList,wList,eList =select.select(input_list, output_list, input_list)21 print(rList)22

    23 for r inrList:24 #如果监听到的是server,则说明有新连结

    25 if r isserver:26 conn, addr =server.accept()27 print('建立了新的连接,', addr)28 #监听列表中把新连接进来的添加

    29 input_list.append(conn)30 #建立消息队列

    31 message_dic[conn] =queue.Queue()32 #如果不是server,则说明收到的是连接发来了消息

    33 else:34 try:35 recv_data = r.recv(1024)36 ifrecv_data:37 print('收到消息', recv_data)38 #添加到监听的队列output_list

    39 output_list.append(r)40 #消息队列中增加消息

    41 message_dic[r].put(recv_data)42 exceptConnectionResetError as e:43 print('连接断开')44 if e inoutput_list:45 output_list.remove(r)46 input_list.remove(r)47 delmessage_dic[r]48 continue

    49

    50 for m inwList:51 #消息队列中取消息

    52 send_data =message_dic[m].get()53 m.send(send_data)54 #从队列output_list中移除

    55 output_list.remove(m)

    1 #client

    2 importsocket3

    4 client =socket.socket()5 client.connect(('localhost',9000))6

    7 whileTrue:8 send_date = input('>>')9 client.send(send_date.encode())10 data = client.recv(1024)11 print(data)12

    13 client.close()

    > pool(只适用于Unix/Linux操作系统)

    poll本质上与select基本相同,只不过监控的最大连接数上相较于select没有了限制,因为poll使用的数据结构是链表,而select使用的是数组,数组是要初始化长度大小的,且不能改变

    触发方式:水平触发

    poll原理

    将fd列表,由用户态拷贝到内核态

    内核态遍历,发现fd状态变为就绪后,返回fd列表

    poll状态

    POLLIN   有数据读取

    POLLPRT  有数据紧急读取

    POLLOUT  准备输出:输出不会阻塞

    POLLERR  某些错误情况出现

    POLLHUP   挂起

    POLLNVAL  无效请求:描述无法打开

    缺点:

    每次调用select,都需要把fd集合由用户态拷贝到内核态,在fd多的时候开销会很大

    每次select都是线性遍历整个列表,当fd很大的时候,遍历的开销也很大

    poll方法

    1. register,将要监控的文件描述符注册到poll中,并添加监控的事件类型

    2. unregister,注销文件描述符监控

    3. modify, 修改文件描述符监控事件类型

    4. poll([timeout]),轮训注册监控的文件描述符,返回元祖列表,元祖内容是一个文件描述符及监控类型(POLLIN,POLLOUT等等),如果设置了timeout,则会阻塞timeout秒,然后返回控列表,如果没有设置timeout 微秒,则会阻塞到有返回值为止

    1 #-*- coding: utf-8 -*-

    2

    3 importselect4 importsocket5 importdatetime6

    7 sock =socket.socket()8 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)9 sock.bind(("localhost", 10000))10 sock.listen(5)11 #设置为非阻塞

    12 sock.setblocking(0)13

    14 poll =select.poll()15 poll.register(sock, select.POLLIN)16

    17 connections ={}18

    19 whileTrue:20 #遍历被监控的文件描述符

    21 print(datetime.datetime.now())22 for fd, event in poll.poll(10000):23 if event ==select.POLLIN:24 if fd ==sock.fileno():25 #如果是当前的sock,则接收请求

    26 con, addr =sock.accept()27 poll.register(con.fileno(), select.POLLIN)28 connections[con.fileno()] =con29 else:30 #如果是监听的请求,读取其内容,并设置其为等待写监听

    31 con =connections[fd]32 data = con.recv(1024)33 ifdata:34 print("%s accept %s" %(fd, data))35 poll.modify(fd, select.POLLOUT)36 else:37 con =connections[fd]38 try:39 con.send(b"Hello, %d" %fd)40 print("con >>>", con)41 finally:42 poll.unregister(con)43 connections.pop(fd)44 con.close()

    >epoll(只适用于Unix/Linux操作系统)

    epoll相当于是linux内核支持的方法,而epoll主要是解决select,poll的一些缺点

    1.数组长度限制

    解决方案:fd上限是最大可以打开文件的数目,具体数目可以查看/proc/sys/fs/file-max。一般会和内存有关

    2.需要每次轮询将数组全部拷贝到内核态

    解决方案:每次注册事件的时候,会把fd拷贝到内核态,而不是每次poll的时候拷贝,这样就保证每个fd只需要拷贝一次。

    3.每次遍历都需要列表线性遍历

    解决方案:不再采用遍历的方案,给每个fd指定一个回调函数,fd就绪时,调用回调函数,这个回调函数会把fd加入到就绪的fd列表中,所以epoll只需要遍历就绪的list即可。

    触发方式:边缘触发

    >selector 模块使用举例:

    1 __author__ = 'Administrator'

    2

    3 import selectors #基于select模块实现的IO多路复用

    4 importsocket5 sel = selectors.DefaultSelector() #根据平台选择最佳的IO多路机制,比如linux就会选择epoll windows会选择select

    6

    7 defaccept(sock, mask):8 conn, addr =sock.accept()9 print('accept', conn, 'from', addr, 'mask', mask)10 conn.setblocking(False)11 sel.register(conn, selectors.EVENT_READ, read)12

    13 defread(conn, mask):14 data = conn.recv(1024)15 ifdata:16 print('echoing', repr(data), conn)17 conn.send(data)18 else:19 print('closing', conn)20 sel.unregister()21 conn.close()22

    23 server=socket.socket()24 server.bind(('localhost',9002))25 server.listen(10)26

    27 server.setblocking(False)28 sel.register(server, selectors.EVENT_READ, accept) #注册sock,有连接进来则调用accept

    29

    30 whileTrue:31 events = sel.select() #默认阻塞,有活动连接就返回活动的连接列表

    32 for key,mask inevents:33 #key.data 有活动的绑定函数

    34 #key.fileobj 有活动的文件描述符

    35 callback =key.data36 callback(key.fileobj, mask)

    执行过程:

    e1d432744745e115ff1e594abc5ff6c9.png

    7889730.html

    7889730.html

    展开全文
  • 一、什么是socket? 我们都知道unix(like)世界里,一切皆文件,而文件是...不过话说回来了 ,计算机里有这么的流,我怎么知道要操作哪个流呢?对,就是文件描述符,即通常所说的fd,一个fd就是一个整数,所以,对

    什么是IO多路复用?

    • IO 多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;
    • 一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;
    • 没有文件句柄就绪就会阻塞应用程序,交出CPU。

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

    一、什么是socket?

    我们都知道unix(like)世界里,一切皆文件,而文件是什么呢?文件就是一串二进制流而已,不管socket,还是FIFO、管道、终端,对我们来说,一切都是文件,一切都是流。在信息 交换的过程中,我们都是对这些流进行数据的收发操作,简称为I/O操作(input and output),往流中读出数据,系统调用read,写入数据,系统调用write。不过话说回来了 ,计算机里有这么多的流,我怎么知道要操作哪个流呢?对,就是文件描述符,即通常所说的fd,一个fd就是一个整数,所以,对这个整数的操作,就是对这个文件(流)的操作。我们创建一个socket,通过系统调用会返回一个文件描述符,那么剩下对socket的操作就会转化为对这个描述符的操作。不能不说这又是一种分层和抽象的思想

    二、阻塞?

    什么是程序的阻塞呢?想象这种情形,比如你等快递,但快递一直没来,你会怎么做?有两种方式:

    • 快递没来,我可以先去睡觉,然后快递来了给我打电话叫我去取就行了。
    • 快递没来,我就不停的给快递打电话说:擦,怎么还没来,给老子快点,直到快递来。

    很显然,你无法忍受第二种方式,不仅耽搁自己的时间,也会让快递很想打你。
    而在计算机世界,这两种情形就对应阻塞和非阻塞忙轮询。

    • 非阻塞忙轮询:数据没来,进程就不停的去检测数据,直到数据来。
    • 阻塞:数据没来,啥都不做,直到数据来了,才进行下一步的处理。

    先说说阻塞,因为一个线程只能处理一个套接字的I/O事件,如果想同时处理多个,可以利用非阻塞忙轮询的方式,伪代码如下:

    while true
    {
        for i in stream[]
        {
            if i has data
            read until unavailable
        }
    }

    我们只要把所有流从头到尾查询一遍,就可以处理多个流了,但这样做很不好,因为如果所有的流都没有I/O事件,白白浪费CPU时间片。正如有一位科学家所说, 计算机所有的问题都可以增加一个中间层来解决,同样,为了避免这里cpu的空转,我们不让这个线程亲自去检查流中是否有事件,而是引进了一个代理(一开始是select,后来是poll),这个代理很牛,它可以同时观察许多流的I/O事件,如果没有事件,代理就阻塞,线程就不会挨个挨个去轮询了,伪代码如下:

    while true
    {
        select(streams[]) //这一步死在这里,知道有一个流有I/O事件时,才往下执行
        for i in streams[]
        {
            if i has data
            read until unavailable
        }
    }

    但是依然有个问题,我们从select那里仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

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

    while true
    {
        active_stream[] = epoll_wait(epollfd)
        for i in active_stream[]
        {
            read or write till
        }
    }

    可以看到,select和epoll最大的区别就是:select只是告诉你一定数目的流有事件了,至于哪个流有事件,还得你一个一个地去轮询,而epoll会把发生的事件告诉你,通过发生的事件,就自然而然定位到哪个流了。不能不说epoll跟select相比,是质的飞跃,我觉得这也是一种牺牲空间,换取时间的思想,毕竟现在硬件越来越便宜了。

    三、I/O多路复用

    好了,我们讲了这么多,再来总结一下,到底什么是I/O多路复用。
    先讲一下I/O模型:
    首先,输入操作一般包含两个步骤:

    1. 等待数据准备好(waiting for data to be ready)。对于一个套接口上的操作,这一步骤关系到数据从网络到达,并将其复制到内核的某个缓冲区。
    2. 将数据从内核缓冲区复制到进程缓冲区(copying the data from the kernel to the process)。

    其次了解一下常用的3种I/O模型:

    1、阻塞I/O模型

    最广泛的模型是阻塞I/O模型,默认情况下,所有套接口都是阻塞的。 进程调用recvfrom系统调用,整个过程是阻塞的,直到数据复制到进程缓冲区时才返回(当然,系统调用被中断也会返回)。

    2、非阻塞I/O模型

    当我们把一个套接口设置为非阻塞时,就是在告诉内核,当请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。当数据没有准备好时,内核立即返回EWOULDBLOCK错误,第四次调用系统调用时,数据已经存在,这时将数据复制到进程缓冲区中。这其中有一个操作时轮询(polling)。

    3、I/O复用模型

    此模型用到select和poll函数,这两个函数也会使进程阻塞,select先阻塞,有活动套接字才返回,但是和阻塞I/O不同的是,这两个函数可以同时阻塞多个I/O操作,而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写(就是监听多个socket)。select被调用后,进程会被阻塞,内核监视所有select负责的socket,当有任何一个socket的数据准备好了,select就会返回套接字可读,我们就可以调用recvfrom处理数据。
    正因为阻塞I/O只能阻塞一个I/O操作,而I/O复用模型能够阻塞多个I/O操作,所以才叫做多路复用。

    4、信号驱动I/O模型(signal driven I/O, SIGIO)

      首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已准备好待处理,也可以立即通知主循环,让它来读取数据报。无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达(第一阶段)期间,进程可以继续执行,不被阻塞。免去了select的阻塞与轮询,当有活跃套接字时,由注册的handler处理。

    5、异步I/O模型(AIO, asynchronous I/O)

      进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

      这个模型工作机制是:告诉内核启动某个操作,并让内核在整个操作(包括第二阶段,即将数据从内核拷贝到进程缓冲区中)完成后通知我们。

    这种模型和前一种模型区别在于:信号驱动I/O是由内核通知我们何时可以启动一个I/O操作,而异步I/O模型是由内核通知我们I/O操作何时完成。

    转载自:https://blog.csdn.net/wsx199397/article/details/38533239?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

    展开全文
  • 首先,多路复用(multiplexing) 是计算机里面很常见的一个概念,我觉得他的核心思想就是利用一组资源做很多件事。 常见的多路复用(multiplexing)除了网络编程里面的IO多路复用;还有计算机网络的时分多路复用,频分...
  • 五种IO模型: 同步阻塞IO(Blocking IO):...IO多路复用(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。 异步IO(Asynchronous IO):即经
  • 一、主要的概念多路复用:从源主机的不同套接字中收集数据块,并为每个数据块封装上首部信息(这将在多路分解时使用)从而生成报文段,然后将报文段传递到网络层的工作称为多路复用。多路分解:将运输层报文段中的数据...
  • 基本概念 1. 关于linux文件描述符 在Linux中,一切都是文件,除了文本文件、源文件、二进制文件等,一个硬件设备也可以被映射为一个虚拟的文件,称为设备文件。例如,stdin 称为标准输入文件,它对应的硬件设备一般...
  • 文章目录1 Redis为什么是单线程的1.1 官方解释1.2 Redis单线程优势1.3 Redis 不仅仅是单线程1.4 Redis的性能瓶颈2 IO多路复用2.1 文件描述符和文件句柄2.2 什么是IO多路复用?2.3 Redis的IO多路复用3 多线程IO多路...
  • 描述服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:(1)同步阻塞IO(Blocking IO):即传统的IO模型。(2)同步非阻塞IO(Non-blocking IO):默认创建的...(3)IO多路复用(IO Multiplexing):即经典的Re...
  • 简单介绍:nginx 采用的是多进程(单线程) + io多路复用(epoll)模型 实现高并发 二、nginx 多进程 启动nginx 解析初始化配置文件后会 创建(fork)一个master进程 之后 这个进程会退出 master 进程会 变为...
  • IO多路复用模型

    2021-01-27 15:40:13
    多路复用 上一章节我们介绍了NIO模型,我们先来回顾一下NIO模型:
  • select、poll、epoll都是IO多路复用的机制。IO多路复用就是通过一种机制,让一个进程/线程可以监视多个描述符,一旦某个描述符就绪(一般是读写就绪),能够通知应用程序进行相应的读写操作。I/O复用虽然能同时监听...
  • 线程多路复用

    2021-03-21 21:00:19
    IO多路复用就是通过这样一种机制:一个进程可以监听多个描述符,一旦某个描述符就绪(一般是读就绪和写就绪),能够通知程序进行相应的操作。但select,poll,epoll本质上都是同步IO,因为他们都需要在读写事件就绪...
  • 似乎从互联网起源,BIO、NIO 的话题就从未间断,无论是平时还是面试。那么他们到底是什么?希望你看完这个文章彻底理解这些概念,同时这边文章也使用 Java 代码实现一个 I/O 多路复用...
  • 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: ...(3)IO多路复用(IOMultiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这...
  • Linux中IO多路复用机制

    2021-05-10 08:39:15
    select 多路复用 IO 模型 先解释一个Linux中文件描述符的概念,通过文件描述符,可以找到文件指针,从而进入打开文件表,文件表里有很多关于文件的相关信息。关于Linux的IO多路复用模型还有 poll 和 epoll ,这里说...
  • JAVA NIO 一步步构建I/O多路复用的请求模型当前环境jdk == 1.8代码地址知识点nio 下 I/O 阻塞与非阻塞实现SocketChannel 介绍I/O 多路复用的原理事件选择器与 SocketChannel 的关系事件监听类型字节缓冲 ByteBuffer ...
  • BIO,即Blockig IO,阻塞IO,一个线程对应一个连接,如果你的服务器有很用户,每个用户都需要与你的服务器建立一个连接,那么你有多少用户,你的服务器就得创建多少个线程,显然是不显示的,而且每个线
  • 一、IO多路复用概念 IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞,挂起线程,直到阻塞超时。 二、...
  • Netty-SocketIO多路复用

    2021-04-17 05:30:32
    概念namespace 和room的概念其实用来同一个服务端socket多路复用的。namespace,room和socketio的关系如下: 每一个socket(每一个客户端)会属于某一个room,如果没有指定,那么会有一个default的room。这个room又会...
  • Linux下Select多路复用实现简易聊天室

    千次阅读 2021-12-01 15:57:27
    多路复用的原理 基本概念 多路复用指的是:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。其实就是一种异步处理的操作,等待可运行的描述...
  • 操作系统-多路复用

    2021-11-19 14:03:03
    多路复用(multiplexing),简称复用,是通信技术中的基本概念 。 事实上,多路复用技术的原理就是,把通信资源或者说是链路、信道资源进行的划分,分成一系列的资源片。把这些资源片分配给每一路通信。每一路通信在...
  • 2、关于第 2 点 IO 多路复用,有些同学看到概念后感觉一头雾水,到底什么是 IO 多路复用? 本文从 IO 并发性能提升来整体思考,来逐步剖析 IO 多路复用的原理。 一、如何快速理解 IO 多路复用? 多进程 多线程 基于...
  • 多路复用 IO 多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪就会阻塞应用程序,交出CPU。其中多路是指网络连接,...
  • 这里写自定义目录标题IO复用的三种机制1. linux操作系统中的基础概念1.1 用户控件...I/O多路复用(multiplexing)的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪
  • I/O多路复用一些概念

    2021-06-07 02:04:16
    二、I/O多路复用一些概念 Linux环境下的network I/O 2.1 概念说明 - 用户空间和内核空间 - 进程切换 - 进程的阻塞 - 文件描述符 - 缓存 I/O 2.2 用户空间和内核空间 现在操作系统都是采用虚拟存储器,那么对32位...
  • 1、操作系统基础1-1 用户态与内核态1-2 系统调用1-3 File Descriptor 文件描述符1-4 简单跟踪程序的系统调用过程2、多路复用的流程3、调试NIO的系统调用三、章节总结 一、进阶篇:Netty封装了什么? ​ 之前整理的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 54,809
精华内容 21,923
关键字:

多路复用的概念