内核EPOLL模型的学习

流:就是可以进行内核操作的对象。比如文件,socket,pipe

I/O操作:从流中读取数据,往流中写入数据。

阻塞模式:要执行的操作无法进行,一直等待条件满足时再执行。等待过程中,无操作,静静等待。(经济简单)

阻塞模式下,内核对于I/o事件的处理是阻塞或者唤醒。

非阻塞模式下,内核对于I/o事件交给其他对象处理,甚至直接忽略。

非阻塞忙轮询:等待过程中,一直查询执行条件是否满足,循环往复。直到条件满足再执行。(浪费时间精力)

缓冲区:目的是为了减少I/O操作引起的系统频繁调用(很慢!)当你操作一个流时,更多的是以缓冲区为单位进行操作。(用户和内核都需要缓冲区)

状态(管道事件) 进程A(往管道的写入数据) 管道(数据的存储,流动) 进程B(从管道读取数据)
缓冲区(管道)空事件 A 没有往管道写数据 管道是空的 B无法读取数据,被阻塞着,就是B睡眠了。
缓冲区非空事件 A开始给管道写数据 管道是非空的 内核通知B可以进行读取数据了。B解除阻塞,醒来
缓冲区满事件 A阻塞, 管道满了 B没有读取数据
缓冲区非满事件 A开始给管道写数据 管道非满了 B进行了读取数据

阻塞模式缺点:一个线程只能处理一个I/o模式。如果要同时处理多个流,要么多进程,要么多线程。但是这两个方法效率都不高。

非阻塞忙轮询:不停地把所有流从头到尾问一遍,又从头开始,这样就可以处理多个流了。

​ 缺点是:如果所有的流中都没有数据,只会白白浪费CPU,

非阻塞无差别轮询:引进一个代理。代理同时观察许多流的I/O事件,在空闲的时候,会把当前的线程阻塞掉,当有一个或者多个流有I/o操作事件时,就唤醒线程,于是就轮询一遍所有的流。

​ 没有I/o事件时,程序阻塞到代理select处,我们仅仅从select处知道了有I/O事件发生了,但是不知道是哪个流,只能无差别轮询。当流多的时候,效率也是相当的低了。

非阻塞事件轮询:代理eventpool会将哪个流发生了什么I/O事件通知我们。此时对这些流的操作都是有意思的,复杂度降到了n(1)

epoll是对select,poll模型的改进,提高了网络编程的性能,广泛用于大规模并发请求的c/s结构中。

1 触发方式:

​ 边缘触发/水平触发,(只使用unix和linux操作系统)

2 原理

3 步骤

​ 1 创建一个epoll对象

​ 2 告诉epoll对象,在指定的socket上监听指定的事件

​ 3 询问epoll对象,从上次查询以来,那些socket发生了那些指定的事件

​ 4 在这些socket上执行一些操作,

​ 5 告诉epoll对象,修改socket列表和或者事件,并监控

​ 6 重复 3 -5 ,直到完成

​ 7 销毁epoll对象

4 相关用法:

​ 1 导入select模块

​ import select

​ 2 创建epoll对象

​ epoll=select.epoll()

​ 3 注册要监控的文件句柄和事件

​ epoll.register(文件句柄,事件类型)

​ 事件类型:

​ select.EPOLLIN 可读事件

​ select.EPOLLOUT 可写事件

​ select.EPOLLERR 错误事件

​ select.EPOLLHUP 客户端断开事件

​ 4 epoll.unregister(文件句柄) 销毁文件句柄

​ 5 epoll.fileno() 返回epoll的控制文件描述符

​ 6 epoll.modify(fileno,event) fileno 是文件描述符,event是事件类型,作用是修改文件描述符,所对应的事件

​ 7 epoll.fromfd(fileno) 从一个文件描述符,创建一个epoll对象

​ 8 epoll.close() 关闭epoll对象的控制文件描述符

猜你喜欢

转载自blog.csdn.net/weixin_42040854/article/details/81006027