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. keycode键盘 按键 - 键码 对应表

    字母和数字键的键码值(keyCode) 按键 键码 按键 键码 按键 键码 按键 键码 A 65 J 74 S 83 1 49 B 66 K 75 T 84 2 50 C 67 L 76 U 85 3 ...

  2. 【习题 4-4 UVA - 253】Cube painting

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 绕(x,y,z)三个轴旋转. 枚举x,y,z各4次的结果. (4次之后能还原.可以方便上一层枚举下一个情况.) [代码] #incl ...

  3. uva 11624

    #include<stdio.h> #include<string.h> #include<queue> using namespace std; #define ...

  4. 楼控-西门子-insight使用-软件重新授权

    在insight的使用中,可能会碰到insight的软件授权失败,此时需要重新将原授权删除,再重新将软件授权. 先看一下软件授权失效会出现什么: 再看看如何删除原来的密钥,再注册新的密钥.

  5. [bzoj1180][CROATIAN2009]OTOCI_LCT

    OTOCI bzoj-1180 CROATIAN-2009 题目大意:给你n个离散的点,m个操作.支持:两点加边(保证还是森林),修改单点权值,询问两点是否联通,查询联通两点之间路径权值. 注释:$1 ...

  6. Hadoop作业性能指标及參数调优实例 (三)Hadoop作业性能參数调优方法

    作者: Shu, Alison Hadoop作业性能调优的两种场景: 一.用户观察到作业性能差,主动寻求帮助. (一)eBayEagle作业性能分析器 1. Hadoop作业性能异常指标 2. Had ...

  7. Linux - 理不清的权限chmod与chown区别

    chmod是修改第一列内容的 ,chown是修改第3,4列内容的. [root@local ~]# chmod 777 -R add.sh [root@local ~]# chown jiqing:j ...

  8. spark rdd median 中位数求解

    lookup(key) Return the list of values in the RDD for key key. This operation is done efficiently if ...

  9. 辨异 —— Java 中的抽象类和接口

    接口优于抽象类.-- <Effective Java>(Item 18) 0. 语法区别 抽象类允许给出某些方法的实现,接口不允许: 为了实现由抽象类定义的类型(type),类必须成为抽象 ...

  10. HDU2080 夹角有多大2

    2019-05-17 15:00:09 加油加油,fightting !!! 这道题不知道acos()函数,acos()返回的是弧度,转化成度数要 / PI * 180 也没有想到通过向量 但是想到了 ...