IO多路复用-select

首先列一下,sellect、poll、epoll三者的区别 
select 
select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。

select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。

另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。

poll 
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。

poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。

epoll 
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。

epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。

另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。


1

2

3

4

5

6

Windows Python:

    提供: select

Mac Python:

    提供: select

Linux Python:

    提供: select、poll、epoll

nginx:epoll

利用select监听终端操作:

#!/usr/bin/env python
#encoding: utf-8
#需linux环境
import select
import threading
import sys

while True:
    readable,writeable,error = select.select([sys.stdin,],[],[],1)
    if sys.stdin in readable:
        print ‘select get stdin‘,sys.stdin.readline()

  

利用select实现伪同时处理多个Socket客户端请求:服务端:
1、通过try异常处理实现

 
2、通过select实现。并且可以同时监听多个ip端口
#!/usr/bin/env python
#encoding: utf-8
import socket
import select
ip_port = (‘127.0.0.1‘,8888)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)
sk.setblocking(False) #是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。  

ip_port2 = (‘127.0.0.1‘,9999)
sk2 = socket.socket()
sk2.bind(ip_port2)
sk2.listen(5)
sk2.setblocking(False)

while True:
    rlist,w,e = select.select([sk,sk2],[],[],1)
    for r in rlist:
        conn,address = r.accept()
        print address

  

利用select实现伪同时处理多个Socket客户端请求:服务端



import socket
import select
ip_port = (‘127.0.0.1‘,8888)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)
sk.setblocking(False)

inputs = [sk,]
while True:
    rlist,w,e = select.select(inputs,[],[],1)
    print ‘input:‘,inputs
    print ‘resut:‘,rlist
    for r in rlist:
        #当客户端第一次连接服务器时
if r == sk:
            conn,address = r.accept()
            print address
            inputs.append(conn)
        #当客户端连上服务器之后,再次发送数据时
else:
            client_data = r.recv(1024)
            if client_data:
                r.sendall(client_data)
            else:  #如果client端断开连接,则移除inputs中的r
inputs.remove(r)
sk.close()

  

利用select实现伪同时处理多个Socket客户端请求:客户端
import socket

ip_port = (‘127.0.0.1‘,8888)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)

while True:
    inp = raw_input(‘please input:‘)
    sk.sendall(inp)
    print sk.recv(1024)
sk.close()

  

 

select实现socket服务端(接收和返回数据)

import socket
import select
ip_port = (‘127.0.0.1‘,8888)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)
sk.setblocking(False)

inputs = [sk,]
outputs = []
while True:
    rlist,wlist,e = select.select(inputs,outputs,inputs,1)
    for r in rlist:
        if r == sk:
            conn,address = r.accept()
            inputs.append(conn)
        else:
            client_data = r.recv(1024)
            if client_data:
                outputs.append(r)
    for w in wlist:
        w.sendall(‘123‘)
        outputs.remove(w)
sk.close()

  

 

select实现socket服务端六(分离读写操作实现)

###########server端##################
import socket
import select
import Queue
ip_port = (‘127.0.0.1‘,8888)
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   #创建一个socket
server.bind(ip_port)
server.listen(5)
server.setblocking(0)                                       #非阻塞

inputs = [ server ]
outputs = []
messages = {}

while inputs:
    rlist,wlist,elist = select.select(inputs,outputs,inputs)
    #rlist读,wlist写,elist错误
for r in rlist:
        if r is server:
            conn,address = r.accept()
            conn.setblocking(0)
            inputs.append(conn)
            messages[conn] = Queue.Queue()
        else:
            client_data = r.recv(1024)
            if client_data:
                messages[r].put(client_data)
                if r not in outputs:
                    outputs.append(r)
            else:
                if r in outputs:
                    outputs.remove(r)
                inputs.remove(r)
                r.close()
    for w in wlist:
        try:
            data = messages[w].get_nowait()
        except Queue.Empty:
            outputs.remove(w)
        else:
            w.send(data)
    for e in elist:
        inputs.remove(e)
        if e in outputs:
            outputs.remove(e)
        e.close()
        del messages[e]

  

###########client端##################
import socket
import sys
#同时要发送的内容
messages = [ ‘This is the message. ‘,
‘It will be sent ‘,
‘in parts.‘,
]
server_address = (‘127.0.0.1‘, 8888)

#同时建立2个socket,实现并发
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
]
print >>sys.stderr, ‘connecting to %s port %s‘ % server_address
for s in socks:
    s.connect(server_address)
for message in messages:
    for s in socks:
        print >>sys.stderr, ‘%s: sending "%s"‘ % (s.getsockname(), message)
        s.send(message)
    for s in socks:
        data = s.recv(1024)
        print >>sys.stderr, ‘%s: received "%s"‘ % (s.getsockname(), data)
        if not data:
            print >>sys.stderr, ‘closing socket‘, s.getsockname()
            s.close()

  

 

 

 

 

 

 

 

 

 

 

 

 

来自为知笔记(Wiz)

时间: 2024-12-09 21:33:31

IO多路复用-select的相关文章

非阻塞io模型和io多路复用----select

一.四种io阻塞1.io阻塞:(1 等待数据处于阻塞状态(2从内核copy到用户态处于阻塞状态2.非io阻塞只有从内核copy到用户态处于阻塞状态3.io多路复用----->优势:可以同时监听多个对象(1从check----->ready 通过selec函数来做,处于阻塞状态(2从内核copy到用户态处于阻塞状态3.异步io不用阻塞二.io多路复用select  poll epoll 都属于io同步里面的io多路复用select:轮询问题,监听数量有限poll:提高了监听数量epoll:解决了

IO多路复用——select

IO多路复用 是同步IO的一种,用一个进程一次等待多个IO就绪事件的发生,加大概率,尽可能高效的等. 适用场景 (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用. (2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现. (3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用. (4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用. (5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O

转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的.下面记录下分别基于Select/Poll/Epoll的echo server实现.Python Select Server,可监控事件数量有限制: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

IO多路复用select、poll、epoll的区别

(1)select==>时间复杂度O(n) 它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作.所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长. (2)poll==>时间复杂度O(n) poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的

Python IO多路复用select模块

多路复用的分析实例:服务端.客户端 #服务端配置 from socket import * import time import select server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1',8080)) server.listen(5) server.setblocking(False) ''' select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接. 当任何一个socket中的数据准

python IO 多路复用 select poll epoll

三个多路复用模型的概念 select select 原理 select 是通过系统调用来监视着一个由多个文件描述符(file descriptor)组成的数组,当select()返回后,数组中就绪的文件描述符会被内核修改标记位(其实就是一个整数),使得进程可以获得这些文件描述符从而进行后续的读写操作.select饰通过遍历来监视整个数组的,而且每次遍历都是线性的. select 优点 select目前几乎在所有的平台上支持,良好跨平台性. select 缺点 每次调用select,都需要把fd集

IO多路复用深入浅出

前言 从零单排高性能问题,这次轮到异步通信了.这个领域入门有点难,需要了解UNIX五种IO模型和 TCP协议,熟练使用三大异步通信框架:Netty.NodeJS.Tornado.目前所有标榜异步的通信框架用的都不是异步IO模型,而是IO多路复 用中的epoll.因为Python提供了对Linux内核API的友好封装,所以我选择Python来学习IO多路复用. IO多路复用 select 举一个EchoServer的例子,客户端发送任何内容,服务端会原模原样返回. #!/usr/bin/env p

IO模型--阻塞IO,非阻塞IO,IO多路复用,异步IO

IO模型介绍: * blocking IO 阻塞IO * nonblocking IO 非阻塞IO * IO multiplexing IO多路复用 * signal driven IO 信号驱动IO () * asynchronous IO 异步IO IO模型介绍: 为了更好地了解IO模型,我们需要事先回顾下:同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,

IO多路复用之select

基本概念 IO多路复用是指内核(线性扫描)一旦发现进程指定的一个或者多个IO条件准备就绪,它就通知该进程,执行定义的操作. 适用场景 1.当客户处理多个描述符时(一般是交互式输入和网络套接字),必须使用I/O复用. 2.当一个客户同时处理多个套接字时,而这种情况是可能的,但很少出现. 3.如果一个TCP服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到I/O复用. 4.如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用. 5.如果一个服务器要处理多个服务或多个协议,一般