- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章深入理解python中的select模块由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
简介 。
Python中的select模块专注于I/O多路复用,提供了select poll epoll三个方法(其中后两个在Linux中可用,windows仅支持select),另外也提供了kqueue方法(freeBSD系统) 。
select方法 。
进程指定内核监听哪些文件描述符(最多监听1024个fd)的哪些事件,当没有文件描述符事件发生时,进程被阻塞;当一个或者多个文件描述符事件发生时,进程被唤醒.
当我们调用select()时:
1、上下文切换转换为内核态 。
2、将fd从用户空间复制到内核空间 。
3、内核遍历所有fd,查看其对应事件是否发生 。
4、如果没发生,将进程阻塞,当设备驱动产生中断或者timeout时间后,将进程唤醒,再次进行遍历 。
5、返回遍历后的fd 。
6、将fd从内核空间复制到用户空间 。
fd:file descriptor 文件描述符 。
1
|
fd_r_list, fd_w_list, fd_e_list
=
select.select(rlist, wlist, xlist, [timeout])
|
参数: 可接受四个参数(前三个必须) 。
返回值:三个列表 。
select方法用来监视文件描述符(当文件描述符条件不满足时,select会阻塞),当某个文件描述符状态改变后,会返回三个列表 。
1、当参数1 序列中的fd满足“可读”条件时,则获取发生变化的fd并添加到fd_r_list中 。
2、当参数2 序列中含有fd时,则将该序列中所有的fd添加到 fd_w_list中 。
3、当参数3 序列中的fd发生错误时,则将该发生错误的fd添加到 fd_e_list中 。
4、当超时时间为空,则select会一直阻塞,直到监听的句柄发生变化 。
当超时时间 = n(正整数)时,那么如果监听的句柄均无任何变化,则select会阻塞n秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行.
实例:利用select实现一个可并发的服务端 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
import
socket
import
select
s
=
socket.socket()
s.bind((
'127.0.0.1'
,
8888
))
s.listen(
5
)
r_list
=
[s,]
num
=
0
while
True
:
rl, wl, error
=
select.select(r_list,[],[],
10
)
num
+
=
1
print
(
'counts is %s'
%
num)
print
(
"rl's length is %s"
%
len
(rl))
for
fd
in
rl:
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())
except
ConnectionAbortedError:
r_list.remove(fd)
s.close()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
socket
flag
=
1
s
=
socket.socket()
s.connect((
'127.0.0.1'
,
8888
))
while
flag:
input_msg
=
input
(
'input>>>'
)
if
input_msg
=
=
'0'
:
break
s.sendall(input_msg.encode())
msg
=
s.recv(
1024
)
print
(msg.decode())
s.close()
|
在服务端我们可以看到,我们需要不停的调用select, 这就意味着:
1 当文件描述符过多时,文件描述符在用户空间与内核空间进行copy会很费时 。
2 当文件描述符过多时,内核对文件描述符的遍历也很浪费时间 。
3 select最大仅仅支持1024个文件描述符 。
poll与select相差不大,本文不作介绍 。
epoll方法:
epoll很好的改进了select:
1、epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时,会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次.
2、epoll会在epoll_ctl时把指定的fd遍历一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd 。
3、epoll对文件描述符没有额外限制 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
select.epoll(sizehint
=
-
1
, flags
=
0
) 创建epoll对象
epoll.close()
Close the control
file
descriptor of the epoll
object
.关闭epoll对象的文件描述符
epoll.closed
True
if
the epoll
object
is
closed.检测epoll对象是否关闭
epoll.fileno()
Return the
file
descriptor number of the control fd.返回epoll对象的文件描述符
epoll.fromfd(fd)
Create an epoll
object
from
a given
file
descriptor.根据指定的fd创建epoll对象
epoll.register(fd[, eventmask])
Register a fd descriptor with the epoll
object
.向epoll对象中注册fd和对应的事件
epoll.modify(fd, eventmask)
Modify a registered
file
descriptor.修改fd的事件
epoll.unregister(fd)
Remove a registered
file
descriptor
from
the epoll
object
.取消注册
epoll.poll(timeout
=
-
1
, maxevents
=
-
1
)
Wait
for
events. timeout
in
seconds (
float
)阻塞,直到注册的fd事件发生,会返回一个
dict
,格式为:{(fd1,event1),(fd2,event2),……(fdn,eventn)}
|
事件:
1
2
3
4
5
6
7
8
9
10
11
12
|
EPOLLIN Available
for
read 可读 状态符为
1
EPOLLOUT Available
for
write 可写 状态符为
4
EPOLLPRI Urgent data
for
read
EPOLLERR Error condition happened on the assoc. fd 发生错误 状态符为
8
EPOLLHUP Hang up happened on the assoc. fd 挂起状态
EPOLLET
Set
Edge Trigger behavior, the default
is
Level Trigger behavior 默认为水平触发,设置该事件后则边缘触发
EPOLLONESHOT
Set
one
-
shot behavior. After one event
is
pulled out, the fd
is
internally disabled
EPOLLRDNORM Equivalent to EPOLLIN
EPOLLRDBAND Priority data band can be read.
EPOLLWRNORM Equivalent to EPOLLOUT
EPOLLWRBAND Priority data may be written.
EPOLLMSG Ignored.
|
水平触发和边缘触发:
Level_triggered(水平触发,有时也称条件触发):当被监控的文件描述符上有可读写事件发生时,epoll.poll()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll.poll()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!! 优点很明显:稳定可靠 。
Edge_triggered(边缘触发,有时也称状态触发):当被监控的文件描述符上有可读写事件发生时,epoll.poll()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll.poll()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!缺点:某些条件下不可靠 。
epoll实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import
socket
import
select
s
=
socket.socket()
s.bind((
'127.0.0.1'
,
8888
))
s.listen(
5
)
epoll_obj
=
select.epoll()
epoll_obj.register(s,select.EPOLLIN)
connections
=
{}
while
True
:
events
=
epoll_obj.poll()
for
fd, event
in
events:
print
(fd,event)
if
fd
=
=
s.fileno():
conn, addr
=
s.accept()
connections[conn.fileno()]
=
conn
epoll_obj.register(conn,select.EPOLLIN)
msg
=
conn.recv(
200
)
conn.sendall(
'ok'
.encode())
else
:
try
:
fd_obj
=
connections[fd]
msg
=
fd_obj.recv(
200
)
fd_obj.sendall(
'ok'
.encode())
except
BrokenPipeError:
epoll_obj.unregister(fd)
connections[fd].close()
del
connections[fd]
s.close()
epoll_obj.close()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
socket
flag
=
1
s
=
socket.socket()
s.connect((
'127.0.0.1'
,
8888
))
while
flag:
input_msg
=
input
(
'input>>>'
)
if
input_msg
=
=
'0'
:
break
s.sendall(input_msg.encode())
msg
=
s.recv(
1024
)
print
(msg.decode())
s.close()
|
总结 。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我的支持.
最后此篇关于深入理解python中的select模块的文章就讲到这里了,如果你想了解更多关于深入理解python中的select模块的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我最近在我的机器上安装了 cx_Oracle 模块,以便连接到远程 Oracle 数据库服务器。 (我身边没有 Oracle 客户端)。 Python:版本 2.7 x86 Oracle:版本 11.
我想从 python timeit 模块检查打印以下内容需要多少时间,如何打印, import timeit x = [x for x in range(10000)] timeit.timeit("
我盯着 vs 代码编辑器上的 java 脚本编码,当我尝试将外部模块包含到我的项目中时,代码编辑器提出了这样的建议 -->(文件是 CommonJS 模块;它可能会转换为 ES6 模块。 )..有什么
我有一个 Node 应用程序,我想在标准 ES6 模块格式中使用(即 "type": "module" in the package.json ,并始终使用 import 和 export)而不转译为
我正在学习将 BlueprintJS 合并到我的 React 网络应用程序中,并且在加载某些 CSS 模块时遇到了很多麻烦。 我已经安装了 npm install @blueprintjs/core和
我需要重构一堆具有这样的调用的文件 define(['module1','module2','module3' etc...], function(a, b, c etc...) { //bun
我是 Angular 的新手,正在学习各种教程(Codecademy、thinkster.io 等),并且已经看到了声明应用程序容器的两种方法。首先: var app = angular.module
我正在尝试将 OUnit 与 OCaml 一起使用。 单元代码源码(unit.ml)如下: open OUnit let empty_list = [] let list_a = [1;2;3] le
我在 Angular 1.x 应用程序中使用 webpack 和 ES6 模块。在我设置的 webpack.config 中: resolve: { alias: { 'angular':
internal/modules/cjs/loader.js:750 return process.dlopen(module, path.toNamespacedPath(filename));
在本教程中,您将借助示例了解 JavaScript 中的模块。 随着我们的程序变得越来越大,它可能包含许多行代码。您可以使用模块根据功能将代码分隔在单独的文件中,而不是将所有内容都放在一个文件
我想知道是否可以将此代码更改为仅调用 MyModule.RED 而不是 MyModule.COLORS.RED。我尝试将 mod 设置为变量来存储颜色,但似乎不起作用。难道是我方法不对? (funct
我有以下代码。它是一个 JavaScript 模块。 (function() { // Object var Cahootsy; Cahootsy = { hello:
关闭。这个问题是 opinion-based 。它目前不接受答案。 想要改进这个问题?更新问题,以便 editing this post 可以用事实和引文来回答它。 关闭 2 年前。 Improve
从用户的角度来看,一个模块能够通过 require 加载并返回一个 table,模块导出的接口都被定义在此 table 中(此 table 被作为一个 namespace)。所有的标准库都是模块。标
Ruby的模块非常类似类,除了: 模块不可以有实体 模块不可以有子类 模块由module...end定义. 实际上...模块的'模块类'是'类的类'这个类的父类.搞懂了吗?不懂?让我们继续看
我有一个脚本,它从 CLI 获取 3 个输入变量并将其分别插入到 3 个变量: GetOptions("old_path=s" => \$old_path, "var=s" =
我有一个简单的 python 包,其目录结构如下: wibble | |-----foo | |----ping.py | |-----bar | |----pong.py 简单的
这种语法会非常有用——这不起作用有什么原因吗?谢谢! module Foo = { let bar: string = "bar" }; let bar = Foo.bar; /* works *
我想运行一个命令: - name: install pip shell: "python {"changed": true, "cmd": "python <(curl https://boot
我是一名优秀的程序员,十分优秀!