事件驱动模型

对于普通编程来说,代码遵循线性流程:开始--》代码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资源的

文件描述符fd

  文件描述符(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的更多相关文章

  1. 事件驱动模型 IO多路复用 阻塞IO与非阻塞IO select epool

    一.事件驱动 1.要理解事件驱动和程序,就需要与非事件驱动的程序进行比较.实际上,现代的程序大多是事件驱动的,比如多线程的程序,肯定是事件驱动的.早期则存在许多非事件驱动的程序,这样的程序,在需要等待 ...

  2. IO模型之IO多路复用 异步IO select poll epoll 的用法

    IO 模型之 多路复用 IO 多路复用IO IO multiplexing 这个词可能有点陌生,但是如果我说 select/epoll ,大概就都能明白了.有些地方也称这种IO方式为 事件驱动IO ( ...

  3. UNP——第六章,多路转接IO——select

    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ...

  4. jQuery 事件——关于select选中

    场景: eg:在管理一篇博文时,因博文的管理有一列叫:状态的列,该列有诸多状态,如:正常,待审核,删除等... 此时,若使用Select下拉列表进行状态选择,并在选中具体项值后,通过Ajax异步提交, ...

  5. jquery事件之select选中事件

    根据select下拉列表选中的不同选项执行不同的方法,工作中经常会用到,这里就要用到Jquery的select选中事件 这里给select加一个叫label_id的id,然后通过id选择器找到这个节点 ...

  6. Python 协程/异步IO/Select\Poll\Epoll异步IO与事件驱动

    1 Gevent 协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到 ...

  7. Python自动化 【第十篇】:Python进阶-多进程/协程/事件驱动与Select\Poll\Epoll异步IO

    本节内容: 多进程 协程 事件驱动与Select\Poll\Epoll异步IO   1.  多进程 启动多个进程 进程中启进程 父进程与子进程 进程间通信 不同进程间内存是不共享的,要想实现两个进程间 ...

  8. 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

    下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...

  9. 《UNIX网络编程》之select IO

    select 函数的原理 select 管理者 用select来管理多个IO 一旦其中的一个或者多个IO检测到我们所感兴趣的事件, select 函数返回,返回值为检测到的事件个数 然后,遍历事件,进 ...

  10. Linux下select, poll和epoll IO模型的详解

    http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...

随机推荐

  1. python之正则匹配match:search findall

    match:从开头位置匹配,只匹配一次,开头匹配不上,则不继续匹配 a,b,\w+ match(a,"abcdef") 匹配a >>> re.match(&quo ...

  2. hibernate多对一单项关联映射

    1.实体类编写: 用户类: public class User { private int id; private String name; private Group group; ..... } ...

  3. idea中查看一个类的调用用和被调用用关系

  4. 详解设备PID和VID

    根据USB规范的规定,所有的USB设备都有供应商ID(VID)和产品识别码(PID),主机通过不同的VID和PID来区别不同的设备. VID和PID都是两个字节长,其中,供应商ID(VID)由供应商向 ...

  5. IntelliJ IDEA2018破解教程

    破解方法:下载破解补丁→修改配置文件→输入激活码→激活成功 由于JetBrains封杀,大部分激活服务器已经不能使用,使用下面的比较麻烦的方法也可以进行破解,但是有效期是到2100年(emmmm,也算 ...

  6. java基础--继承、实现、依赖、关联、聚合、组合的联系与区别

    继承 指的是一个类或者接口继承另一个类或者接口,而且可以增加自己的新功能. 实现 指的是一个class类实现interface接口. 依赖 简单说,就是一个类中的方法用到了另一个类,一般依赖关系在ja ...

  7. leetcode-mid-backtracking -78 Subsets

    mycode   86.06% class Solution(object): def subsets(self, nums): """ :type nums: List ...

  8. seleniumIDE回放找不到页面元素

    seleniumIDE回放找不到页面元素 如下所示,自动回放就报错,手动执行就不报错.

  9. TP3.2写分页

    用TP3.2写分页 手册上说的好难懂,我自己去网上找资料 ,现在整理一下,以后可能会用: 在Think下面有Page.class.php类: 我在这个下面放了一个function.php的(算是类吧又 ...

  10. apache-httpd2.2编译安装

    1.下载源码包 wget http://mirrors.hust.edu.cn/apache/httpd-2.2.32.tar.gz2.解压源码包 tar -zxvf httpd-2.2.32.tar ...