《Linux 核心 IO 模型深析(中篇):探索Cmake与多路转接的高效实现poll》

《Linux 核心 IO 模型深析(中篇):探索Cmake与多路转接的高效实现poll》
前引:IO 是 Linux 系统性能的核心瓶颈之一,所有 IO 操作本质上都离不开 “等待” 与 “拷贝” 两个关键步骤。在五种经典 IO 模型中,非阻塞 IO 以 “轮询” 打破传统阻塞限制,多路转接 IO 凭 “多文件描述符监听” 实现高效等待,二者凭借独特的工作逻辑,成为高并发、低延迟场景的核心选择。本文将深入剖析两种模型的底层原理、工作流程、优劣势差异,以及实际开发中的落地要点,帮助开发者真正理解其设计思想并灵活运用!

目录

【一】Cmake替代make

(1)先安装:需要sudo权限

(2)准备一个空目录

(3)三步上篮

(4)效果

【二】poll接口介绍

(1)函数原型

(2)参数说明

(1)第一个参数

(2)第二个参数

(3)第三个参数

(3)返回值

(4)特点

(5)函数使用

(1)建立关心数组

(2)初始化关心数组

(3)设置poll

(4)任务处理

(5)添加新链接

(6)读取数据

(6)效果演示

(7)完整代码


【一】Cmake替代make

理解:Cmake中输入目标和源文件,可以自己调用make生成,更加简化,主流

使用方法:

(1)先安装:需要sudo权限
# Ubuntu/Debian sudo apt update && sudo apt install cmake -y # 验证安装(显示版本即成功) cmake --version
(2)准备一个空目录

因为Cmake会产生一堆副文件,避免污染重要目录的源码,比如我创建了一个名为 build 目录

(3)三步上篮

在需要生成的源码同目录下创建 CMakeLists.txt文件,添加下面的代码内容:

第一行直接复制,第二行和第三行按照对应情况修改即可

# 最低CMake版本要求(根据自己安装的版本调整,比如3.10) cmake_minimum_required(VERSION 3.10) # 项目名(随便取,比如myapp) project(myapp) # 生成可执行文件:可执行文件名为myapp,编译的源文件是main.c add_executable(myapp main.c)
(4)效果

例如:用 cmake 指令调用 CMakeLists.txt 文件会直接生成 makefile 文件,再手动 make 指令

如果要生成多个可执行程序:如果修改了源码,再重新 make /make clean即可,和之前一样

【二】poll接口介绍

(1)函数原型
#include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout);
(2)参数说明
(1)第一个参数

参数描述:指向结构体 pollfd 类型的指针(管理多个文件描述符可以是 struct polled 类型的数组)

struct pollfd { int fd; // 要监听的文件描述符(-1 表示忽略此结构体) short events; // 要监听的事件(输入参数,由程序设置) short revents; // 实际发生的事件(输出参数,由内核填充) };

监听事件选项:即 events 由你设置选项,revents 由poll调用之后由操作系统给你填写

事件标识含义(events 输入)含义(revents 输出)
POLLIN监听 “可读” 事件该 fd 有数据可读
POLLOUT监听 “可写” 事件该 fd 可写入数据
POLLERR无需主动设置该 fd 发生错误
POLLHUP无需主动设置该 fd 对应的连接关闭(如 socket 断开)
POLLNVAL无需主动设置fd 无效(如未打开)
(2)第二个参数

参数描述:数组中有效结构体的数量(必须大于0),可理解为监听的文件描述符个数

(3)第三个参数

参数描述:超时时间(单位:毫秒)(>0  最大返回时间  =0 非阻塞立刻返回  =-1 阻塞使用)

(3)返回值
  • 成功:返回就绪的文件描述符总数(可能为 0,即超时)
  • 失败:返回 -1,且设置 errno(如 EINTR 表示被信号中断,可重试)
(4)特点
(1)只要有事件就绪就会一直通知你,这和select通知特点一样

(2)监听个数由用户决定,受限于系统资源,而select受限于自身数组

(3)输入和输出分离,而select输入输出是合并的

(4)每次调用不需要重新设置监听集合
(5)函数使用
(1)建立关心数组
#define max_num_size 10 struct pollfd fds[max_num_size];
(2)初始化关心数组
 void Initialize_struct() { for(int i=0;i<max_num_size;i++) { fds[i].fd=-1; } }
(3)设置poll

先将listen套接字添加到关心数组,再根据poll的返回值判断是否需要处理新链接

 void Deal() { //初始化结构体 Initialize_struct(); //将listen套接字添加到关心结构体 fds[0].fd=_V.Fd(); fds[0].events=POLLIN; for(;;) { //计算关心的个数 int nods=0; for(nods=0;nods<max_num_size;nods++) { if(fds[nods].fd==-1)continue; else nods++; } //调用poll int po = poll(fds, nods, 1000); // 判断事件 switch (po) { case 0: { std::cout << "没有客户端访问我...." << std::endl; } case -1: { // log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"错误码:%d,错误信息:%s",errno,strerror(errno)); break; } default: { // 处理新链接 Handle(); } } } }
(4)任务处理

遍历关心数组:

如果是listen套接字 并且 该套接字准备就绪,就将accept返回的文件描述符重新添加到关心数组

如果不是listen套接字 并且 该套接字又准备就绪,说明是读端(只关心了读),可以直接recv

 void Handle() { for(int i=0;i<max_num_size;i++) { //如果是listen套接字且listen套接字准备就绪 if(fds[i].fd==_V.Fd() && fds[i].revents & POLLIN) { Accept(); } else if(fds[i].revents & POLLIN)//需要判断该文件描述符的读端是否就绪 { Recv(i); } } }
(5)添加新链接

找到下标为 -1 的空余结构体位置,将accept返回的进行添加

 void Accept() { //获取accept文件描述符 int fd = _V.Accept(); //添加到关心结构体 int i; for(i=0;i<max_num_size;i++) { if(fds[i].fd==-1)break; } if(i==max_num_size) { std::cout<<"满了.....hhhhhhhh"<<std::endl; close(fd); } //添加 fds[i].fd=fd; fds[i].events=POLLIN; }
(6)读取数据

读取对应文件描述符即可

 void Recv(int i) { char buffer[1024] = {0}; ssize_t d = recv(fds[i].fd, buffer, sizeof(buffer) - 1, 0); if (d > 0) { buffer[d] = 0; std::cout << "客户端发送了数据 : "; std::cout << buffer << std::endl; }else if (d == 0) { // 对方断开了连接 close(fds[i].fd); fds[i].fd = -1; // 关闭当前的文件描述符,并且从数组中删掉 } else { // 读取错误 close(fds[i].fd); fds[i].fd = -1; } }
(6)效果演示

首先我们创建Cmake文件:

避免垃圾信息干扰当前目录,我们新建一个目录,执行 cmake .. 指令,再执行make,运行程序

运行效果:
(7)完整代码
注意:以下类中,Accept()为服务器 accept 函数
// 辅助数组大小 #define max_num_size 10 class Media { public: void Install() { _V.Socket(); // 绑定 _V.Bind(); // 发起连接 _V.Listen(); } void Initialize_struct() { for(int i=0;i<max_num_size;i++) { fds[i].fd=-1; } } void Accept() { //获取accept文件描述符 int fd = _V.Accept(); //添加到关心结构体 int i; for(i=0;i<max_num_size;i++) { if(fds[i].fd==-1)break; } if(i==max_num_size) { std::cout<<"满了.....hhhhhhhh"<<std::endl; close(fd); } //添加 fds[i].fd=fd; fds[i].events=POLLIN; } void Recv(int i) { char buffer[1024] = {0}; ssize_t d = recv(fds[i].fd, buffer, sizeof(buffer) - 1, 0); if (d > 0) { buffer[d] = 0; std::cout << "客户端发送了数据 : "; std::cout << buffer << std::endl; }else if (d == 0) { // 对方断开了连接 close(fds[i].fd); fds[i].fd = -1; // 关闭当前的文件描述符,并且从数组中删掉 } else { // 读取错误 close(fds[i].fd); fds[i].fd = -1; } } void Handle() { for(int i=0;i<max_num_size;i++) { //如果是listen套接字且listen套接字准备就绪 if(fds[i].fd==_V.Fd() && fds[i].revents & POLLIN) { Accept(); } else if(fds[i].revents & POLLIN)//需要判断该文件描述符的读端是否就绪 { Recv(i); } } } void Deal() { //初始化结构体 Initialize_struct(); //将listen套接字添加到关心结构体 fds[0].fd=_V.Fd(); fds[0].events=POLLIN; for(;;) { //计算关心的个数 int nods=0; for(nods=0;nods<max_num_size;nods++) { if(fds[nods].fd==-1)continue; else nods++; } //调用poll int po = poll(fds, nods, 1000); // 判断事件 switch (po) { case 0: { std::cout << "没有客户端访问我...." << std::endl; } case -1: { // log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"错误码:%d,错误信息:%s",errno,strerror(errno)); break; } default: { // 处理新链接 Handle(); } } } } private: Server _V; struct pollfd fds[max_num_size]; };

Read more

最新电子电气架构(EEA)调研-3

而新一代的强实时性、高确定性,以及满足CAP定理的同步分布式协同技术(SDCT),可以实现替代TSN、DDS的应用,且此技术已经在无人车辆得到验证,同时其低成本学习曲线、无复杂二次开发工作,将开发人员的劳动强度、学习曲线极大降低,使开发人员更多的去完成算法、执行器功能完善。 五、各大车厂的EEA 我们调研策略是从公开信息中获得各大车厂的EEA信息,并在如下中进行展示。 我们集中了华为、特斯拉、大众、蔚来、小鹏、理想、东风(岚图)等有代表领先性的车辆电子电气架构厂商。        1、华为 图12 华为的CCA电子电气架构              (1)华为“计算+通信”CC架构的三个平台                         1)MDC智能驾驶平台;                         2)CDC智能座舱平台                         3)VDC整车控制平台。        联接指的是华为智能网联解决方案,解决车内、车外网络高速连接问题,云服务则是基于云计算提供的服务,如在线车主服务、娱乐和OTA等。 华

By Ne0inhk
Apache IoTDB 架构特性与 Prometheus+Grafana 监控体系部署实践

Apache IoTDB 架构特性与 Prometheus+Grafana 监控体系部署实践

Apache IoTDB 架构特性与 Prometheus+Grafana 监控体系部署实践 文章目录 * Apache IoTDB 架构特性与 Prometheus+Grafana 监控体系部署实践 * Apache IoTDB 核心特性与价值 * Apache IoTDB 监控面板完整部署方案 * 安装步骤 * 步骤一:IoTDB开启监控指标采集 * 步骤二:安装、配置Prometheus * 步骤三:安装grafana并配置数据源 * 步骤四:导入IoTDB Grafana看板 * TimechoDB(基于 Apache IoTDB)增强特性 * 总结与应用场景建议 Apache IoTDB 核心特性与价值 Apache IoTDB 专为物联网场景打造的高性能轻量级时序数据库,以 “设备 - 测点” 原生数据模型贴合物理设备与传感器关系,通过高压缩算法、百万级并发写入能力和毫秒级查询响应优化海量时序数据存储成本与处理效率,同时支持边缘轻量部署、

By Ne0inhk
SQL Server 2019安装教程(超详细图文)

SQL Server 2019安装教程(超详细图文)

SQL Server 介绍) SQL Server 是由 微软(Microsoft) 开发的一款 关系型数据库管理系统(RDBMS),支持结构化查询语言(SQL)进行数据存储、管理和分析。自1989年首次发布以来,SQL Server 已成为企业级数据管理的核心解决方案,广泛应用于金融、电商、ERP、CRM 等业务系统。它提供高可用性、安全性、事务处理(ACID)和商业智能(BI)支持,并支持 Windows 和 Linux 跨平台部署。 一、获取 SQL Server 2019 安装包 1. 官方下载方式 前往微软官网注册账号后,即可下载 SQL Server Developer 版本(

By Ne0inhk