理解计算机系统_并发编程(3)_基于I/O复用的并发(二):基于I/O多路复用的并发事件驱动服务器

前言
       

        以<深入理解计算机系统>(以下称“本书”)内容为基础,对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定

引入 

        接续上一帖理解计算机系统_并发编程(2)_基于I/O复用的并发(一):select浅解-CSDN博客,实现一个基于I/O多路复用的并发事件驱动服务器.研读代码,总结编程的一些模式.

状态机

        本书P686和P687介绍了状态机的概念并配图

        在事件驱动程序中,某些事件会导致流向前推进.一般思路是将逻辑流模型化为状态机.一个状态机就是一组状态,输入事件转移,其中转移是将状态和输入事件映射到状态.---黑体字是原话

        ---解读:逻辑是什么?是一种因果关系.逻辑流解释为从因到果的流程.状态机和逻辑流相互对应

        状态对应逻辑(因果),表述为:当条件A满足时,用事件B响应.

        输入事件对应起因已满足,表述为:条件A已满足

        转移对应用结果响应,表述为:事件B

        /*书面叙述概念时,常用较大的篇幅来写.而读者理解时可以采用简单的方式*/.

        所有的逻辑都可以转化成状态机 

        状态机的推导:"状态"是逻辑是想法,不需要代码;"输入事件"和"转移"需要代码表达.所以状态机可以看作是一种理想的逻辑表达方式,从思路到实现都包含了进去.

服务器I/O多路复用的状态机

        本书P687第3段:服务器使用I/O多路复用,借助select函数检测输入事件的发生.当每个已连接描述符准备好可读时,服务器就为相应的状态机执行转移,在这里就是从描述符读和写回一个文本行.

        ---解读:select:检测输入事件;  转移:描述符读和写回一个文本行.

        任何一个状态机必然有检测输入事件,这里用的是select函数. 这里的转移事件---"描述符读和写回一个文本行"非必须,演示用.

基于I/O多路复用的并发事件驱动服务器

=============================内容分割线↓===================================

读代码的说明

         /*代码是最有说服力的,表现编程思想和程序模型的存在*/

         学习别人的源码,除了理解别人的思路,还要一点也很重要:从源码中整理和提取写代码的模式.

         注意:读代码的顺序是从主程序开始读,先理解大概意思,再去理解他调用的函数.同样道理,遇到变量,也不要一个一个挨着去读,要结合使用去理解.

=============================内容分割线↑===================================

代码整体说明

        本书P687最后一段是基于I/O多路复用的并发服务器示例代码的整体说明.

        /*如果自己写了一段代码,技术文档也可以借鉴这种方式,让别人容易理解*/

        一个pool结构里维护着活动客户端的集合(第3~11行)  ---黑体字是原话

        ---解读:这里有个词叫"维护",经常在其他技术文献中看到什么维护着什么,感觉很专业.从内容上看,维护代表"描述"或者"控制".

        活动客户端的集合在服务器端用一个结构来描述,结构取名叫pool(池),理解为客户端连接池.

        然后是初始化init_pool,进入while循环,检测输入事件,连接请求到达,调用add_client函数,送回文本.这些内容要简单了解后,边读源码边理解.

代码研读

         注意:经典书的经典代码,很难得(好好看好好学)

         主程序在本书P687和P688,名为echoservers.c

函数开始

          1>第3~11行是"连接池"结构声明

typedef struct{int maxfd;fd_set read_set;fd_set ready_set;int nready;int maxi;int clientfd[FD_SETSIZE];rio_t clientrio[FD_SETSIZE];
}pool;

         如前所述,不用一下全看完,边读代码边看连接池结构是怎样定义的.

         2>进入main函数,第17~20行是变量定义,也是结合后面代码读.注意pool定义为静态变量,即表示不想被别的模块使用,如果要用到,必须提供接口.---这是C语言基础.这里加上了static,声明成main函数中的局部静态变量,为了保持更新.

        注意这里的命名方式不太合理,类型名和变量名取得一样.规则写法应该把类型名写成首字母大写Pool.

        3>第26行调用Open_listenfd函数

         这里再梳理一遍监听描述符的建立:Open_listenfd函数是由服务器调用,表示等待客户端连接请求.一旦客户端发来连接请求,代码才执行,生成listenfd监听描述符,代表服务器已经知道你的请求了.后续将视情况调用accept接受请求,生成connfd描述符.

init_pool函数

        在本书P688图12-9.从名字看来表示:初始化连接池.

        第4~7行代码如下

int i;
p->maxi=-1;
for(i=0;i<FD_SETSIZE;i++)p->clentfd[i]=-1;

        ---解读:clientfd[FD_SETSIZE]是第一个理解的数据,他是什么意思?书上说了clientfd数组表示已连接描述符的集合.

        /*已连接描述符在服务器端通常用connfd,书上用了clientfd来表示连接(客户端连接),connfd用到了其他地方*/

        开始时没有连接进来,所以数组中所有整数值设置为-1.-1表示槽位(用其他值也可以,不一定是-1,但既然其值用来表示描述符,所以推荐小于-1的值和其他文件描述符做区分)

        FD_SETSIZE,本书没有显式说明.从全大写的表示来看,他是#define宏定义的一个常数.字面意思代表了允许连接数.

        maxi:已连接集合里的最大索引.初始时没有连接,也被设置为-1.

        笔者开始时在这里也饶了半天,这个数组的来源是这样的,演示如下:

        服务器每当建立一个连接,就会生成一个描述符connfd来传输数据,描述符是个整型数据(从4开始).那么当前有多少个描述符已建立呢?这些描述符该怎样控制其数量呢?设计了一个整型数组

typedef struct{...int maxi;                 //当前已连接描述符的个数-1,同时也是下面数组中不等于-1的索引值int clientfd[FD_SETSIZE]; //已连接描述符数组,最大连接数限制为FD_SETSIZE...
}pool;

        初始化时,clientfd[FD_SETSIZE]={-1,-1.....-1},此时索引maxi设置-1.

        当有一个描述符(比如4)被产生,写个算法让他进去数组中,此时clientfd[FD_SETSIZE]={4,-1.....-1},maxi加1,等于0.当遍历这个数组查询时,就知道有1个元素(索引等于0)

        这样是不是把意思表达清晰了?而这个把数组索引maxi单独表达的写法,也值得一学.

        第10~12行代码如下        

p->maxfd=listenfd;
FD_ZERO(&p->read_set);
FD_SET(listenfd,&p->read_set);

      ---解读:maxfd是读集合的基数,"读集合"和"基数"意思在理解计算机系统_并发编程(2)_基于I/O复用的并发(一):select浅解-CSDN博客有,此处不多说.

        FD_ZERO清空读集合,FD_SET把listenfd添加到读集合中. ---初始化配置读集合 

回到主函数

        第29行进入while循环,第30行设置"准备好集合",第31行调用Select函数

         再看pool中对应数据的使用:

         read_set:读集合;

         ready_set:准备好集合.

         nready:准备好集合中元素的个数

         第35行调用FD_ISSET传入listenfd,表示连接判定---如果有客户端请求连接,则执行.

         第36行的意思在本书前面一章(笔者也没有细读,照着用)

         第37行调用Accept函数,表示服务器端同意连接,生成connfd描述符(写法也参照前面一章)

add_client()函数

        添加一个新的客户端到活动客户端池中,更新pool结构中的数据.

=============================内容分割线↓===================================

        注意select函数的解读,他的第一次调用和多次调用的返回值不同.

        当第一次被调用,也就是maxfd+1=4的情况下,此时读集合只有一种情况响应---第一个连接请求进来,listenfd描述符被激活.这种情况下p->nready等于1.而如果select函数非第一次调用,表示他可能会有新的连接,或者已有连接准备好读,这时的p->nready不等于1.---代码要考虑满足多种情况.

=============================内容分割线↑===================================

        第4行更新p->nready,添加描述符到客户端池的时候,准备好集合的元素个数-1.

        第5~8行查找槽位,把生成的connfd描述符写入clientfd数组.

        注意第6行的if(p->clientfd[i]<0)不是唯一写法,因为在init_pool的定义中clientfd数组中的值都等于-1.而生成的connfd的值都是≥4的,所以写成if(p->clientfd[i]==-1)也可以.

        第9行初始化读缓冲区,这部分内容没有细说,可以"抄".这里有个clientrio数组,是分配给每个连接的缓冲区.

         第12行把connfd添加进读集合,意思是下次调用select可以作为条件进入准备好集合.

         第15,16行更新maxfd,当读集合添加了一个描述符后,其基数应该加1.这里用了一个比较

         第17,18行更新maxi,"顺势"使新进入的连接作为准备好集合的一员,参加后面的代码,更新准备好集合的索引

         第21行可不写,写了更明确:如果i==FD_SETSIZE,超出最大连接数,add_client函数执行无效

check_clients()函数

          /*主函数中已没有其他内容,check_clients()函数紧跟add_client()函数.*/

           本书P690中间有注释:check_clients服务准备好的客户端连接.遍历clientfd数组,取出文件描述符并读写,不详述.

           注意:

            第7行的for条件中p->nready>0,表示第一次添加进准备好集合(上面17,18行的描述)不能进入这个环节.

            第12行的connfd>0笔者没看懂,以为每个connfd都要≥4(可能有些东西没完全搞懂),

            第12行的FD_ISSET(connfd,&p->ready_set)判断遍历出来的connfd是否是本次select执行的结果,以此作为后面代码执行的前提.从这里可以反推出select函数执行的结果是类似于{1,3,5}这样的整型数组.

            /*语法上使用if或者for,while做条件判断时,如果有多重判断,值得注意他的意思*/

I/O多路复用技术的优劣

        以下黑体字是本书原话

        1.基于事件驱动,听起来很好,为客户端提供他们需要的服务,对于基于进程的并发服务器来说,是很困难的.

         2.编码复杂.我们的事件驱动的并发echo服务器需要的代码比基于进程的服务器多三倍,并且很不行,随着并发粒度的减小,复杂性还会上升.

         3.不能充分利用多核处理器.

代码层面的小结

        1.当想要对程序数据进行控制和描述时,维护一个结构.结构的数据类型设计,和读代码时一样,不用一次到位,一边写代码一边根据需要增加(笔者在前面数据类设计中提到过,即使有冗余属性问题也不大---也就是代码不够优雅).结构的好处是他是全局变量,可以动态修改,实时更新.

        2.select函数的第一个参数,从前面的硬编码listenfd+1变成了maxfd+1,实时更新.

        3.遗憾本书并没有把select讲得很透彻,需要找资料补充.select的使用限于"复制粘贴".比如select调用到什么情况下结束?和时间长短是否有关系(像进程一样由时间片停止)

小结

        基于I/O多路复用的并发事件驱动服务器的一点理解

            

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/80761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系统可靠性分析:指标解析与模型应用全览

以下是关于系统可靠性分析中可靠性指标、串联系统与并联系统、混合系统、系统可靠性模型的相关内容&#xff1a; 一、可靠性指标 可靠度&#xff1a;是系统、设备或元件在规定条件和规定时间内完成规定功能的概率。假设一个系统由多个部件组成&#xff0c;每个部件都有其自身…

数字高程模型(DEM)公开数据集介绍与下载指南

数字高程模型&#xff08;DEM&#xff09;公开数据集介绍与下载指南 数字高程模型&#xff08;Digital Elevation Model, DEM&#xff09;广泛应用于地理信息系统&#xff08;GIS&#xff09;、水文模拟、城市规划、环境分析、灾害评估等领域。本文系统梳理了主流的DEM公开数据…

Python+大模型 day01

Python基础 计算机系统组成 基础语法 如:student_num 4.标识符要做到见名知意,增强代码的可读性 关键字 系统或者Python定义的,有特殊功能的字符组合 在学习过程中,文件名没有遵循标识符命名规则,是为了按序号编写文件方便查找复习 但是,在开发中,所有的Python文件名称必须…

C++引用编程练习

#include <iostream> using namespace std; double vals[] {10.1, 12.6, 33.1, 24.1, 50.0}; double& setValues(int i) { double& ref vals[i]; return ref; // 返回第 i 个元素的引用&#xff0c;ref 是一个引用变量&#xff0c;ref 引用 vals[i] } // 要调用…

机密虚拟机的威胁模型

本文将介绍近年兴起的机密虚拟机&#xff08;Confidential Virtual Machine&#xff09;技术所旨在抵御的威胁模型&#xff0c;主要关注内存机密性&#xff08;confidentiality&#xff09;和内存完整性&#xff08;integrity&#xff09;两个方面。在解释该威胁可能造成的问题…

【Rust trait特质】如何在Rust中使用trait特质,全面解析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Simulink模型回调

Simulink 模型回调函数是一种特殊的 MATLAB 函数&#xff0c;可在模型生命周期的特定阶段自动执行。它们允许用户自定义模型行为、执行初始化任务、验证参数或记录数据。以下是各回调函数的详细说明&#xff1a; 1. PreLoadFcn 触发时机&#xff1a;Simulink 模型加载到内存之…

FPGA:Xilinx Kintex 7实现DDR3 SDRAM读写

在Xilinx Kintex 7系列FPGA上实现对DDR3 SDRAM的读写&#xff0c;主要依赖Xilinx提供的Memory Interface Generator (MIG) IP核&#xff0c;结合Vivado设计流程。以下是详细步骤和关键点&#xff1a; 1. 准备工作 硬件需求&#xff1a; Kintex-7 FPGA&#xff08;如XC7K325T&…

Python爬虫实战:研究进制流数据,实现逆向解密

1. 引言 1.1 研究背景与意义 在现代网络环境中,数据加密已成为保护信息安全的重要手段。许多网站和应用通过二进制流数据传输敏感信息,如视频、金融交易数据等。这些数据通常经过复杂的加密算法处理,直接分析难度较大。逆向工程进制流数据不仅有助于合法的数据获取与分析,…

Java Spring Boot项目目录规范示例

以下是一个典型的 Java Spring Boot 项目目录结构规范示例&#xff0c;结合了分层架构和模块化设计的最佳实践&#xff1a; text 复制 下载 src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ └── myapp/ │…

图像颜色理论与数据挖掘应用的全景解析

文章目录 一、图像颜色系统的理论基础1.1 图像数字化的本质逻辑1.2 颜色空间的数学框架1.3 量化过程的技术原理 二、主要颜色空间的深度解析2.1 RGB颜色空间的加法原理2.2 HSV颜色空间的感知模型2.3 CMYK颜色空间的减色原理 三、图像几何属性与高级特征3.1 分辨率与像素密度的关…

mysql两张关联表批量更新一张表存在数据,而另一张表不存在数据的sql

一、mysql两张关联表批量更新一张表存在、另一张表不存在的数据 创建user和user_order表 CREATE TABLE user (id varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,id_card varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NU…

PNG转ico图标(支持圆角矩形/方形+透明背景)Python脚本 - 随笔

摘要 在网站开发或应用程序设计中&#xff0c;常需将高品质PNG图像转换为ICO格式图标。本文提供一份基于Pillow库实现的&#xff0c;能够完美保留透明背景且支持导出圆角矩形/方形图标的格式转换脚本。 源码示例 圆角方形 from PIL import Image, ImageDraw, ImageOpsdef c…

在线SQL转ER图工具

在线SQL转ER图网站 在数据库设计、软件开发或学术研究中&#xff0c;ER图&#xff08;实体-关系图&#xff09; 是展示数据库结构的重要工具。然而&#xff0c;手动绘制ER图不仅耗时费力&#xff0c;还容易出错。今天&#xff0c;我将为大家推荐一款非常实用的在线工具——SQL…

绘制时间对应的数据曲线

头文件#pragma once #include "ChartControl.h" #include <vector> #include "DBOperate.h&

【挑战项目】 --- 微服务编程测评系统(在线OJ系统)(二)

三十二、Swagger介绍&使用 官网:https://swagger.io/ 什么是swagger Swagger是一个接口文档生成工具,它可以帮助开发者自动生成接口文档。当项目的接口发生变更时,Swagger可以实时更新文档,确保文档的准确性和时效性。Swagger还内置了测试功能,开发者可以直接在文档中…

人事管理系统总结

1.Maven 创建 Spring Boot 项目&#xff1a; 主要使用 Maven 创建 Spring Boot 项目、配置 MySQL 数据库、回顾 Spring Boot 分层架构、使用 MyBatis 逆向工程生成代码及整合测试项目等内容&#xff0c;具体如下&#xff1a; Maven 创建 Spring Boot 项目 可通过 IDEA 直接创…

SpringBoot--springboot简述及快速入门

spring Boot是spring提供的一个子项目&#xff0c;用于快速构建spring应用程序 传统方式&#xff1a; 在众多子项目中&#xff0c;spring framework项目为核心子项目&#xff0c;提供了核心的功能&#xff0c;其他的子项目都需要依赖于spring framework&#xff0c;在我们实际…

INT202 Complexity of Algroithms 算法的复杂度 Pt.7 NP-Completeness NP完全性

文章目录 1.P与NP问题1.1 计算上难以解决的问题&#xff08;Hard Computational Problems&#xff09;1.2 决策问题和优化问题&#xff08;Decision/Optimization problems&#xff09;1.3 计算问题的正式定义1.4 复杂性类1.4.1 复杂性类 P P P1.4.2 证明&#xff08;Certifica…

websocketpp 安装及使用

介绍 WebSocket 是从 HTML5 开始支持的一种网页端和服务端保持长连接的消息推送机制。 传统的 web 程序都是属于 "一问一答" 的形式&#xff0c;即客户端给服务器发送了一个 HTTP 请求&#xff0c;服务器给客户端返回一个 HTTP 响应。这种情况下服务器是属于被动…