事件驱动模型

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

    1.Long类和Integer相类似,都是基本类型的包装类,类中的方法大部分都是类似的: 关于Integer类的浅析可以参看:Integer类源码浅析 2.这里主要介绍一下LongCache类,该缓存 ...

  2. @清晰掉 malloc是如何分配内存的?

    任何一个用过或学过C的人对malloc都不会陌生.大家都知道malloc可以分配一段连续的内存空间,并且在不再使用时可以通过free释放掉.但是,许多程序员对malloc背后的事情并不熟悉,许多人甚至 ...

  3. SQL Server阻塞blocking案例分析

    今天在性能测试过程中发现大量阻塞报警,检查whoisactive(https://github.com/amachanic/sp_whoisactive/)数据发现,阻塞blocking头部sessi ...

  4. legend3---Homestead常用操作代码

    legend3---Homestead常用操作代码 一.总结 一句话总结: 在虚拟机里面改变文件windows里面也会变,在windows里面改变虚拟机里面也会变,所以可以在windows里面编程或者 ...

  5. C# 格式化XML方法

    /// <summary> /// 格式化XML方法 /// </summary> public class UXMLFormat { public static string ...

  6. 在windows下使用Mingw搭建模拟Linux

    1.到官网下载最新版Mingw 2.点击安装,最好选择默认路径,如果不是的话,路径中一定不能有空格. 3.选择安装,mingw-developer-toolkit.mingw32-base.mingw ...

  7. gitlab webhook jenkins 403问题解决方案

    1.gitlab webhook 403问题,一般描述为Error 403 anonymous is missing the Job/Build Permission 解决方法: 安装插件:gitla ...

  8. K8S 笔记,请坚持

    service 通过 selector 来选择指定 label 的 pod. 如何访问 service 呢?集群内部访问,使用 cluster ip:集群外部访问,使用 NodePort. clust ...

  9. Python Module_pdb_DEBUG 方法

    目录 目录 pdb pdb 的 Debug 方式 pdb 的调试指令 示例 IPython 自带的 Debug 工具 ipdb pdb pdb 是 Python 自带的程序包,为 Python 程序提 ...

  10. Jmeter 接口测试 响应结果中文是Unicode转为中文

    1.增加一个后置处理器:BeanShell PostProcessor 内容如下: //获取响应代码Unicode编码的        String s2=new String(prev.getRes ...