全面剖析UNIX网络编程的五种I/O模式

在Linux系统中,内核将所有外部设备都视为文件来操作。对一个文件的读写操作会调用内核提供的系统命令,并返回一个文件描述符(file descriptor,简称fd)。同样地,对一个套接字的读写操作也有对应的描述符,称为socket描述符(socketfd)。描述符实际上是一个数字,指向内核中的一个结构体,该结构体包含文件路径、数据区等属性。

《UNIX网络编程》将I/O模型分为五种类型:

1. 阻塞I/O模型

阻塞I/O模型是最常用的I/O模型。在默认情况下,所有文件操作都是阻塞的。当在进程空间中调用recvfrom时,该系统调用会一直阻塞,直到数据包到达并被复制到应用进程的缓冲区中,或者发生错误。这段时间内,进程会一直等待,因此被称为阻塞I/O模型。

工作流程

  1. 应用程序调用recvfrom
  2. recvfrom阻塞等待数据包到达或错误发生
  3. 数据包到达或发生错误时,recvfrom返回

示例代码

char buffer[1024]; // 定义一个缓冲区用于存储接收到的数据
int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL); // 调用recvfrom阻塞等待数据

代码讲解

  • char buffer[1024]:定义一个大小为1024字节的缓冲区,用于存储接收到的数据。
  • int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL,NULL):调用recvfrom函数从套接字sockfd读取数据。该调用会阻塞,直到数据包到达并被复制到缓冲区buffer中。返回值n是读取到的字节数。

2. 非阻塞I/O模型

在非阻塞I/O模型中,当调用recvfrom时,如果缓冲区没有数据,它会立即返回一个EWOULDBLOCK错误。应用程序通常会通过轮询不断检查这个状态,以查看内核是否有数据到达。

工作流程

  1. 应用程序调用recvfrom
  2. 如果没有数据,recvfrom立即返回EWOULDBLOCK
  3. 应用程序轮询检查数据到达状态
  4. 数据到达时,recvfrom成功返回
// 设置套接字为非阻塞模式
fcntl(sockfd, F_SETFL, O_NONBLOCK); // 设置非阻塞模式char buffer[1024]; // 定义缓冲区
int n;
while ((n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL)) < 0) {if (errno == EWOULDBLOCK) {// 无数据,继续轮询} else {// 处理其他错误}
}

代码讲解

  • fcntl(sockfd, F_SETFL, O_NONBLOCK):将套接字设置为非阻塞模式,这样recvfrom调用不会阻塞。
  • while ((n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL))< 0):循环调用recvfrom读取数据。如果没有数据,recvfrom会立即返回EWOULDBLOCK错误。
  • if (errno == EWOULDBLOCK) {}:检查错误类型是否为EWOULDBLOCK,表示当前没有数据到达,继续轮询。
  • else {}:处理其他可能的错误情况。

3. I/O复用模型

I/O复用模型利用select/poll系统调用来监控多个文件描述符。进程将一个或多个fd传递给select/poll,并阻塞在这些操作上。当任何一个fd就绪时,select/poll会返回。这种方式允许我们同时监控多个I/O事件,select/poll是顺序扫描fd是否就绪,而且支持的fd有限,因此它的使用受到了一些制约。epoll是Linux特有的更高效的I/O复用方式,它采用事件驱动机制代替了顺序扫描,比传统的selectpoll更高效。当有fd就绪时,立即回调函数rollback

工作流程

  1. 应用程序调用select/poll
  2. 阻塞等待任一fd就绪
  3. fd就绪时,select/poll返回
  4. 处理I/O事件

示例代码

fd_set readfds; // 定义文件描述符集合
FD_ZERO(&readfds); // 初始化文件描述符集合
FD_SET(sockfd, &readfds); // 将套接字添加到集合中int nfds = select(sockfd + 1, &readfds, NULL, NULL, NULL); // 调用select等待数据
if (nfds > 0) {if (FD_ISSET(sockfd, &readfds)) { // 检查套接字是否有数据char buffer[1024];int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL); // 读取数据// 处理数据}
}

代码讲解

  • fd_set readfds:定义一个文件描述符集合readfds
  • FD_ZERO(&readfds):将文件描述符集合readfds初始化为空。
  • FD_SET(sockfd, &readfds):将套接字sockfd添加到文件描述符集合中。
  • int nfds = select(sockfd + 1, &readfds, NULL, NULL, NULL):调用select,等待集合中的任何一个文件描述符变为就绪状态。如果有文件描述符就绪,select返回。
  • if (FD_ISSET(sockfd, &readfds)) {}:检查套接字sockfd是否有数据可读。
  • int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL):从套接字读取数据并存储到缓冲区buffer中。

4. 信号驱动I/O模型

信号驱动I/O模型首先需要开启套接字的信号驱动I/O功能,并通过sigaction系统调用设置一个信号处理函数。当数据准备就绪时,内核会向进程发送一个SIGIO信号,通知应用程序调用recvfrom来读取数据,并通知主循环函数处理数据。

工作流程

  1. 设置信号处理函数并启用信号驱动I/O
  2. 继续处理其他任务
  3. 数据准备就绪时,内核发送SIGIO信号
  4. 信号处理函数调用recvfrom读取数据

示例代码

// 信号处理函数
void sigio_handler(int signo) {char buffer[1024];int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL); // 读取数据// 处理数据
}// 设置信号处理函数
struct sigaction sa;
sa.sa_handler = sigio_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGIO, &sa, NULL);// 启用信号驱动I/O
fcntl(sockfd, F_SETOWN, getpid()); // 将当前进程设为套接字的所有者,以便接收信号
int flags = fcntl(sockfd, F_GETFL);
fcntl(sockfd, F_SETFL, flags | O_ASYNC); // 启用异步I/O

代码讲解

  • void sigio_handler(int signo) {}:定义一个信号处理函数,当接收到SIGIO信号时被调用。
  • struct sigaction sa:定义一个sigaction结构体sa
  • sa.sa_handler = sigio_handler:将信号处理函数设置为sigo_handler
  • sigemptyset(&sa.sa_mask):初始化信号掩码。
  • sa.sa_flags = 0:设置标志位。
  • sigaction(SIGIO, &sa, NULL):为SIGIO信号安装信号处理函数。
  • fcntl(sockfd, F_SETOWN, getpid()):将当前进程设置为套接字的所有者,以便接收SIGIO信号。
  • int flags = fcntl(sockfd, F_GETFL):获取套接字的当前标志。
  • fcntl(sockfd, F_SETFL, flags | O_ASYNC):启用异步I/O(信号驱动I/O)。

5. 异步I/O模型

异步I/O模型与信号驱动I/O模型类似,但它不仅通知我们何时可以开始I/O操作,还会通知我们操作已经完成。应用程序通过系统调用告诉内核启动某个操作,当操作完成(包括将数据从内核复制到用户缓冲区)后,内核会通知应用程序。

工作流程

  1. 告知内核启动I/O操作
  2. 继续处理其他任务
  3. 内核通知I/O操作完成
  4. 处理完成的数据

示例代码

// 定义异步I/O控制块
struct aiocb cb;
memset(&cb, 0, sizeof(struct aiocb)); // 清零控制块
cb.aio_fildes = sockfd; // 设置文件描述符
cb.aio_buf = buffer; // 设置缓冲区
cb.aio_nbytes = sizeof(buffer); // 设置读取字节数
cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; // 设置信号通知方式
cb.aio_sigevent.sigev_signo = SIGIO; // 设置通知信号// 启动异步读操作
aio_read(&cb); // 启动异步读取// 信号处理函数
void aio_completion_handler(int signo) {// 检查操作是否完成if (aio_error(&cb) == 0) {int n = aio_return(&cb); // 获取读取的字节数// 处理数据}
}

代码讲解

  • struct aiocb cb:定义一个异步I/O控制块cb
  • memset(&cb, 0, sizeof(struct aiocb)):将控制块清零。
  • cb.aio_fildes = sockfd:设置文件描述符sockfd
  • cb.aio_buf = buffer:设置缓冲区buffer。
  • cb.aio_nbytes = sizeof(buffer):设置要读取的字节数。
  • cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL:设置信号通知方式。
  • cb.aio_sigevent.sigev_signo = SIGIO:设置通知信号为SIGIO
  • aio_read(&cb):启动异步读操作,立即返回,内核将在操作完成时通知。
  • void aio_completion_handler(int signo) {}:定义一个信号处理函数,当异步操作完成时被调用。
  • if (aio_error(&cb) == 0) {}:检查异步操作是否出错。
  • int n = aio_return(&cb):获取读取的字节数。

总结

本文介绍了UNIX网络编程中五种主要的I/O模型:阻塞I/O、非阻塞I/O、I/O复用、信号驱动I/O和异步I/O。每种模型都有其独特的应用场景和优缺点,理解这些模型对于优化网络编程性能至关重要。通过实际的代码示例和详细讲解,您可以更好地掌握每种模型的工作原理和实现方法。

参考文献

  • 《UNIX网络编程(第1卷):套接字联网API》 - W. Richard Stevens, Bill Fenner, Andrew M.
    Rudoff
  • 《高级编程:UNIX环境》 - W. Richard Stevens, Stephen A. Rago
  • Linux手册页(man pages)

希望这篇文章对您了解和使用不同的I/O模型有所帮助。如果您有任何问题或需要进一步的解释,请随时留言。

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

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

相关文章

你对仲裁裁决不服怎么办?我教你四个狠招!

你对仲裁裁决不服怎么办&#xff1f;我教你四个狠招&#xff01; 这个标题是什么意思呢&#xff1f;也就是说&#xff0c;当你&#xff08;或用人单位&#xff09;向劳动仲裁委提出仲裁申请后&#xff0c;但劳动仲裁结果没有维护你的权益&#xff0c;或者你不满意&#xff0c;…

[STM32+HAL]LD-1501MG舵机二次开发指南

一、准备材料 核心板&#xff1a;STM32F407ZGT6 舵机&#xff1a;LD-1501MG数字舵机 控制器&#xff1a;24路PWM舵机控制器 二、HAL库配置 开启串口与控制器通信 三、Keil填写代码 1、Servo.c #include "Servo.h" #include "usart.h"uint8_t Message…

python内存马学习

python内存马学习 python内存马学习 python内存马学习环境搭建和复现分析payloadFlask 请求上下文管理机制bypass高版本flask内存马的利用before_request**after_request** teardown_requesterrorhandler相关例题H&NCTF 2024 ezFlask python内存马 环境搭建和复现 from fl…

离大模型落地应用最近的工程化技术(RAG)

虽然大规模语言模型&#xff08;LLM&#xff09;在自然语言处理&#xff08;NLP&#xff09;方面表现出了其强大的文本生成和理解能力&#xff0c;但是它们在实际应用中仍然面临一些挑战&#xff0c;如处理大规模知识库和实时获取最新信息的能力&#xff0c;并且会产生幻觉。为…

Flutter 中的 ColoredBox 小部件:全面指南

Flutter 中的 ColoredBox 小部件&#xff1a;全面指南 在 Flutter 的世界中&#xff0c;ColoredBox 是一个用于填充颜色的简单而强大的小部件。它是一个不透明的矩形&#xff0c;可以用来创建颜色块&#xff0c;作为布局的占位符&#xff0c;或者简单地改变某个区域的背景色。…

「代码厨房大揭秘:Python性能优化的烹饪秘籍!」

哈喽&#xff0c;我是阿佑&#xff0c;上篇咱们讲了 Socket 编程 —— 探索Python Socket编程&#xff0c;赋予你的网络应用隐形斗篷般的超能力&#xff01;从基础到实战&#xff0c;构建安全的聊天室和HTTP服务器&#xff0c;成为网络世界的守护者。加入我们&#xff0c;一起揭…

Java中数字里面混合有下划线10_000 代表什么意思?

public static void main(String[] args) {int a 10_000;System.out.println(a); // 10000} java 7 的 特性 &#xff1a; https://docs.oracle.com/javase/7/docs/technotes/guides/language/underscores-literals.html 程序中的数字可以使用下划线来进行分割&#xff08;_…

区块链系统开发测试----链码部署开发、系统开发验证

一.检查配置环境 检查虚拟机环境&#xff0c;确保有正在运行的Hyperledger Fabric区块链&#xff0c;并且其中chaincode_basic、credit_chaincode链码可以正常调用 查看chaincode_basic、credit_chaincode链码调用 二.开发征信链码代码 基于现有征信链码&#xff0c;开发征信…

Niantic利用Meta Llama让数字生物栩栩如生

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

华为机考入门python3--(26)牛客26-字符串排序

分类&#xff1a;字符串 知识点&#xff1a; 字符串是否仅由字母构成 my_str.isalpha() 字母列表按小写排序 letters.sort(keylambda x: x.lower()) 题目来自【牛客】 def custom_sort(input_str):letters []non_letters []for char in input_str:if char.isalpha…

【软件工程】【23.04】p1

关键字&#xff1a; 软件模型、提炼、加工表达工具、通信内聚、访问依赖、边界类交互分析、RUP核心工作流、首先测试数据流、软件验证过程、CMMI过程域分类工程类&#xff1b; 软件工程目的、功能需求是需求的主体、结构化方法、耦合、详细设计工具、类、类图、RUP采用用例技…

鲜花门店小程序开发流程:详细教程,让你轻松掌握

想要开发一款专属于自己鲜花门店的小程序吗&#xff1f;不知道从何开始&#xff1f;别担心&#xff0c;本文将为你提供详细的开发流程&#xff0c;帮助你轻松掌握。 1. 注册登录乔拓云网并进入操作后台 首先&#xff0c;你需要注册并登录乔拓云网&#xff0c;然后进入操作后台…

SQLServer2022 ISJSON新特性增强json_type_constraint参数

SQLServer2022 ISJSON新特性增强&#xff0c;引入json_type_constraint参数&#xff0c;检查json类型 参考官方文档 https://learn.microsoft.com/zh-cn/sql/t-sql/functions/isjson-transact-sql?viewsql-server-ver15 1、本文内容 语法参数返回值注解 适用于&#xff1a…

Java列表导出时将附件信息压缩成一个zip

一&#xff1a;使用场景 在最近的工作当中遇到了一个需求&#xff0c;在列表导出时&#xff0c;不仅需要将列表信息导出为excel文件&#xff0c;同时也需要将列表每一条数据所对应的附件信息放在同一个文件夹当中&#xff0c;并且压缩成一个zip响应给浏览器。首先后端需要写两…

Llama模型家族之使用 Supervised Fine-Tuning(SFT)微调预训练Llama 3 语言模型(三)通过web页面方式微调

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…

Amazon云计算AWS(一)

目录 一、基础存储架构Dynamo&#xff08;一&#xff09;Dynamo概况&#xff08;二&#xff09;Dynamo架构的主要技术 二、弹性计算云EC2&#xff08;一&#xff09;EC2的基本架构&#xff08;二&#xff09;EC2的关键技术&#xff08;三&#xff09;EC2的安全及容错机制 提供的…

Python自动化办公Excel数据处理实战指南

目录 一、引言 二、需求分析 三、技术选型 四、实战操作 数据读取 数据清洗 数据分析 数据输出 五、学习资源推荐&#xff1a; 六、结语 一、引言 在现代办公环境中&#xff0c;Excel数据处理是一项不可或缺的技能。然而&#xff0c;当数据量庞大、处理流程复杂时&a…

Linux文本文件管理003

★排序、去重、统计★ 1&#xff09;排序 sort -n按照数值排序 -r降序排列 2&#xff09;去重 uniq 过滤相邻、重复的行 -c 对重复行计数 3&#xff09;统计 wc 统计文件中的字节数、单词数、行数 -l 显示行数 今天通过使用grep、awk、cut指令和上面几个选项提取文本文件…

Java开发常见基础问题

Java开发的多个方面&#xff0c;包括但不限于Java基础知识、多线程并发、JVM、框架使用、数据库、设计模式、网络编程等。 以下是一些常见的问题以及回答的方向&#xff1a; Java 开发技术常见问题&#xff08;一&#xff09; Java 基础知识 对象和类的区别是什么&#xff1…

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(八)

本系列课程&#xff0c;将重点讲解Phpsploit-Framework框架软件的基础使用&#xff01; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; 继续接上一篇文章内容&#xff0c;讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 现在&#xff0c;我们…