1、select模块介绍
Python中的select模块专注于I/O多路复用,select方法用来监听文件描述符(fd),当没有文件描述符时间发生时,进程被阻塞;当一个或多个描述符实际发生时,进程被唤醒。
当我们调用select()时:
1、上下文切换转换为内核态
2、将fd从用户空间复制到内核空间
3、内核遍历所有fd,查看其对应事件是否发生
4、如果没发生,将进程阻塞,当设备驱动产生中断或者timeout时间后,将进程唤醒,再次进行遍历
5、返回遍历后的fd
6、将fd从内核空间复制到用户空间
select模块以列表形式接受四个参数,分别是需要监听的可读文件对象,可写文件对象,产生异常的文件对象和超时设置,当某个文件描述符状态改变后,会返回三个列表。
具体实现:
fd_rlist,fd_wlist,fd_elist=select.select(rlist, wlist, xlist, [timeout]))
参数: 可接受四个参数(前三个必须)
rlist: wait until ready for reading
wlist: wait until ready for writing
xlist: wait for an “exceptional condition”
timeout: 超时时间
返回值:三个列表:
1、当参数1 序列中的fd满足“可读”条件时,则获取发生变化的fd并添加到fd_rlist中
2、当参数2 序列中含有fd时,则将该序列中所有的fd添加到 fd_wlist中
3、当参数3 序列中的fd发生错误时,则将该发生错误的fd添加到 fd_elist中
4、当超时时间为空,则select会一直阻塞,直到监听的句柄发生变化
当超时时间 = n(正整数)时,那么如果监听的句柄均无任何变化,则select会阻塞n秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。
2、select模块实现并发的服务端:
服务端:
importsocketimportselect
s=socket.socket()
s.bind('127.0.0.1','8080')
s.listen(5)
r_list=[s,]
num=0whileTrue:
r1,w1,error=select.select(r_list,[],[],5)
num+=1
print('count is %s'%num)print('r1's length is %s'%len(r1))
for fd inr1:if fd==s:
conn,addr=fd.accept()
r_list.append(conn)
msg=conn.recv(200)
conn.sendall(('first----%s'%conn.fileno()).encode())else:try:
msg=fd.recv(200)
fd.sendall('second'.encode())exceptConnectionAbortedError:
r_list.remove(fd)
s.close()
客户端
importsocket
flag=1s=socket.socket()
s.connect('127.0.0.1','8080')whileflag:
input_msg=input('请输入:')if input_msg=='0':breaks.sendall(input_msg.encode())
msg=s.recv(1024)print(msg.decode())
s.close()
在服务端我们可以看到,我们需要不停的调用select, 这就意味着:
1 当文件描述符过多时,文件描述符在用户空间与内核空间进行copy会很费时
2 当文件描述符过多时,内核对文件描述符的遍历也很浪费时间
3 select最大仅仅支持1024个文件描述符