Date: 2019-06-19

Author: Sun

1. Select

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

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

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

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

​ Select是一种线性扫描方式处理

​ 在python中,select函数是一个对底层操作系统的直接访问的接口。它用来监控sockets、files和pipes,等待IO完成(Waiting for I/O completion)。当有可读、可写或是异常事件产生时,select可以很容易的监控到。

​ select.select(rlist, wlist, xlist[, timeout]) 传递三个参数,一个为输入而观察的文件对象列表,一个为输出而观察的文件对象列表和一个观察错误异常的文件列表。第四个是一个可选参数,表示超时秒数。其返回3个tuple,每个tuple都是一个准备好的对象列表,它和前边的参数是一样的顺序。

2. Poll

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

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

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

3. 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

​ 在python中,select函数是一个对底层操作系统的直接访问的接口。它用来监控sockets、files和pipes,等待IO完成(Waiting for I/O completion)。当有可读、可写或是异常事件产生时,select可以很容易的监控到。

select.select(rlist, wlist, xlist[, timeout]) 传递三个参数,一个为输入而观察的文件对象列表,一个为输出而观察的文件对象列表和一个观察错误异常的文件列表。第四个是一个可选参数,表示超时秒数。其返回3个tuple,每个tuple都是一个准备好的对象列表,它和前边的参数是一样的顺序。

当我们调用select()时:

  1、上下文切换转换为内核态

  2、将fd从用户空间复制到内核空间

  3、内核遍历所有fd,查看其对应事件是否发生

  4、如果没发生,将进程阻塞,当设备驱动产生中断或者timeout时间后,将进程唤醒,再次进行遍历

  5、返回遍历后的fd

  6、将fd从内核空间复制到用户空间

通过socket建立网络连接的步骤:
至少需要2个套接字, server和client
需要建立socket之间的连接, 通过连接来进行收发data client 和 server连接的过程:
1. 建立server的套接字,绑定主机和端口,并监听client的连接请求
2. client套接字根据server的地址发出连接请求, 连接到server的socket上; client socket需要提供自己的 socket fd,以便server socket回应
3. 当server监听到client连接请求时, 响应请求, 建立一个新的线程, 把server fd 发送给client 而后, server继续监听其他client请求, 而client和server通过socket连接互发data通信

select方法

fd_r_list, fd_w_list, fd_e_list ``= 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: 超时时间

返回值:三个列表

fd_r_list: 读列表, fd_w_list:写列表, fd_e_list :异常列表

4. 案例分析

服务器端代码:

import socket, select, threading

host = socket.gethostname()
port = 5963
addr = (host, port) inputs = []
fd_name = {}
def who_in_room(w):
name_list = []
for k in w:
name_list.append(w[k])
return name_list def conn():
print('wait connecting...')
ss = socket.socket()
ss.bind(addr)
ss.listen(5)
return ss def new_coming(ss):
client, add = ss.accept()
print('欢迎 %s %s' % (client, add))
wel = '''聊天室,请输入您的名称:'''
try:
client.send(wel.encode(encoding='utf8'))
name = client.recv(1024).decode(encoding='utf8')
inputs.append(client)
fd_name[client] = name
nameList = "当前聊天室内,有如下成员: %s" % (who_in_room(fd_name))
client.send(nameList.encode(encoding='utf8'))
except Exception as e:
print(e) def server_run():
ss = conn()
inputs.append(ss) while True:
rList, wList, eList = select.select(inputs, [], [])
for temp in rList:
if temp is ss:
new_coming(ss)
else:
disconnect = False
try:
data = temp.recv(1024) #bytes
data = data.decode(encoding='utf8') #str
user_name = fd_name[temp] #bytes
data = user_name + ' say : ' + data
#data = data.decode('utf8')
except socket.error:
data = fd_name[temp] + ' leave the room'
disconnect = True
if disconnect:
inputs.remove(temp)
print(f"disconnect message:{data}")
for other in inputs:
if other != ss and other != temp:
try:
other.send(data.encode(encoding='utf8'))
except Exception as e:
print(e)
del fd_name[temp] else:
print(f"connect message: {data}")
for other in inputs:
if other != ss and other != temp:
try:
other.send(data.encode(encoding='utf8'))
except Exception as e:
print(e) if __name__ == '__main__':
server_run()

客户端代码:

# -*- coding: utf-8 -*-
__author__ = 'sun'
__date__ = '2019/6/19 15:18' import socket, select, threading, sys; host = socket.gethostname() addr = (host, 5963) def conn():
s = socket.socket()
s.connect(addr)
return s def lis(s):
my = [s]
while True:
r, w, e = select.select(my, [], [])
if s in r:
try:
print(s.recv(1024).decode(encoding='utf8'))
except socket.error:
print('socket is error')
exit() def talk(s):
while True:
try:
info = input('')
s.send(info.encode(encoding='utf8'))
except Exception as e:
print(e)
exit() def main():
ss = conn()
t = threading.Thread(target=lis, args=(ss,))
t.start()
t1 = threading.Thread(target=talk, args=(ss,))
t1.start() if __name__ == '__main__':
main()

启动一个服务器端代码,启动两个客户端代码;两个客户端进行通信,中间经过服务器进行中转。

扩展:考虑采用python配色方案来处理上述网络场景。

Select, Poll,Epoll的更多相关文章

  1. socket阻塞与非阻塞,同步与异步、I/O模型,select与poll、epoll比较

    1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步:      所谓同步,就 ...

  2. select,poll,epoll之间的区别

    (1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替.而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替, ...

  3. 理解select,poll,epoll实现分析

    mark 引用:http://janfan.cn/chinese/2015/01/05/select-poll-impl-inside-the-kernel.html 文章 select()/poll ...

  4. [转帖]select提高并发,select和poll、epoll的区别(杂)

    同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. https://www.2cto.com/kf/20161 ...

  5. select,poll,epoll,selectors

    一 了解select,poll,epoll IO复用:为了解释这个名词,首先来理解下复用这个概念,复用也就是共用的意思,这样理解还是有些抽象, 为此,咱们来理解下复用在通信领域的使用,在通信领域中为了 ...

  6. inux中,关于多路复用的使用,有三种不同的API,select、poll和epoll

    inux中,关于多路复用的使用,有三种不同的API,select.poll和epoll https://www.cnblogs.com/yearsj/p/9647135.html 在上一篇博文中提到了 ...

  7. 以python理解Linux的IO多路复用,select、poll、epoll

    题外话 之前在看Unix环境高级编程的时候,看完高级IO那一章,感觉自己萌萌哒,0.0 ,有点囫囵吞枣的感觉,之后翻了几篇博客,从纯系统的角度理解,稍微有了点概念,以这两篇为例,可以以后参考: htt ...

  8. Linux网络编程之select、poll、epoll的比较,以及epoll的水平触发(LT)和边缘触发(ET)

    Linux的网络通信先后推出了select.poll.epoll三种模式. select有以下三个问题: (1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大. ...

  9. linux开发各种I/O操作简析,以及select、poll、epoll机制的对比

    作者:良知犹存 转载授权以及围观:欢迎添加微信公众号:羽林君 IO 概念区分 四个相关概念: 同步(Synchronous) 异步( Asynchronous) 阻塞( Blocking ) 非阻塞( ...

  10. select、poll、epoll之间的区别总结

    select.poll.epoll之间的区别总结 05/05. 2014 select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪 ...

随机推荐

  1. ldap 用户组和用户(4)

    Posixgroup用户组属性 默认情况下openldap的用户组属性是Posixgroup,Posixgroup用户组属性和用户没有实际的对应关系.如果我们一定要把Posixgroup和user对应 ...

  2. 22.external version

    主要知识点 基于external version进行乐观锁并发控制 es提供了一个feature,就是说,你可以不用它提供的内部_version版本号来进行并发控制,可以基于你自己维护的一个版本号来进 ...

  3. 单例模式的理解【php】

    单例模式(Singleton Pattern):顾名思义,就是只有一个实例.作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 为什么要使用单例模式 1.P ...

  4. 【ZOJ 4070】Function and Function

    [链接] 我是链接,点我呀:) [题意] [题解] 递归一会. 会发现最后肯定是0,1一直循环. 开始循环之后就直接返回结果就好. [代码] #include <bits/stdc++.h> ...

  5. lucene_04_解析语法查询

    解析语法查询就是调用方法查询的原始查询 例如: 查询所有的查询器的语法为:*:*,因为lucene查询是根据term来做的,既是:key:value类型.*:*表示所有域中的所有值. api调用语法解 ...

  6. 通过winrm使用powershell远程管理服务器

    原文地址 在Linux中,我们可以使用安全的SSH方便的进行远程管理.但在Windows下,除了不安全的Telnet以外,从Windows Server 2008开始提供了另外一种命令行原创管理方式, ...

  7. UVA The Tower of Babylon

    The Tower of Babylon Perhaps you have heard of the legend of the Tower of Babylon. Nowadays many det ...

  8. 启动BIOS虚拟化

    启动BIOS虚拟化 学习了:https://jingyan.baidu.com/article/335530daa55d7e19cb41c3c2.html securable.exe下载地址:http ...

  9. HDU 2830 Matrix Swapping II (预处理的线性dp)

    Matrix Swapping II Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Other ...

  10. eclipse+maven的web项目访问jsp乱码

    在jsp中第一行加一句这个就不会乱码了 <%@ page language="java" import="java.util.*" pageEncodin ...