一次读懂 Select、Poll、Epoll IO复用技术

news/2024/7/9 16:56:22 标签: epoll, php, 数据结构与算法

“ 阅读本文大概需要 6 分钟。”

我们之前采用的多进程方式实现的服务器端,一次创建多个工作子进程来给客户端提供服务。其实这种方式是存在问题的。

可以打个比方:如果我们先前创建的几个进程承载不了目前快速发展的业务的话,是不是还得增加进程数?我们都知道系统创建进程是需要消耗大量资源的,所以这样就会导致系统资源不足的情况。

那么有没有一种方式可以让一个进程同时为多个客户端端提供服务?

接下来要讲的IO复用技术就是对于上述问题的最好解答。

对于IO复用,我们可以通过一个例子来很好的理解它。(例子来自于《TCP/IP网络编程》)

某教室有10名学生和1名老师,这些学生上课会不停的提问,所以一个老师处理不了这么多的问题。那么学校为每个学生都配一名老师,

也就是这个教室目前有10名老师。此后,只要有新的转校生,那么就会为这个学生专门分配一个老师,因为转校生也喜欢提问题。如果把以上例子中的学生比作客户端,那么老师就是负责进行数据交换的服务端。则该例子可以比作是多进程的方式。

后来有一天,来了一位具有超能力的老师,这位老师回答问题非常迅速,并且可以应对所有的问题。而这位老师采用的方式是学生提问前必须先举手,确认举手学生后在回答问题。则现在的情况就是IO复用。

目前的常用的IO复用模型有三种:select,poll,epoll

select模型:

说的通俗一点就是各个客户端连接的文件描述符也就是套接字,都被放到了一个集合中,调用select函数之后会一直监视这些文件描述符中有哪些可读,如果有可读的描述符那么我们的工作进程就去读取资源。PHP 中有内置的函数来完成 select 系统调用。

函数原型:

int socket_select (array &$read ,array &$write ,array &$except ,int $tv_sec [,int $tv_usec= 0 ])

作用说明:用于确定一个或多个套接字的状态,对每一个套接字,调用者可查询它的可读性、可写性及错误状态信息

参数说明:

read: 指向一组等待可读性检查的套接字

write: 指向一组等待可写性检查的套接字

except: 指向一组等待错误检查的套接字

tv_sec: 用来设置 select() 的等待时间,秒

tv_usec: 用来设置 select() 的等待时间,微妙

这里注意一下,如果 tv_sec 设置为0,则 socket_select 立即返回,也就是非阻塞的。如果 tv_sec 设置为 null ,则 socket_select 将一直阻塞到有套接字满足条件。

下面通过代码代码来简单举例:

图片描述

poll模型:

poll 和 select 的实现非常类似,本质上的区别就是存放 fd 集合的数据结构不一样。select 在一个进程内可以维持最多 1024 个连接,poll 在此基础上做了加强,可以维持任意数量的连接。

但 select 和 poll 方式有一个很大的问题就是,我们不难看出来 select 是通过轮训的方式来查找是否可读或者可写,打个比方,如果同时有100万个连接都没有断开,而只有一个客户端发送了数据,所以这里它还是需要循环这么多次,造成资源浪费。

所以后来出现了 epoll 系统调用。

   epoll模型: 

epoll 是 select 和 poll 的增强版,epoll 同 poll 一样,文件描述符数量无限制。

epoll是基于内核的反射机制,在有活跃的 socket 时,系统会调用我们提前设置的回调函数。而 poll 和 select 都是遍历。

但是也并不是所有情况下 epoll 都比 select/poll 好,比如在如下场景:

在大多数客户端都很活跃的情况下,系统会把所有的回调函数都唤醒,所以会导致负载较高。既然要处理这么多的连接,那倒不如 select 遍历简单有效。

在 PHP 中我们可以使用 libevet 拓展来实现 epoll

libevent 是一个用C语言写的,基于事件驱动的高性能网络库。支持多种 I/O 多路复用技术,epoll、 poll、 dev/poll、 select 和 kqueue 等。 libevent 同时为文件描述符、信号、超时设定等事件提供了监听回调。所以这种编程方式也可以说是事件编程。

先放代码体验一番:

服务端:

图片描述

客户端:

图片描述

先说简单的客户端,客户端的主要作用也就是像服务端发送了两句话。第一句是 hello world!,然后等待两秒之后再次发送 send again!

并且每次发送之后都将接收到服务端返回的字节数。

讲解服务端之前先了解一下关于时间循环的一些函数:

event_base_new 创建一个事件库(只需创建一次)

event_new 创建事件

event_set 为创建的事件设置要监听文件描述符fd,以及事件类型、回调函数

event_base_set 将创建的事件与事件库关联

event_add 将设置好的事件加入事件监听器

event_base_loop 开启事件循环

还有 event_set 的几个参数:

  • EV_TIMEOUT: 超时
  • EV_READ: 只要网络缓冲中还有数据,回调函数就会被触发
  • EV_WRITE: 只要塞给网络缓冲的数据被写完,回调函数就会被触发
  • EV_SIGNAL: POSIX信号量
  • EV_PERSIST: 不指定这个属性的话,回调函数被触发后事件会被删除

服务端的三个函数:

read_cb() 接受数据,发送数据

error_cb() 错误处理

accept_cb() 受理请求并且把新的文件描述符加入事件库,同时注册 read_cb 回调

整个服务端的主要流程如下:

1.创建事件库

2.设置事件回调

3.绑定事件

4.开始事件循环

5.如有符合条件的文件描述符则系统开始调用我们提前设定好的处理函数

至于详细的流程我就不分析了,大致流程应该都能理解,接下来就靠自己巩固了。一定要自己动手实践才行。

当然本文也只是起到抛砖引玉而已,以上有问题的地方欢迎指出。由于作者没有开通留言功能,所以欢迎后台直接回复哦。( 阿毛的Coding之路 )

本人会持续分享一些关于编程以及编程自学相关的文章(不限于PHP),记录自己的自学编程之路。同时希望自己的分享能够帮助一些对编程感兴趣以及正在编程道路上的朋友。


http://www.niftyadmin.cn/n/1214894.html

相关文章

掘金开发者大会 · 微信小程序专场 | 讲师、嘉宾、赞助商、合作伙伴、媒体一网打尽!...

2 天后,掘金将于2018年9月16日在北京富力万丽酒店召开以「掘金开发者大会 微信小程序专场」为题的开发者大会,届时将有超过 600 名开发者、数十个小程序团队、数十家合作伙伴及媒体参与。此次活动由腾讯云钻石赞助,以及讯飞开放平台、云眼、…

python数学库numpy_第70课 python常用模块numpy:对数组运算提供大量的数学函数库...

一、numpy概述numpy(Numerical Python)提供了python对多维数组对象的支持:ndarray,具有矢量运算能力,快速、节省空间。numpy支持高级大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。二、创建ndarray数组ndarr…

ubuntu20安装mysql8_Ubuntu18.0.4下mysql 8.0.20 安装配置方法图文教程

mysql 8.0.20 安装配置方法图文教程整理下,供大家参考。一、准备工作1、检查是否已经安装mysql当前计算机没有安装mysql。2、下载mysql8.0。根据mysql安装指南,发现mysql5.7版本最高只适配到ubuntu17.04,而mysql8.0最高适配到ubuntu18.04。点…

Python CBV和FBV

说一下CBV(classbaseviews)和FBV(functionbaseviews),我们之前Django 里写的都是FBV,今天看一下 CBV,也就是在我们的视图函数里写类CBV 总结: 1、 from django.views import View class CBVTest(View): #这里我们的cla…

小智机器人有初中课程吗_张小智

姓名:张小智职称:讲师邮箱: zhangxiaozhiwit.edu.cn个人基本情况分别于2016年和2019年在澳门大学获得机电工程硕士和博士。在相关领域在国内外重要学术刊物及学术会议上发表学术论文20多篇,其中被SCI检索5篇,被EI检索16篇。多次参加大型国际学术会议并作…

在Windows 10上更改家庭组连接设置

在Windows 10上更改家庭组连接设置的步骤:第1步:进入 好系统重装助手 控制面板。步骤2:单击“网络和Internet”下的“选择家庭组和共享选项 ”。步骤3:选择 重装助手下载 更改家庭组窗口中的高级共享设置以继续。步骤4&#xff…

java简单代码_java如何写出简洁代码

一、重构原则:1、三次法则(事不过三,三则重构)2、何时重构:(1)添加功能时重构(2)修补错误时重构(3)复审代码时重构3、重构优点:(1)允许逻辑共享(2)分开解释意图和实现(3)隔离变化(4)封装条件逻辑4、重构难点:(1)数据库…

RHEL7配置vlan的几种方法

rhel7中根据是否启用了NetworkManager服务,有2种配置vlan的方法,外加使用ip命令,一共是3种方法:1 如果启用了NetworkManager:方法1:使用nmtui图形化工具来配置;方法2:使用nmcli命令行…