IO多路复用
- 大多数操作系统都是支持select和poll
- Linux2.5+ 支持epoll
- BSD、Mac支持kequeue
- Windows的IOCP
Python的select库
实现了select、poll系统调用,这个基本上操作系统都是支持。部分实现了epoll。
底层的IO多路复用模块开发中的选择
1、完全跨平台、使用select、poll。但是性能较差
2、针对不同操作系统自行选择支持的技术,这样做会提高IO的处理能力
Selects库、执行选择支持的IO技术
3.4版本提供这个库,高级IO复用库
#类层结构 BaseSelector + ---- SelectSelector 实现Select + ---- PollSelector 实现poll + ---- EpollSelector 实现epoll + ---- DevpollSelector 实现devpoll + ---- KqueueSelector 实现kqueue
selectors.DefaultSelector 返回当前平台最有效、性能最高的实现。但是,由于没有实现Windows下的IOCP,所以退化为select
#在select模块源码最下面有如下代码 # 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
abstractmethod.register(fileobj,events,data=None):为select注册一个文件对象,监控它的IO事件。
- fileobj:被监控文件对象,例如socket对象
- events事件,该文件对象必须等待的事件
- data可选的与此文件对象相关联的不透明数据,例如:关联用来存储每个客户端的会话ID,关联方法。通过这个参数在关注的事件产生后让select干什么事
Event常量 含义 EVENT_READ 可读ob01,内核已经准备好输入输出设备,可以开始读了 EVENT_WRITE 可写ob10,内核准备好了,可以往里写了 代码
import selectors import threading import socket import logging FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s" logging.basicConfig(format=FORMAT,level=logging.INFO) #构建缺省性最优selector selector = selectors.DefaultSelector() #回调函数,自己定义形参 def accept(sock:socket.socket ): "mask : 事件掩码的或值" conn , cleint = sock.accept() #阻塞等待连接进来 conn.setblocking(False) #准备为每一个客户端建立单独的sock对象 #监视conn这个文件对象 selector.register(conn,selectors.EVENT_READ,recv) logging.info(key) #回调函数 def recv(conn:socket.socket): data = conn.recv(1024) msg = "Your msg is {}".format(data.decode()) conn.send(msg.encode()) #创建TCP连接 sock = socket.socket() #创建socket对象 sock.bind(("127.0.0.1",9990)) sock.listen() logging.info(sock) sock.setblocking(False) #非阻塞 e = threading.Event() print(threading.enumerate()) #注册selector 中监听的对象,事件和回调的data,返回selecttorKey #将sock、关注事件、data都绑定到key实例属性上 key = selector.register(sock,selectors.EVENT_READ,accept) logging.info(key) print(key) def select(e): while not e.is_set(): #开始监听,等到有文件对象监控事件产生,返回(key,mask)元组 events = selector.select() if events: print("1111",events) for key ,mask in events: call_back = key.data #accept 或者 recv call_back(key.fileobj) print("kkkkkkkk",key,"mmmmmm",mask) threading.Thread(target=select,args=(e,),name="select").start() def main(): while not e.is_set(): cmd = input(">>>") if cmd.strip() == "quit": e.set() fobjs = [] logging.info("{}".format(selector.get_map().items())) for fd,key in selector.get_map().items(): print(22222222222222222,fd,key) print(key.fileobj) fobjs.append(key.fileobj) for bobj in fobjs: selector.unregister(bobj) bobj.close() #关闭socket selector.close() if __name__ == '__main__': main()