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

    千次阅读 2019-06-23 14:48:40
    IO多路复用 同步IO、异步IO、IO多路复用 IO两个阶段 IO过程分两阶段: 数据准备阶段。从设备读取数据到内核空间的缓冲区 内核空间复制回用户空间进程缓冲区阶段 发生IO的时候: 内核从IO设备读数据(例如:淘米...

    IO多路复用

    同步IO、异步IO、IO多路复用

    IO两个阶段

    • IO过程分两阶段:
      1. 数据准备阶段。从设备读取数据到内核空间的缓冲区
      2. 内核空间复制回用户空间进程缓冲区阶段
    • 发生IO的时候:
      1. 内核从IO设备读数据(例如:淘米,把米放在饭锅里煮饭)
      2. 进程从内核复制数据(盛饭,从内核这个饭锅里把饭装到碗里来)
    • 系统调用----read函数

    IO模型

    同步IO模型包括阻塞IO非阻塞IOIO多路复用

    1. 阻塞IO:进程等待(阻塞),直到读写完成。(全程等待)
      system_002
      中文版
      system_003

    2. 非阻塞IO

      • 进程调用recvfrom操作,如果IO设备没有准备好,立即返回ERROR,进程不阻塞。用户可以再次发起系统调用(可以轮询),如果内核已经准备好,就阻塞,然后复制数据到用户空间。
      1. 第一阶段数据没有准备好,可以先忙别的,等会再来看看。检查数据是否准备好了的过程是非阻塞的。
      2. 第二阶段是阻塞的,即内核空间和用户空间之间复制数据时阻塞的。
      • 生活中的例子:淘米、蒸饭我不等,我去玩会,盛饭过程我等着你装好饭,但是要等到盛好饭才算完事,这是同步的,结果就是盛好饭。
        system_004
        中文版
        system_005
    3. IO多路复用

      • IO多路复用,也称为:Event-driven IO
      • 所谓IO多路复用,就是同时监控多个IO,有一个准备好了,就不需要继续等待其他IO,可以开始处理,提交了同时处理IO的能力。
      • select几乎所有操作系统平台都支持,poll是对select的升级。
      • epoll,Linux系统内核2.5+开始支持,对select和poll的增强,在监视的基础上,增加回调机制。BSD、Mac平台有kqueue,windows server有iocp。
        system_006
      • 以select为例,将关注的IO操作告诉select函数并调用,进程阻塞,内核“监视”select关注的文件描述符fd,被关注的任何一个fd对应的IO准备好了数据,select返回。再使用read将数据复制到用户进程。
      • select举例:食堂供应很多菜(众多的IO),你需要吃某三菜一汤,大师傅(操作系统)所要现做,需要等待,你只好等待大师傅叫。其中一样菜好了,大师傅叫你,说你点的菜有好的了,你得自己遍历找找看哪一样才好了,请服务员把做好的菜打给你。
      • epoll是有菜准备好了,大师傅叫你去几号窗口直接打菜,不用自己找菜了。
      • 一般情况下,select最多能监听1024个fd(文件描述符,监听数量可以修改,但不建议修改),但是由于select采用轮询的方式,当管理的IO多了,每次都要遍历全部fd,效率低下。
      • epoll没有管理的fd的上限,且是回调机制,不需遍历,效率很高。
        system_007
    4. 信号驱动IO

      • 进程在IO访问时,先通过sigaction系统调用,提交一个信号处理函数,立即返回。
      • 进程不阻塞。当内核准备好数据后,产生一个SIGIO信号(电平触发)并投递给信号处理函数。可以在此函数中调用recvfrom函数操作数据从内核控件复制到用户控件,这段过程进程阻塞。
        system_008
    5. 异步IO

      • 在进程发起异步IO请求,立即返回。内核完成IO的两个阶段,内核给进程发一个信号。
      • 举例:来打饭,跟大师傅说饭好了叫你,饭菜准备好了,窗口服务员把饭盛好了打电话叫你。两个阶段都是异步的。整个过程中,进程都可以忙别的,等好了才过来。
      • 举例:今天不想出去到饭店吃饭了,点外卖,饭菜在饭店做好了(第一阶段),快递员从饭店送到你家门口(第二阶段)。
      • Linux的aio的系统调用,内核从版本2.6开始支持
        system_009
        对应的中文版
        system_010
    • 总结
      前面4个都是同步IO,因为核心操作recv函数调用时,进程阻塞直到拿到最终结果为止,而异步IO进程全程不阻塞。
      system_011

    Python中IO多路复用

    1. IO多路复用
      • 大多数操作系统都支持select和poll
      • Linux2.5+支持epoll
      • BSD、Mac支持kqueue
      • Solaris实现了/dev/poll
      • Windows server的IOCP
    • Python的select库实现了select、poll系统调用,这个基本上操作系统都支持。部分实现了epoll。它是底层的IO多路复用模块。
    • 开发中的选择
      1. 完全跨平台,使用select、poll。但是性能较差
      2. 针对不同操作系统自行选择支持的技术,这样做会提高IO处理的性能
    • select维护一个文件描述数据结构,单个进程使用有上限,通常是1024,线性扫描这个数据结构。效率低。
    • pool和select的区别是内部数据结构使用链表,没有这个最大限制,但是依然是线性遍历才知道那个设备就绪了。
    • epoll使用事件通知机制,使用回调机制提高效率
    • select/poll还要从内核空间复制消息到用户空间,而epoll通过内核空间和用户空间共享一块内存来减少复制。

    selectors库

    3.4版本提供selectors库,高级IO复用库。

    1. 层次结构:
      • BaseSelector
        • SelectSelector #实现select
        • PollSelector #实现poll
        • EpollSelector #实现epoll
        • DevpollSelector #实现devpoll
        • KqueueSelector #实现kqueue
    • selectors.DefaultSelector返回当前平台最有效、性能最高的实现。但是,由于没有实现Windows下的IOCP,所以,Windows下只能退化为select。
    # 在selects模块源码最下面有如下代码
    # Choose the best implementation, roughly:
    #    epoll|kqueue|devpoll > poll > select.
    # select() also can't accept a FD > FD_SETSIZE (usually around 1024)
    if 'KqueueSelector' in globals():
        DefaultSelector = KqueueSelector
    elif 'EpollSelector' in globals():
        DefaultSelector = EpollSelector
    elif 'DevpollSelector' in globals():
        DefaultSelector = DevpollSelector
    elif 'PollSelector' in globals():
        DefaultSelector = PollSelector
    else:
        DefaultSelector = SelectSelector
    
    • 事件注册代码
    class SelectSelector(_BaseSelectorImpl):
        """Select-based selector."""
    
        def __init__(self):
            super().__init__()
            self._readers = set()
            self._writers = set()
    
        def register(self, fileobj, events, data=None):
            key = super().register(fileobj, events, data)
            if events & EVENT_READ:
                self._readers.add(key.fd)
            if events & EVENT_WRITE:
                self._writers.add(key.fd)
            return key
    
    • 为selector注册一个文件对象,监视它的IO事件。返回SelectKey对象
    • fileobj被监视文件对象,例如:socket对象
    • events事件,该文件对象必须等待事件
    • data可选的与此文件对象相关联的不透明数据,例如,关联用来存储每个客户端的会话ID,关联方法。通过这个参数在关注事件产生后让selector干什么事。
    Event常量含义
    EVENT_READ可读0b01,内核已经准备好输入输出设备,可以开始读了
    EVENT_WRITE可写0b10,内核准备好了,可以往里写了
    • selectors.SelectorKey有4个属性:

      1. fileobj注册的文件对象
      2. fd文件描述符
      3. events等待上面的文件描述符的文件对象的事件
      4. data注册时关联的数据
    • selectors的基本使用方法

      1. 获取操作系统最优select
        • selectors.DefaultSelector()
      2. 注册需要监听的文件描述符对象
        • selectors.register(fileobj, events, data=None)->Event对象
          • fileobj #文件描述符对象,socket也是文件描述符对象
          • events #事件,有selectors.EVENT_READ和selectors.EVENT_WRITE
          • data #数据,默认为None
      3. 监听事件selectors.select() 会产生阻塞,如果监听到会返回一个Events列表,表示所有已经触发的事件。
      4. 处理事情
      5. 注销所有监听
        • selectors.unregister(fileobj)
          • fileobj 正在监听的文件描述符对象
      6. 关闭selectors对象 selectors.close()

    练习:IO多路复用TCP Server

    • 完成一个TCP Server,能够接受客户端请求并回应客户客户端消息。
    import logging
    import sys
    import selectors
    import socket
    
    logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
    
    #构成Socket
    sk = socket.socket()
    sk.bind(("127.0.0.1",3999))
    sk.setblocking(False) #注意:建议非阻塞
    sk.listen()
    #构建本系统最优的Selector
    server = selectors.DefaultSelector()
    
    #回调函数,sock的读事件
    #形参自己定义
    def accep(sk:socket.socket,mask:int):
        conn,raddr = sk.accept()
        server.register(conn,selectors.EVENT_READ,read)
    
    def read(conn:socket.socket,mask):
        data = conn.recv(1024)
        msg = "{}-{}".format(conn.getpeername(), data.decode())
        logging.info(msg)
        conn.send(msg.encode())
    
    #在内核中注册sk的读入事件,返回SelectorKey对象
    #key记录了fileobje,fileobj的fd,events,data
    server.register(sk,selectors.EVENT_READ,accep)
    #开始循环
    while True:
        #监听注册的对象的事件,发生被关注事件则返回events
        events = server.select()
        logging.info(events) #[(key,mask)]
        for key,mask in events:
            key.data(key.fileobj,mask)
    
    

    实战:IO多路复用群聊软件

    不需要启动多线程来执行socket的accept,recv方法

    import logging
    import sys
    import selectors
    import socket
    import threading
    
    logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
    
    class ChatServer:
        def __init__(self,ip="127.0.0.1",port=3999):
            self.sock = socket.socket()
            self.laddr = ip,port
            self.event = threading.Event()
            #构建本系统最优Selector
            self.select = selectors.DefaultSelector()
    
        def start(self):
            self.sock.bind(self.laddr)
            self.sock.listen()
            self.sock.setblocking(False) #设置为非阻塞
            #注册sock的被关注数据,返回SelectorKey对象
            #key记录了fileobj,fileobj的fd,events,data
            self.select.register(self.sock,selectors.EVENT_READ,"accept")
    
            #事件监听循环
            threading.Thread(target=self.run,name="run",daemon=True).start()
    
        def run(self):
            try:
                #开始循环
                while not self.event.is_set():
                    for key, mask in self.select.select():
                        #根据不同的data做不通的处理
                        if key.data == "accept":
                            self.accept(mask)
                        else:
                            key.data(key.fileobj, mask)
            finally:
                #循环结束时反注册所有事件监听,并关闭conn
                conns = [k.fileobj for k in self.select.get_map().values()]
                for cn in conns:
                    self.select.unregister(cn)
                    cn.close()
    
        #self.sock的事件监听
        def accept(self,mk):
            conn,raddr = self.sock.accept()
            conn.setblocking(False) #设置为非阻塞
            logging.info("一个新的链接建立:{}".format(raddr))
            self.select.register(conn,selectors.EVENT_READ,self.recv)
    
        #回调函数,mk--》mask:事件的掩码
        def recv(self,conn:socket.socket,mk:int):
            try:
                data = conn.recv(1024)
            except Exception as e:
                logging.info(e)
                self.select.unregister(conn)
                conn.close()
                return
            if data == b"quit" or data == b"":
                self.select.unregister(conn)
                logging.info("退出了一个连接{}".format(conn.getpeername()))
                conn.close()
                return
            msg = "[{}] {}".format(conn.getpeername(),data.decode()).encode()
            logging.info(msg)
            for cn in self.select.get_map().values():
                if cn.data == "accept": continue
                cn.fileobj.send(msg)
    
        def stop(self):
            self.event.set()
            self.select.close()
    
        @classmethod
        def main(cls):
            chatserver = cls()
            chatserver.start()
            while not chatserver.event.is_set():
                cmd = input(">>>")
                if cmd.strip() == "quit":
                    chatserver.stop()
                else:
                    logging.info(threading.enumerate())
    
    ChatServer.main()
    

    总结

    • 使用IO多路复用 + (select、epoll)并不一定比多线程 + 同步阻塞IO性能好,其最大优势可言处理更多的链接。
    • 多线程 + 同步阻塞IO模型
    • 开辟太多线程,线程开辟,销毁开销还是较大,倒是可以使用线程池;线程多,线程自己使用的内存也很可观;多线程切换时要保护现场和恢复现场,线程过多,切换会占用大量时间。
    • 链接比较少,多线程+同步阻塞IO模型比较合适,效率也不低
    • 如果链接非常多,对服务端程序来说,IO并发还是比较高的,这时候,开辟太多线程其实业不是很划算,这时候IO多路复用或许是更好的选择。
    展开全文
  • 什么是IO 多路复用呢? 我一个SocketServer有500个链接连过来了,我想让500个链接都是并发的,每一个链接都需要操作IO,但是单线程下IO都是串行的,我实现多路的,看起来像是并发的效果,这就是多路复用! 概念说明...
  • 多路复用,是内核发生了变化 BIO 多线程的缺点:大量线程上下文切换,线程利用率不高 NIO的缺点:加入有1w连接,每次循环中的read都要进行一次系统调用,需要进行用户态内核态切换 多路复用:一次系统调用可监控多...
    BIO-> NIO -> 多路复用,是内核发生了变化
    
    BIO 多线程的缺点:大量线程上下文切换,线程利用率不高
    NIO的缺点:加入有1w连接,每次循环中的read都要进行一次系统调用,需要进行用户态内核态切换
    多路复用:一次系统调用可监控多个文件描述符的是否都相应事件发生
    
    select原理:将要监控的文件描述符以bitmap的方式传入内核空间,内核遍历传入的所有文件描述符,对满足条件的文件
               置位然后全部返回,用户程序需要遍历所有文件描述符
    select缺点:监听的数目最大是1024个,内核仍然需要遍历各个文件描述符以查看其状态,每次调用前都要准备需要监视的文件描述符集合,
                用户拿到返回的结果还需要遍历结果集找到置位的文件描述符
    poll原理:将要监控的文件描述符以结构体数组的方式传入内核空间,内核遍历所有的文件描述符,对满足条件的文件描述符打上标记
              然后全部返回,用户程序需要遍历所有文件描述符,同时当他找到被打上标记的文件描述符时,可以将标记清空,从而达到重用的目的
    		  
    poll缺点:内核仍然需要遍历各个文件描述符以查看其状态,用户拿到返回的结果还需要遍历结果集找到置位的文件描述符
    epoll原理:
       1.创建epoll句柄  epoll_create(size)
         声明自己可以监听文件描述符的数目,同时在内核开辟对应大小的空间
       2.epoll_ctl()
         在epoll句柄上设置被监听的文件描述符,以及监听的事件类型。
       3.epoll_wait()
         传入一个数组,等待监听的事件发生,对应事件放入数组,然后返回事件个数
       epoll的关键在于,它并不会遍历文件描述符,而是每次有事件发生时,内核调用ecpoll_ctl注册的回调函数
       将对应的文件描述符及事件添加到一个集合中
    
    epoll优点:要被监控的文件描述符只需要传入内核一次,内核找到对应事件发生的文件描述符只需要O(1)的时间复杂度
              用户拿到的结果不需要再遍历,只需要读出前k个事件发生的文件描述符
    
    
    selelct的用法,每次循环时,先进行select调用,传入要监控的secket对应的文件描述符,内核进行遍历轮询,然后将可读的那几个返回,
                   然后程序只需要调用那些可读的socket的read方法
    			   缺点:内核中遍历仍然是O(N)
    
    零拷贝:传统发送数据,read,系统调用,用户态内核态的切换,数据从磁盘读入内核空间,然后从内核空间复制到
            用户空间,write,系统调用,用户态内核态的切换,数据从用户空间复制到内核空间,内核将其发送
    	    sendfile(零拷贝),系统调用,用户态内核态的切换,数据从磁盘读入内核空间然后内核将其发送
    
    直接内存:用户空间和内核空间都可以访问的内存,好处是对于执行频繁在内核空间与用户空间来回拷贝数据的代码,可以使用直接内存
              将数据放到共享内存上。
    
    kafka的读取速度为什么快:
      1)数据的写入是追加形式的,不涉及随机写
      2)接收到的数据会放到直接内存中,然后可由内核程序直接写入磁盘
      3)发生的数据使用了0拷贝技术
    
    展开全文
  • 多路复用

    千次阅读 多人点赞 2019-08-13 14:26:58
    多路复用先我觉得有必要讲一下什么是阻塞IO、非阻塞IO、同步IO、异步IO这几个东西;linux的五种IO模型: 1)阻塞I/O(blocking I/O) 2)非阻塞I/O(nonblocking I/O) 3) I/O复用(select和poll)(I/O ...

    讲多路复用先我觉得有必要讲一下什么是阻塞IO、非阻塞IO、同步IO、异步IO这几个东西;linux的五种IO模型:

    1)阻塞I/O(blocking I/O)

    2)非阻塞I/O(nonblocking I/O)

    3) I/O复用(select和poll)(I/O multiplexing)

    4)信号驱动I/O(signal driven I/O (SIGIO))

    5)异步I/O(asynchronous I/O (the POSIX aio_functions))。

     

    POSIX(可移植操作系统接口)把同步IO操作定义为导致进程阻塞直到IO完成的操作,反之则是异步IO。同步异步的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的调函数。

    上面的概念看着就很不好理解,这也常常让我傻傻分不清,关于同步和异步,又去查了关于同步和异步,找到一个比较好理解的说法

    同步和异步其实指的是,请求发起方对消息结果的获取是主动发起的,还是等被动通知的。如果是请求方主动发起的,一直在等待应答结果(同步阻塞),或者可以先去处理其他的事情,但要不断轮询查看发起的请求是否有应答结果(同步非阻塞 )因为不管如何都要发起方主动获取消息结果,所以形式上还是同步操作。如果是由服务方通知的,也就是请求方发出请求后,要么在一直等待通知(异步阻塞),要么就先去干自己的事了(异步非阻塞),当事情处理完成之后,服务方会主动通知请求方,它的请求已经完成,这就是异步。异步通知的方式一般是通过状态改变,消息通知,或者回调函数来完成,大多数时候采用的都是回调函数。

    就可以很清楚知道,同步等待应答结果,是主动去获取消息结果,而异步是等待通知,等别人给他通知,怎么理解这两个的区别呢,后面介绍IO模式的时候慢慢说

    下面关于IO模型转至:https://www.cnblogs.com/euphie/p/6376508.html

     

    阻塞IO模型

    使用recv的默认参数一直等数据直到拷贝到用户空间,这段时间内进程始终阻塞。A同学用杯子装水,打开水龙头装满水然后离开。这一过程就可以看成是使用了阻塞IO模型,因为如果水龙头没有水,他也要等到有水并装满杯子才能离开去做别的事情。很显然,这种IO模型是同步的。

     

    非阻塞IO模型

    改变flags,让recv不管有没有获取到数据都返回,如果没有数据那么一段时间后再调用recv看看,如此循环。B同学也用杯子装水,打开水龙头后发现没有水,它离开了,过一会他又拿着杯子来看看……在中间离开的这些时间里,B同学离开了装水现场(回到用户进程空间),可以做他自己的事情。这就是非阻塞IO模型。但是它只有是检查无数据的时候是非阻塞的,在数据到达的时候依然要等待复制数据到用户空间(等着水将水杯装满),因此它还是同步IO。

     

    IO复用模型

    这里在调用recv前先调用select或者poll,这2个系统调用都可以在内核准备好数据(网络数据到达内核)时告知用户进程,这个时候再调用recv一定是有数据的。因此这一过程中它是阻塞于select或poll,而没有阻塞于recv,有人将非阻塞IO定义成在读写操作时没有阻塞于系统调用的IO操作(不包括数据从内核复制到用户空间时的阻塞,因为这相对于网络IO来说确实很短暂),如果按这样理解,这种IO模型也能称之为非阻塞IO模型,但是按POSIX来看,它也是同步IO,那么也和楼上一样称之为同步非阻塞IO吧。

    这种IO模型比较特别,分个段。因为它能同时监听多个文件描述符(fd)。这个时候C同学来装水,发现有一排水龙头,舍管阿姨告诉他这些水龙头都还没有水,等有水了告诉他。于是等啊等(select调用中),过了一会阿姨告诉他有水了,但不知道是哪个水龙头有水,自己看吧。于是C同学一个个打开,往杯子里装水(recv)。这里再顺便说说鼎鼎大名的epoll(高性能的代名词啊),epoll也属于IO复用模型,主要区别在于舍管阿姨会告诉C同学哪几个水龙头有水了,不需要一个个打开看(当然还有其它区别)。

     

    信号驱动IO模型

    通过调用sigaction注册信号函数,等内核数据准备好的时候系统中断当前程序,执行信号函数(在这里面调用recv)。D同学让舍管阿姨等有水的时候通知他(注册信号函数),没多久D同学得知有水了,跑去装水。是不是很像异步IO?很遗憾,它还是同步IO(省不了装水的时间啊)。

     

    异步IO模型

    调用aio_read,让内核等数据准备好,并且复制到用户进程空间后执行事先指定好的函数。E同学让舍管阿姨将杯子装满水后通知他。整个过程E同学都可以做别的事情(没有recv),这才是真正的异步IO。

     

    总结,IO分两阶段:

    1.数据准备阶段

    2.内核空间复制回用户进程缓冲区阶段

    一般来讲:阻塞IO模型、非阻塞IO模型、IO复用模型(select/poll/epoll)、信号驱动IO模型都属于同步IO,因为阶段2是阻塞的(尽管时间很短)。只有异步IO模型是符合POSIX异步IO操作含义的,不管在阶段1还是阶段2都可以干别的事。

     

    看完上面的介绍就能清楚什么是同步和异步了,对于IO而言,同步IO不管怎样都还是要read将数据读出来,而异步IO就通知你有数据来就可以直接对数据进行处理,就像个小孩子,同步小朋友肚子饿了,再怎么偷懒,饭菜家里人做好了,他也还是自己去打饭,然后吃,而异步小朋友是个小霸王,要别人打好饭送到面前喂他,他再决定是躺着吃还是坐着吃。

     

    阻塞和非阻塞,可以fcntl设置文件成非阻塞,要想非阻塞操作普通的文件时,open(const char *pathname, int flags, mode_t mode);参数flags加上O_NONBLOCK或O_NDELAY。对于网络socket的文件描述符,recv(int sockfd, void *buf, size_t len, int flags);最后一个参数flag设置成MSG_DONTWAIT,对于阻塞的socket也可以setsockopt设置超时时间,可能还有很多处理方法,这里只是说几个我知道的~,接下来就主要说多路复用了!也不会说很详细具体怎么用吧,就是说一下多路复用select、poll、epoll的区别,各自的优缺点什么的。

     

    多路复用select、poll、epoll

    网上看到的几个比较有趣的对比,来源https://www.cnblogs.com/wt645631686/p/8528912.html

    1、select大妈 每一个女生下楼, select大妈都不知道这个是不是你的女神, 她需要一个一个询问, 并且select大妈能力还有限, 最多一次帮你监视1024个妹子;

    poll大妈不限制盯着女生的数量, 只要是经过宿舍楼门口的女生, 都会帮你去问是不是你女神;

    epoll大妈不限制盯着女生的数量, 并且也不需要一个一个去问. 那么如何做呢? epoll大妈会为每个进宿舍楼的女生脸上贴上一个大字条,上面写上女生自己的名字, 只要女生下楼了, epoll大妈就知道这个是不是你女神了, 然后大妈再通知你;

     

    2、一个epoll场景:一个酒吧服务员(一个线程),前面趴了一群醉汉,突然一个吼一声“倒酒”(事件),你小跑过去给他倒一杯,然后随他去吧,突然又一个要倒酒,你又过去倒上,就这样一个服务员服务好多人,有时没人喝酒,服务员处于空闲状态,可以干点别的玩玩手机。至于epoll与selectpoll的区别在于后两者的场景中醉汉不说话,你要挨个问要不要酒,没时间玩手机了。io多路复用大概就是指这几个醉汉共用一个服务员。

     

    select

    相关函数

    FD_ZERO(fd_set* fds) //清空集合
    FD_SET(int fd, fd_set* fds) //将给定的描述符加入集合
    FD_ISSET(int fd, fd_set* fds) //判断指定描述符是否在集合中
    FD_CLR(int fd, fd_set* fds) //将给定的描述符从文件中删除
    int select(int max_fd, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout); 

     

    select监视并等待多个文件描述符的属性发生变化,它监视的属性分3类,分别readfds(文件描述符有数据到来可读)writefds(文件描述符可写)、和exceptfds(文件描述符异常)调用后select函数会阻塞,直到有描述符就绪(有数据可读、可写、或者有错误异常),或者超时timeout 指定等待时间)发生函数才返回。当select()函数返回后,可以通过遍历 fdset,来找到究竟是哪些文件描述符就绪。 select函数的返回值是就绪描述符的数目,超时时返回0,出错返回-1;需要注意的还有select第一个参数max_fd指待监听的fd的总个数,它的值是待监听的最大文件描述符加1

    fd_set的定义

    typedef struct

    {

    /*XPG4.2requiresthismembername.Otherwiseavoidthename

    fromtheglobalnamespace.*/

    #ifdef__USE_XOPEN

    __fd_maskfds_bits[__FD_SETSIZE/__NFDBITS];

    #define__FDS_BITS(set)((set)->fds_bits)

    #else

    __fd_mask__fds_bits[__FD_SETSIZE/__NFDBITS];

    #define__FDS_BITS(set)((set)->__fds_bits)

    #endif

    }fd_set;

    在Linux内核有个参数__FD_SETSIZE定义了每个FD_SET的句柄个数中,这也意味着select所用到的FD_SET是有限的,也正是这个原因select()默认只能同时处理1024个客户端连接请求:
    /linux/posix_types.h:
    #define __FD_SETSIZE 1024 

    还需要注意的是,select返回之后没有发生事件的fd的位会被清空,例如:

    (1)执行fd_set set;FD_ZERO(&set);则set用位表示是...0000,0000。

    (2)若fd=5,执行FD_SET(fd,&set);后set变为...0001,0000(第5位置为1)

    (3)若再加入fd=2,fd=1,则set变为...0001,0011

    (4)执行select(6,&set,0,0,0)阻塞等待

    (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为...0000,0011。注意:没有事件发生的fd=5被清空。

    所以每次select轮询完之后,如果还需要再用到select,需要重新调用FD_SET();将文件描述符加入到监听事件里 

    select的缺点:

    1. 单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;根据fd_size的定义它的大小为32个整数大小(32位机器为32*32,所有共有1024bits可以记录fd),每个fd一个bit,所以最大只能同时处理1024个fd

    2. 内核 / 用户空间内存拷贝问题,select需要复制大量的句柄数据结构,产生巨大的开销;

    3. select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件;

    4. select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将这些文件描述符通知进程。

     

    Poll:

    select()和poll()系统调用的本质一样。poll()的机制与 select() 类似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll() 没有最大文件描述符数量的限制(但是数量过大后性能也是会下降)。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大 

    #include <poll.h>
    
    struct pollfd
    
    {
    
    int fd; /* 文件描述符 */
    
    short events; /* 等待的事件 */
    
    short revents; /* 实际发生了的事件 */
    
    } ;
    
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);

    第一个参数用来指向一个struct pollfd类型的数组,每一个pollfd结构体指定了一个被监视的文件描述符,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域,events域中请求的任何事件都可能在revents域中返回。

    第二个参数同样是待监听的最大文件描述符加一

    第三个参数是超时时间

    相比select模型,poll使用链表保存文件描述符,因此没有了监视文件数量的限制,但其他三个缺点依然存在。

     

    epoll:

    相关函数:

    1.调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
    2. 调用epoll_ctl向epoll对象中添加或删除监听的文件描述符
    3. 调用epoll_wait收集发生的事件的连接

     

    Epoll触发模式LT模式和ET模式: 

    epoll有LT(evel triggered水平触发)和ET(edge-triggered边沿触发)两种触发模式,LT是默认的模式,ET是“高速”模式。LT模式下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作,而在ET(边缘触发)模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读。ET和LT的区别,LT事件不会丢弃,而是只要读buffer里面有数据可以让用户读,则不断的通知你。而ET则只在事件发生之时通知。可以简单理解为LT是水平触发,而ET则为边缘触发。LT模式只要有事件未处理就会触发,而ET则只在高低电平变换时(即状态从1到0或者0到1)触发。

     

    epoll的优点:

    1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)
    2、效率提升,不是轮询的方式,只监听你感兴趣并添加进去的文件描述符不会随着文件描述符的增加效率下降。只有活跃可用的文件描述符才会调用callback函数;即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。

     

     

    选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。

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

    2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善

     

    展开全文
  • 多路复用技术

    千次阅读 2019-10-04 07:26:18
    多路复用技术 多路复用技术是把多个低速信道组合成一个高速信道的技术,它可以有效的提高数据链路的利用率,从而使得一条高速的主干链路同时为多条低速的接入链路提供服务,也就是使得网络干线可以同时运载大量的...

    多路复用技术

    多路复用技术是把多个低速信道组合成一个高速信道的技术,它可以有效的提高数据链路的利用率,从而使得一条高速的主干链路同时为多条低速的接入链路提供服务,也就是使得网络干线可以同时运载大量的语音和数据传输。多路复用技术是为了充分利用传输媒体,人们研究了在一条物理线路上建立多个通信信道的技术。

    多路复用技术的实质是,将一个区域的多个用户数据通过发送多路复用器进行汇集,然后将汇集后的数据通过一个物理线路进行传送,接收多路复用器再对数据进行分离,分发到多个用户。多路复用通常分为频分多路复用、时分多路复用、波分多路复用、码分多址和空分多址。

    频分多路复用

    频分多路复用技术FDM(Frequency Division MulTIplexing)。

    频分多路复用利用通信线路的可用带宽超过了给定的带宽这一优点。频分多路复用的基本原理是:如果每路信号以不同的载波频率进行调制,而且各个载波频率是完全独立的,即各个信道所占用的频带不相互重叠,相邻信道之间用“警戒频带”隔离,那么每个信道就能独立地传输一路信号。

    频分多路复用的主要特点是,信号被划分成若干通道(频道,波段),每个通道互不重叠,独立进行数据传递。每个载波信号形成一个不重叠、相互隔离(不连续)的频带。接收端通过带通滤波器来分离信号。频分多路复用在无线电广播和电视领域中的应用较多。ADSL也是一个典型的频分多路复用。ADSL用频分多路复用的方法,在PSTN使用双绞线上划分出三个频段:0~4kHz用来传送传统的语音信号;20~50kHz用来传送计算机上载的数据信息;150~500kHz或140~1100kHz用来传送从服务器上下载的数据信息。

    时分多路复用

    时分多路复用技术TDM(TIme Division MulTIplexing)

    时分多路复用是以信道传输时间作为分割对象,通过为多个信道分配互不重叠的时间片段的方法来实现多路复用。时分多路复用将用于传输的时间划分为若干个时间片段,每个用户分得一个时间片。时分多路复用通信,是各路信号在同一信道上占有不同时间片进行通信。由抽样理论可知,抽样的一个重要作用,是将时间上连续的信号变成时间上的离散信号,其在信道上占用时间的有限性,为多路信号沿同一信道传输提供条件。具体说就是把时间分成一些均匀的时间片,通过同步(固定分配)或统计(动态分配)的方式,将各路信号的传输时间配分在不同的时间片,以达到互相分开,互不干扰的目的。

    至2011年9月,应用最广泛的时分多路复用是贝尔系统的T1载波。T1载波是将24路音频信道复用在一条通信线路上,每路音频信号在送到多路复用器之前,要通过一个脉冲编码调制编码器,编码器每秒抽样8000次。24路信号的每一路,轮流将一个字节插入到帧中,每个字节的长度为8位,其中7位是数据位,1位用于信道控制。每帧由24&TImes;8=192位组成,附加1bit作为帧的开始标志位,所以每帧共有193bit。由于发送一帧需要125ms,一秒钟可以发送8000帧。因此T1载波数据传输速率为:

    193bit×8000=1544000bps=1544Kbps=1.544Mbps

    多路复用技术原理解析

    波分多路复用

    波分多路复用技术WDM(Wavelength Division Multiplexing)

    波分复用用同一根光纤内传输多路不用波长的光信号,以提高单根光纤的传输能力。因为光通信的光源在光通信的“窗口”上只占用了很窄的一部分,还有很大的范围没有利用。

    也可以这样认为WDM是FDM应用于光纤信道的一个变例。如果让不用波长的光信号在同一根光纤上传输而互不干扰,利用多个波长适当错开的光源同时在一根光纤上传送各自携带的信息,就可以增加所传输的信息容量。由于是用不同的波长传送各自的信息,因此即使在同一根光纤上也不会相互干扰。在接收端转换成电信号时,可以独立地保持每个不同波长的光源所传送的信息。这种方式就叫做“波分复用”。

    如果将一系列载有信息的不同波长的光载波,在光领域内以1至几百纳米的波长间隔合在一起沿单根光纤传输,在接收器再一一定的方法,将各个不同波长的光载波分开。在光纤上的工作窗口上安排100个波长不同的光源,同时在一根光纤上传送各自携带的信息,就能使光纤通信系统的容量提高100倍。

    码分多址

    码分多址技术CDMA(Code Division Multiple Access)

    码分多址是采用地址码和时间、频率共同区分信道的方式。CDMA的特征是个每个用户有特定的地址码,而地址码之间相互具有正交性,因此各用户信息的发射信号在频率、时间和空间上都可能重叠,从而使用有限的频率资源得到利用。

    CDMA是在扩频技术上发展起来的无线通信技术,即将需要传送的具有一定信号带宽的信息数据,从一个带宽远大于信号带宽的高速伪随机码进行调制,使原数据信号的带宽被扩展,再经载波调制并发送出去。接收端也使用完全相同的伪随机码,对接受的带宽信号作相关处理,把宽带信号换成原信息数据的窄带信号即解扩,以实现信息通信。

    不同的移动台(或手机)可以使用同一个频率,但是每个移动台(或手机)都被分配带有一个独特的“码序列”,该序列码与所有别的“序列码”都不相同,因为是靠不同的“码序列”来区分不同的移动台(或手机),所以各个用户相互之间也没有干扰从而达到了多路复用的目的。

    空分多址

    空分多址技术SDMA(Space Division Multiple Access)

    这种技术是将空间分割构成不同的信道,从而实现频率的重复使用,达到信道增容的目的。举例来说,在一个卫星上使用多个天线,各个天线的波束射向地球表面的不同区域地面上不同区域的地球站,他们在同一时间,即使用相同的频率进行工作,它们之间也不会形成干扰。SDMA系统的处理程序如下:

    1、系统将首先对来自所有天线中的信号进行快照或取样,然后将其转换成数字形式,并存储在内存中。

    2、计算机中的SDMA处理器将立即分析样本,对无线环境进行评估,确认用户、干扰源及所在的位置。

    3、处理器对天线信号的组合方式进行计算,力争最佳地恢复用户的信号。借助这种策略,每位用户的信号接收质量将提高,而其他用户的信号或干扰信号则会遭到屏蔽。

    4、系统进行模拟计算,使天线阵列可以有选择地向空间发送信号。再次在此基础上,每位用户的信号都可以通过单独的通信信道空间-空间信道实现高效的传输。

    5、在上述处理的基础上,系统就能够在每条空间信道上发送和接受信号,从而使这些信号称为双向信道。

    利用上述流程,SDMA系统就能够在一条普通信道上创建大量的频分、时分或码分双向空间信道,没一条信道扣可以完全活的整个阵列的增益和抗干扰功能。从理论上而言,带m个单元的阵列能够在每条普通行道上支持m条空间信道。但在实际应用中支持的信道数量将略低于这个数目,具体情况则取决于环境。由此可见,SDMA系统可使系统容量成倍增加,使得系统在有限的频谱内可以支持更多的用户,从而成倍的提高频谱使用效率。

    自2011年9月,近几十年来,无线通信经历了从模拟到数字,从固定到移动的重大变革。而就移动通信而言,为了更有效地利用有限的无线频率资源,时分多址技术(TDMA)、频分多址技术(FDMA)、码分多址技术(CDMA)得到了广泛的应用,并在此基础上建立了GSM和CDMA(是区别于3G的窄带CDMA)两大主要的移动通信网络。就技术而言,现有的这三种多址技术已经得到了充分的应用,频谱的使用效率已经发挥到了极限。空分多址技术(SDMA)则突破了传统的三维思维模式,在传统的三维技术的基础上,在第四维空间上极大地拓宽了频谱的使用方式,使用移动用户仅仅由于空间位置的不同而复用同一个传统的物理信道称为可能,并将移动通信技术引入了一个更为崭新的领域。

    多路复用技术原理解析

    多路复用是什么意思?

    在I/O编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者I/O多路复用技术进行处理。I/O多路复用技术通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降底了系统的维护工作量,节省了系统资源,I/O多路复用的主要应用场景如下:

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

    服务器需要同时处理多种网络协议的套接字。

    目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,在Linux网络编程过程中,很长一段时间都使用select做轮询和网络事件通知,然而select的一些固有缺陷导致了它的应用受到了很大的限制,最终Linux不得不在新的内核版本中寻找select的替代方案,最终选择了epoll。epoll与select的原理比较类似,为了克服select的缺点,epoll作了很多重大改进,现总结如下:

    1. 支持一个进程打开的socket描述符(FD)不受限制(仅受限于操作系统的最大文件句柄数)。

    select最大的缺陷就是单个进程所打开的FD是有一定限制的,它由FD_SETSIZE设置,默认值是1024。对于那些需要支持上万个TCP连接的大型服务器来说显然太少了。可以选择修改这个宏,然后重新编译内核,不过这会带来网络效率的下降。我们也可以通过选择多进程的方案(传统的Apache方案)解决这个问题,不过虽然在Linux上创建进程的代价比较小,但仍旧是不可忽视的,另外,进程间的数据交换非常麻烦,对于Java由于没有共享内存,需要通过Socket通信或者其他方式进行数据同步,这带来了额外的性能损耗,增加了程序复杂度,所以也不是一种完美的解决方案。值得庆幸的是,epoll并没有这个限制,它所支持的FD上限是操作系统的最大文件句柄数,这个数字远远大于1024。例如,在1GB内存的机器上大约是10万个句柄左右,具体的值可以通过cat/proc/sys/fs/filemax察看,通常情况下这个值跟系统的内存关系比较大。

    2. I/O效率不会随着FD数目的增加而线性下降。

    传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,由于网络延时或者链路空闲,任一时刻只有少部分的socket是“活跃”的,但是select/poll每次调用都会线性扫描全部集合,导致效率呈现线性下降。epoll不存在这个问题,它只会对“活跃”的socket进行操作-这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的,那么,只有“活跃”的socket才会主动的去调用callback函数,其他idle状态socket则不会。在这点上,epoll实现了一个伪AIO。针对epoll和select性能对比的benchmark测试表明:如果所有的socket都处于活跃态。例如一个高速LAN环境,epoll并不比select/poll效率高太多;相反,如果过多使用epoll_ctl,效率相比还有稍微的下降。但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。

    3. 使用mmap加速内核与用户空间的消息传递

    无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存复制就显得非常重要,epoll是通过内核和用户空间mmap使用同一块内存实现。

    4. epoll的API更加简单

    用来克服select/poll缺点的方法不只有epoll,epoll只是一种Linux的实现方案。在freeBSD下有kqueue,而dev/poll是最古老的Solaris的方案,使用难度依次递增。但epoll更加简单。

    转载于:https://www.cnblogs.com/wdp1990/p/11616190.html

    展开全文
  • Redis I/O 多路复用

    万次阅读 多人点赞 2019-05-19 19:18:01
    引出IO多路复用 为什么 Redis 中要使用 I/O 多路复用这种技术呢? 首先,Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下...
  • redis IO多路复用技术

    万次阅读 2018-02-04 18:38:22
    redis 采用网络IO多路复用技术来保证在多连接的时候, 系统的高吞吐量。 LINUX IO多路复用原理 在linux下面, 常见的有5中网络IO方式, 具体可以参考如下的文章, 总结的很清楚, 我们就不再具体介绍: ...
  • 什么是IO多路复用,理解IO多路复用

    千次阅读 多人点赞 2020-04-03 12:18:00
    三、I/O多路复用 好了,我们讲了这么多,再来总结一下,到底什么是I/O多路复用。 先讲一下I/O模型: 首先,输入操作一般包含两个步骤: 等待数据准备好(waiting for data to be ready)。对于一个套接口上的操作,...
  • 高并发服务器中的多线程多进程缺陷问题: 多进程: 进程数量有限 代价太高(销毁,上下文切换) 受限于CPU 内存隔离 进程间通信代价高 ...以上都是不可避免的,所以引出了IO多路复用(IO多路转接
  • redis的多路复用

    2020-03-26 15:45:16
    采用了网络io多路复用技术来保证在多连接的时候的系统的吞吐量 为什么redis使用io多路复用技术? 因为redis是单线程的,所有的操作都是按照顺序线性执行的,但是由于读写操作是阻塞的,所以某个请求假如是阻塞...
  • I/O多路复用之select

    千次阅读 2017-03-30 16:39:49
    I/O多路复用:I/O多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备就绪后,它就通知该进程的应用程序,应用程序就可以马上完成响应的I/O操作,而不需要等待系统完成I/O操作,这样大大提高了效率。I/O多路...
  • Redis的I/O多路复用

    千次阅读 2020-08-06 09:28:14
    Redis的I/O多路复用[1] 为什么 Redis 中要使用 I/O 多路复用?[2] I/O 多路复用模型[3] select[4] poll[5] epoll[6] Reactor 设计模式[7] 封装 epoll 函数[8] select & poll & epoll比较[9] 深入理解select...
  • IO多路复用技术 讲解 epoll 技术前,我们先了解一下什么是 IO 多路复用技术。 假设现在有一个服务器程序调用 accept 函数成功与客户端建立了连接,那么通过 accept 函数返回的通讯套接字,服务器就可以调用 ...
  • Java IO多路复用机制详解

    千次阅读 2020-04-03 18:03:42
    1、在Java中,常见的IO模型有4种, 同步阻塞IO(BlockingIO):即传统的IO模型。 同步非阻塞IO(Non-blockingIO):默认创建的socket都... IO多路复用(IOMultiplexing):经典的Reactor模式,也称为异步阻塞IO,...
  • 多路复用原理介绍

    千次阅读 2017-11-09 18:05:00
    多路复用 I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。 Linux Linux中的 select,poll,epoll 都是IO多路复用的机制。 具体...
  • 文章目录阻塞和非阻塞IO模型阻塞 io 模型 blocking IO非阻塞 io 模型 nonblocking IOIO多路复用模型select和pollepollIO多路复用模型应用场景 阻塞和非阻塞IO模型 操作系统在处理io的时候,主要有两个阶段: 1.等待...
  • 多路复用实现分析

    千次阅读 2018-07-11 11:35:18
    多路复用实现分析select,poll,epoll都是IO多路复用的机制。所谓I/O多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但...
  • 面试之多路复用

    万次阅读 多人点赞 2018-03-27 21:07:31
    所谓I/O多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通...
  • Netty入门笔记-I/O多路复用技术

    千次阅读 2019-08-01 10:35:30
      上次博客结尾的时候简单提到了多路复用技术。在I/O编程过程中,如果需要多个客户端接入请求时,可以利用多线程或者I/O多路复用技术进行处理。I/O多路复用技术是通过把多个I/O的阻塞复用到同一个select的阻塞上,...
  • 【Linux系统编程】IO多路复用之epoll

    千次阅读 2019-11-09 20:51:39
    epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新...
  • Redis IO多路复用技术及epoll实现原理

    千次阅读 2019-09-17 22:09:27
    10、Redis IO多路复用技术以及epoll实现原理 Redis是一个单线程的但性能是非常好的内存数据库,主要用来作为缓存系统。Redis采用网络IO多路复用技术来保证在多连接的时候,系统吞吐量高。 10.1 为什么Redis要使用...
  • I/O多路复用(multiplexing)的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作 select、poll 和 ...
  • python-IO多路复用

    千次阅读 2017-04-12 17:58:54
    I/O多路复用I/O多路复用是用于提升效率,单个进程可以同时监听多个网络连接IOI/O是指Input/OutputI/O多路复用,通过一种机制,可以监视多个文件描述符,一旦描述符就绪(读就绪和写就绪),能通知程序进行相应的读写...
  • IO多路复用原理剖析

    千次阅读 2019-10-18 17:44:41
    IO多路复用原理剖析 (最近笔试遇到笔试题:select,poll,epoll都是IO多路复用的机制)。 I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的...
  • redis的多路复用原理

    千次阅读 2018-12-03 14:31:46
    这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程 采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗), 且 Redis 在内存中操作数据的速度非常快,也就是说...
  • 文章目录并发使用场景相关概念进程及特点线程及特点IO多路复用 并发使用场景 首先对不同语言使用的并发方式进行概括: 多进程 多线程 IO多路复用 Python 计算密集型 IO密集型(请求需要长期占有服务) IO...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 64,830
精华内容 25,932
关键字:

内存多路复用