概念:

首先列一下,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()时便得到通知。

select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

这里玩一下select和poll的实例,不过这个例子对于理解异步和解决问题的办法都会有很大的帮助。一个串行的方法怎么可能实现并行的效果那?

 #!/usr/bin/env python
# -*- coding: utf-8 -*- import select
import socket
import sys
import Queue server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#socket本身是串行处理数据的,当A连接的时候B是无法进行"连接"的,A的会话完毕才可以进行B的连接。如果加上setblocking(0) B用户可以连接,但是也必须等待A处理完事才能处理B
#的事情。
server.setblocking(0)
server_address=('10.64.8.92',10000)
print >> sys.stderr,"starting up !!!!!"
server.bind(server_address) #A连接后还可以在连接4个人,在多的人就无法连接。一共5个人可以排队
server.listen(5) '''
select 有三个表(自定义),input接受消息,output 回消息,error 错误信息
select,回循环遍历每一个列表。不停的遍历默认1024文件描述符。不管有没有对象真的占用描述符都会一直循环,并且一个对象都没有的时候
将进入阻塞状态
'''
#这里必须这样写把server放进去,新的连接信息其实并不会加入这个列表中去。这个列表始终只有server
inputs=[server] #这个链列表是存储要发消息的连接对象
outputs=[] #这个队列是存放每个对象发过来的消息
message_queues={} #开始循环监听
while inputs:
print >>sys.stderr, '\nwaiting for the next event'
#inputs列表只会有server,个人理解select.select就是监控server 文件描述符是否有变化
readable, writable, exceptional = select.select(inputs, outputs, inputs) #开始循环readable 只要有链接对象过来那么这里就开始执行。
for s in readable:
#注意这里只有新创建的时候才为true,当连接之后在发消息过来这里为false(如果中途断开重新连接那算新的连接)
if s is server:
print "s:",s
print "server:",server
print "readable:",readable
print "ser filen:",server.fileno()
print "s filen:",s.fileno()
# A "readable" server socket is ready to accept a connection
#connection这里我理解就是socket已经分配了一个文件描述符
connection, client_address = s.accept()
print >>sys.stderr, 'new connection from', client_address
#这里的意思可能是不阻塞其它对象连接这个server,把这个注释掉也没有阻塞新客户端连接
connection.setblocking(0)
#将套接字对象放到inputs列表中,inputs过程就完事了
inputs.append(connection) # Give the connection a queue for data we want to send
#将套接字注册一个队列,存储消息用。就是通过这个队列的方法(先进先出)将消息发给接受的对象
message_queues[connection] = Queue.Queue()
else:#这个地方就是套接字对象已经在inpus列表中存在了
data = s.recv(1024)
if data:
# A readable client socket has data
print >>sys.stderr, 'received "%s" from %s' % (data, s.getpeername())
#将消息放到对应的套接字队列里面
message_queues[s].put(data)
# Add output channel for response 将套接如果不在outputs队列中那么就追加进去
if s not in outputs:
outputs.append(s)
else:
# Interpret empty result as closed connection
print >>sys.stderr, 'closing', client_address, 'after reading no data'
# Stop listening for input on the connection
if s in outputs:
outputs.remove(s) #既然客户端都断开了,我就不用再给它返回数据了,所以这时候如果这个客户端的连接对象还在outputs列表中,就把它删掉
inputs.remove(s) #inputs中也删除掉
s.close() #把这个连接关闭掉 # Remove message queue
del message_queues[s] #这里循环writable,就是处理output队列
for s in writable:
try:
#取套接字对象下的队列消息
next_msg = message_queues[s].get_nowait()
except Queue.Empty:
# No messages waiting so stop checking for writability.
print >>sys.stderr, 'output queue for', s.getpeername(), 'is empty'
outputs.remove(s)
else:#当消息没有报错那就说明队列中有消息,这时把消息发送出去,这个消息发给谁s中有记录所以不怕找不到人
print >>sys.stderr, 'sending "%s" to %s' % (next_msg, s.getpeername())
s.send(next_msg)
#这里循环出错列表,将出错的套接字在三个列表和队列中全部清除掉
for s in exceptional:
print >>sys.stderr, 'handling exceptional condition for', s.getpeername()
# Stop listening for input on the connection
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close() # Remove message queue
del message_queues[s]

poll的方法 跟select差不多只不过列表不用大家自己创建了

poll flags 状态,其实这个就相当于select自己建立的列表。poll就是不断的改变flags 状态然后做出相应的动作

 #!/usr/bin/env python
# -*- coding: utf-8 -*-
import select
import socket
import sys
import Queue # Create a TCP/IP socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0) # Bind the socket to the port
server_address = ('10.64.8.92', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
server.bind(server_address) # Listen for incoming connections
server.listen(5) # Keep up with the queues of outgoing messages
message_queues = {} TIMEOUT = 1000.
# Commonly used flag setes
READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR
READ_WRITE = READ_ONLY | select.POLLOUT poller = select.poll()
#注册下服务器,当收到新连接的时候出发事件
poller.register(server, READ_ONLY) # 返回文件描述符和对象的一个映射关系
fd_to_socket = { server.fileno(): server,
}
while True: # Wait for at least one of the sockets to be ready for processing
print >>sys.stderr, '\nwaiting for the next event'
events = poller.poll(TIMEOUT) for fd, flag in events: # 检测套接字文件描述符
s = fd_to_socket[fd]
if flag & (select.POLLIN | select.POLLPRI):#新连接和消息接受在这里处理
if s is server:
# "readable" 开始接受新的连接
connection, client_address = s.accept()
print >>sys.stderr, 'new connection from', client_address
connection.setblocking(0)
fd_to_socket[ connection.fileno() ] = connection
poller.register(connection, READ_ONLY)
# Give the connection a queue for data we want to send
message_queues[connection] = Queue.Queue()
else:#如果不是新的连接这里传递过来的一定是数据。这里就接受一下
data = s.recv(1024)
if data:#
# A readable client socket has data
print >>sys.stderr, 'received "%s" from %s' % (data, s.getpeername())
message_queues[s].put(data)
# Add output channel for response,,修改状态 这个就是select中的output列表
poller.modify(s, READ_WRITE)
else:
# Interpret empty result as closed connection
print >>sys.stderr, 'closing', client_address, 'after reading no data'
# Stop listening for input on the connection
poller.unregister(s)
s.close() # Remove message queue
del message_queues[s]
elif flag & select.POLLHUP: #客户端挂了或者客户端关闭了
# Client hung up
print >>sys.stderr, 'closing', client_address, 'after receiving HUP'
# Stop listening for input on the connection
poller.unregister(s)
s.close()
elif flag & select.POLLOUT:#回复数据
# Socket is ready to send data, if there is any to send.
try:
next_msg = message_queues[s].get_nowait()
except Queue.Empty:
# No messages waiting so stop checking for writability.
print >>sys.stderr, 'output queue for', s.getpeername(), 'is empty'
poller.modify(s, READ_ONLY)
else:
print >>sys.stderr, 'sending "%s" to %s' % (next_msg, s.getpeername())
s.send(next_msg)
elif flag & select.POLLERR:
print >>sys.stderr, 'handling exceptional condition for', s.getpeername()
# Stop listening for input on the connection
poller.unregister(s)
s.close() # Remove message queue
del message_queues[s]

客户端连接代码:这个手动测试的代码方便理解异步中的每个步骤

 #!/usr/bin/env python
import socket
import sys HOST, PORT = "10.64.8.92",10000 # Create a socket (SOCK_STREAM means a TCP socket)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
while 1:
# Connect to server and send data
data=raw_input("echo server:")
sock.sendall(bytes(data)) # Receive data from the server and shut down
received = str(sock.recv(1024))
print received

原文:

https://pymotw.com/2/select/

python 异步 select pooll epoll的更多相关文章

  1. python的select和epoll

    python的select和epoll 1.select模型: linux中每个套接字都是文件,都有唯一的文件描述符,这些设备的文件描述符被放在一个数组中,然后select调用的时候遍历这个数组,如果 ...

  2. IO模型与select,poll,epoll

    五种:阻塞,非阻塞,IO复印,信号驱动,异步. select,poll,epoll select: 典型用32个32位的整数表示1024个描述符,并发的局限. poll:功能同上,但数据结构不一样(链 ...

  3. Python之路-python(Queue队列、进程、Gevent协程、Select\Poll\Epoll异步IO与事件驱动)

    一.进程: 1.语法 2.进程间通讯 3.进程池 二.Gevent协程 三.Select\Poll\Epoll异步IO与事件驱动 一.进程: 1.语法 简单的启动线程语法 def run(name): ...

  4. Python自动化 【第十篇】:Python进阶-多进程/协程/事件驱动与Select\Poll\Epoll异步IO

    本节内容: 多进程 协程 事件驱动与Select\Poll\Epoll异步IO   1.  多进程 启动多个进程 进程中启进程 父进程与子进程 进程间通信 不同进程间内存是不共享的,要想实现两个进程间 ...

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

    下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...

  6. Python异步非阻塞IO多路复用Select/Poll/Epoll使用,线程,进程,协程

    1.使用select模拟socketserver伪并发处理客户端请求,代码如下: import socket import select sk = socket.socket() sk.bind((' ...

  7. python开发学习-day10(select/poll/epoll回顾、redis、rabbitmq-pika)

    s12-20160319-day10 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...

  8. Select/Poll/Epoll异步IO

    IO多路复用 同步io和异步io,阻塞io和非阻塞io分别是什么,有什么样的区别? io模式 对于一次io 访问(以read为例),数据会先拷贝到操作系统内核的缓冲区,然后才会从操作系统内核的缓冲区拷 ...

  9. Select\Poll\Epoll异步IO与事件驱动

    事件驱动与异步IO 事件驱动编程是一种编程规范,这里程序的执行流由外部事件来规定.它的特点是包含一个事件循环,但外部事件发生时使用回调机制来触发响应的处理.另外两种常见的编程规范是(单线程)同步以及多 ...

随机推荐

  1. Python学习笔记(二十)调试

    摘抄自: https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143191557 ...

  2. NYOJ 1272 表达式求值 第九届省赛 (字符串处理)

    title: 表达式求值 第九届省赛 nyoj 1272 tags: [栈,数据结构] 题目链接 描述 假设表达式定义为: 1. 一个十进制的正整数 X 是一个表达式. 2. 如果 X 和 Y 是 表 ...

  3. 如何关闭sublime更新提示

    前提:sublime已激活

  4. hdu 2063 过山车 二分匹配(匈牙利算法)

    简单题hdu2063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2063 过山车 Time Limit: 1000/1000 MS (Java/Ot ...

  5. Coursera在线学习---第四节.过拟合问题

    一.解决过拟合问题方法 1)减少特征数量 --人为筛选 --靠模型筛选 2)正则化(Regularization) 原理:可以降低参数Θ的数量级,使一些Θ值变得非常之小.这样的目的既能保证足够的特征变 ...

  6. 利用Jsoup模拟跳过登录爬虫获取数据

    今天在学习爬虫的时候想着学习一下利用jsoup模拟登录.下面分为有验证码和无验证码的情况进行讨论. ---------------------------无验证码的情况---------------- ...

  7. perl6 登录phpmyadmin

    use HTTP::UserAgent; my $ua = HTTP::UserAgent.new; my $url = 'http://localhost/phpMyAdmin/index.php' ...

  8. oracle命令生成AWR报告

    --命令生成AWR报告oracle@linux:~> sqlplus / as sysdba SQL*Plus: Release 11.1.0.7.0 - Production on Fri A ...

  9. Android仿新浪新闻SlidingMenu界面的实现 .

    先看看原图: 如图所示,这种侧滑效果以另一种方式替代了原先tab导航的那种用户体验方式 给人耳目一新的感觉,现已被广大知名应用所效仿,如新浪新闻,网易新闻,人人网等 那么这种效果该如何实现呢?那就需要 ...

  10. C/C++——C语言库函数大全

    本文转载自:https://blog.csdn.net/yanfan0916/article/details/6450442###; 1. 分类函数: ctype.h  int isalpha(int ...