事件,IO,select
事件驱动模型
对于普通编程来说,代码遵循线性流程:开始--》代码A--》代码B--》代码C--》。。。--》结束,编程者知道代码的运行顺序,由编程者控制
事件驱动模型,流程则是:开始--》初始化--》等待,这个等待不同于常规编程的等待,如input(),强制需要用户输入某种数据。
事件驱动模型的等待,是不知道的,不需要强制用户输入,而是当某个事件发生,程序就会做出反应,这些事件包括鼠标点击,信息输入,键盘固定设置的键等
对于编写服务器处理模型的程序,有3种方式
1.每接到一个请求,创建一个线程处理
2.每接到一个请求,创建一个进程处理
3.每接到一个请求,放到一个事件列表中,让主进程通过IO阻塞的方式处理
第三种就是协程、事件驱动模型方式,大多网络服务器都是采用这种方式
例:
对于UI编程来说,常常对鼠标点击要做出反应,如果采用创建一个线程循环检测是否有鼠标点击,则会有很多弊端
如:CPU资源的浪费,阻塞情况下,其他的情况的检测,将无法进行,监测多个设备时,会造成响应时间延迟
所以对UI编程来说,都会采用事件驱动模型,如很多平台都会提供onClick()事件
事件驱动模型的大致流程如下:
1.有一个事件队列
2.当发生一个事件时,向列表中增加这个事件
3.有一个循环,不断从队列中取出事件,根据事件的不同,调用不同的函数
4.事件都各自保存自己的函数指针,这样,每个事件都有自己的处理函数

事件驱动模型编程是一种编程范式,这里的执行流由外部的事件决定,它的特点是有一个事件循环,当外部事件发生时通过回调机制触发相应处理
另外两种编程范式是单线程同步,多线程编程
综述:
1.事件驱动模型可以节省CPU资源,它有机会释放CPU进入睡眠状态(注意这是有机会,也可以自主选择不释放),当时间触发时再被唤醒
2.典型的事件驱动的程序就是一个死循环,并以一个线程的方式存在,包含两部分,按照一定条件接收并选择要处理的事件和处理事件的过程,
且当事件没有被触发时,程序会因为查询事件队列失败而进入睡眠状态,从而释放CPU
3.事件驱动的程序必定直接或间接拥有一个事件队列,用于存储未处理的事件
4.事件驱动程序完全由外部输入的事件控制,
5.事件驱动程序,可以按照一定的顺序处理队列中的事件,而这个顺序是由事件触发的顺序决定,这一特性往往用于保证某个过程的原子化
6.注意:事件驱动的监听事件是由操作系统调用CPU完成的
IO多路复用
用户空间和内核空间
操作系统采用虚拟存储器,对于32位操作系统而言,它的寻址空间为4G(2的32次方)
操作系统的核心是内核,独立于普通应用程序,可以访问被保护的内存空间,也拥有访问底层硬件设备的所有权
为了保证用户程序不能直接访问内核(kernel),保护内核的安全,操作系统将虚拟内存分为两部分,一部分为内核空间,一部分为用户空间
针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,
而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。
进程切换
为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起进程的执行,这种行为称为进程切换,由操作系统完成
从一个进程运行转到另一个进程运行,发生的变化:
保存处理机上下文,包括程序计数器和其他寄存器
更新PCB信息,把进程的PCB移入相应的队列,如:就绪,在某事件阻塞队列等
选择另一进程执行,并更新其PCB
更新内存管理的数据结构,恢复处理机上下文
进程阻塞
正在执行的程序,由于期待的某个事件未发生,如请求系统资源失败,等待某种操作,新数据尚未到达等等,
则由系统自动执行阻塞原语(block),当进程进入阻塞状态,是不占用CPU资源的
文件描述符(file descriptor),计算机术语,是一个用于指向文件引用的抽象概念,在形式上是一个非负整数
它是一个索引值,当打开或创建一个文件时,内核向进程返回一个描述符
一些涉及底层程序编写时多围绕文件描述符编写,只适用于Linux和Unix
缓存I/O
缓存I/O又称标准I/O,大多文件系统的默认I/O操作都是缓存I/O,Linux的缓存I/O机制中,
操作系统会将I/O的数据缓存在文件系统的页缓存(page cache)
即数据会先拷贝到操作系统内核的缓存区,然后再从内核的缓存区拷贝到用户的地址空间
缺点:数据在传输时的内核和用户空间的拷贝操作,对CPU和内存带来的开销是非常大的
blocking IO(阻塞IO)
在Linux下,所有的socket默认情况下都是blocking,其大致流程如下

当用户调用recvfrom时,内核(kernel)开始准备数据,对于network IO来说,很多时候数据在开始时并未到达,于是kernel就会等待,
用户这边,进程就会阻塞,直到kernel拿到所有数据,并copy到用户内存中,kernel会返回一个结果,用户进程才会解除block状态
所以,blocking IO的特点就是IO执行的两个过程都被block了
non-blocking IO(非阻塞IO)
Linux下,可以设置socket使其变为non-blocking IO,流程如下

与blocking IO不同,non-blocking IO在发出询问时,如果数据没有准备好,kernel会立刻回一个消息,用户不用等待
用户进程通过的轮询,直到kernel检测到数据准备好,然后将数据copy到用户内存
它将一个长时间的阻塞分成多个小阻塞,在此期间,进程不断被CPU访问,CPU权限还在自己手中,这段期间是可以再操作其他的事情,
也因此导致任务响应延迟增大
注意当kernel检测到数据准备好了之后的拷贝操作,依然是阻塞状态
#server端 import time
import socket
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setsockopt
sk.bind(('127.0.0.1',6667))
sk.listen(5)
sk.setblocking(False) #设置为非阻塞IO
while True:
try:
print ('waiting client connection .......')
connection,address = sk.accept() # 进程主动轮询
print("+++",address)
client_messge = connection.recv(1024)
print(str(client_messge,'utf8'))
connection.close()
except Exception as e:
print (e)
time.sleep(4)
#client端 import time
import socket
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM) while True:
sk.connect(('127.0.0.1',6667))
print("hello")
sk.sendall(bytes("hello","utf8"))
time.sleep(2)
break
IO multilexing(IO多路复用)
利用select或epoll轮询所负责的socket,当数据到达时,就通知用户进程,select/epoll的好处在于单个process可以处理多个网络连接的IO

当用户调用select后,整个进程都会被block,同时kernel会监测所有select负责的socket,当任何一个socket数据准备好后,select就会返回
此时,用户在read操作,将数据从kernel中copy到用户进程
想比较blocking IO ,IO multilexing 优势在于可以同时处理多个connection(如果处理的链接数不多的话,不一定比multi-threading+blocking IO快)
#server端 import socket
import select
sk=socket.socket()
sk.bind(("127.0.0.1",8801))
sk.listen(5)
inputs=[sk,]
while True:
r,w,e=select.select(inputs,[],[],5) #通过select监测socket连接,返回3个结果
#4个参数,第1个为可读集合,第2个位可写,第3个为异常信息,第4个是等待的最大时间
# print(len(r)) for obj in r:
if obj==sk:
conn,add=obj.accept()
print(conn)
inputs.append(conn)
else:
data_byte=obj.recv(1024)
print(str(data_byte,'utf8'))
inp=input('回答%s号客户>>>'%inputs.index(obj))
obj.sendall(bytes(inp,'utf8')) # print('>>',r)
#client.py import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8801)) while True:
inp=input(">>>>")
sk.sendall(bytes(inp,"utf8"))
data=sk.recv(1024)
print(str(data,'utf8'))
asynchronous I/o(异步IO)

全程不存在阻塞,用户发起read后,kernel会立刻返回结果,然后用户就会去做其他的事情,然后kernel等待数据准备好,然后copy到用户内存
全部做好之后,kernel会发送一个signal,告诉用户已read完成
各个IO model过程比较

select,poll,epoll
select:通过select()系统调用来监测多个系统描述符的数组,当select()返回时,该数组中的文件描述符就会被修改标志位,
使得进程可以获得这些标志位,从而进行后续的读写操作,select全平台支持,但单个进程监测文件描述符存在最大限制,Linux一般最大1024
poll:与select差不多,但它没有最大限制
epoll:无最大限制,和select不同,当某个 链接活跃时,能直接提供具体的链接,而不用像select将整个链接轮询一遍才能得到这个链接
IO多路复用的触发方式
水平触发:如果文件描述符已经就绪,可以非阻塞的执行IO时,此时触发通知,允许任意时刻重复检测IO状态,而不必尽可能多的执行IO
边缘触发:如果文件描述符在上一次状态改变后又有新的IO操作来了,触发通知,此时就要尽可能多的执行IO
select属于水平触发,epoll既属于水平触发,又属于边缘触发
两种触发的区别:当水平触发时,select/epoll会立即返回,不会有阻塞,因为已经有数据了
当边缘触发时,不会返回,此时是阻塞状态,等到新的数据到来时,epoll才会返回,此时新数据,老数据都可以读到
从电子角度来看:水平触发是只在高电平和低电平情况下才会触发。边缘触发是在电平发生改变(高->低,低->高)时触发
事件,IO,select的更多相关文章
- 事件驱动模型 IO多路复用 阻塞IO与非阻塞IO select epool
一.事件驱动 1.要理解事件驱动和程序,就需要与非事件驱动的程序进行比较.实际上,现代的程序大多是事件驱动的,比如多线程的程序,肯定是事件驱动的.早期则存在许多非事件驱动的程序,这样的程序,在需要等待 ...
- IO模型之IO多路复用 异步IO select poll epoll 的用法
IO 模型之 多路复用 IO 多路复用IO IO multiplexing 这个词可能有点陌生,但是如果我说 select/epoll ,大概就都能明白了.有些地方也称这种IO方式为 事件驱动IO ( ...
- UNP——第六章,多路转接IO——select
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ...
- jQuery 事件——关于select选中
场景: eg:在管理一篇博文时,因博文的管理有一列叫:状态的列,该列有诸多状态,如:正常,待审核,删除等... 此时,若使用Select下拉列表进行状态选择,并在选中具体项值后,通过Ajax异步提交, ...
- jquery事件之select选中事件
根据select下拉列表选中的不同选项执行不同的方法,工作中经常会用到,这里就要用到Jquery的select选中事件 这里给select加一个叫label_id的id,然后通过id选择器找到这个节点 ...
- Python 协程/异步IO/Select\Poll\Epoll异步IO与事件驱动
1 Gevent 协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到 ...
- Python自动化 【第十篇】:Python进阶-多进程/协程/事件驱动与Select\Poll\Epoll异步IO
本节内容: 多进程 协程 事件驱动与Select\Poll\Epoll异步IO 1. 多进程 启动多个进程 进程中启进程 父进程与子进程 进程间通信 不同进程间内存是不共享的,要想实现两个进程间 ...
- 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】
下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...
- 《UNIX网络编程》之select IO
select 函数的原理 select 管理者 用select来管理多个IO 一旦其中的一个或者多个IO检测到我们所感兴趣的事件, select 函数返回,返回值为检测到的事件个数 然后,遍历事件,进 ...
- Linux下select, poll和epoll IO模型的详解
http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...
随机推荐
- insert和insertSelective区别
两者的区别在于如果选择insert 那么所有的字段都会添加一遍即使没有值 <insert id="insert" parameterType="com.ego.po ...
- Redis cluster Specification 笔记
ref: http://redis.io/topics/cluster-spec 1. 设计目标: 高性能:线性扩展:不支持合并操作:写操作安全:小概率丢弃:(对于每个key)只要有一个slave工作 ...
- DeepFaceLab 模型预训练参数Pretrain的使用!
Pretrain参数是20190501版本才加入的参数,作者加入这个参数的目的应该是提升模型的训练速度和增强适应性.具体有哪些提升,需要大家去摸索,我这里分享一下自己的使用过程. 这个参数仅针对S ...
- lambda表达式使用解析
1.Predicate/Consumer/Function/Supplier介绍 Predicate boolean test(T t); Consumer accpet(T t); Function ...
- 日常linux命令
绝对路径用什么符号表示?当前目录.上层目录用什么表示?主目录用什么表示? 切换目录用什么命令? 绝对路径: 如/etc/init.d 当前目录和上层目录: ./ . ...
- jQuery验证控件jquery.validate.js汉化
如需要修改,可在js代码中加入: jQuery.extend(jQuery.validator.messages, { required: "必选字段", remote: &q ...
- 【HANA系列】SAP HANA SQL获取当前日期
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA SQL获取当前 ...
- 初学node.js-nodejs中实现删除用户路由
一.users_model.js 功能:定义用户对象模型 var mongoose=require('mongoose'), Schema=mongoose.Schema; var UserSche ...
- 关于Goroutine与Channel
关于Goroutine的原理 原理上的内容比较多,比如goroutine启动的时候要执行哪些相关的操作,一点一点的补充一下. channel的基本原理 channel是go语言中的特殊的机制,既可以同 ...
- Linux操作系统目录一览表
/ // 根目录 /bin //存放必要的命令 比如ls.cp.mkdir等命令 /boot //存放内核以及启动所需的文件 /dev //存放硬件设备文件 比如声卡.磁盘.光驱 /etc //存放系 ...