当前位置: 首页 > news >正文

文件IO和多路复用IO

目录

前言

一、文件 I/O

1.基本文件 I/O 操作

1.1打开文件

1.2读取文件内容 (read)

1.3写入文件 (write)

1.4关闭文件 (close)

2.文件指针

二、多路复用 I/O

1.常用的多路复用 I/O 模型

1.1select

1.2poll

1.3epoll

2.使用 select、poll 和 epoll 进行简单的 I/O 监控

3.select、poll 和 epoll 三种 I/O 多路复用模型的对比


前言

  • 文件 I/O 是程序与外部数据交互的基础,可以通过读取和写入文件实现数据持久化。文件 I/O 的操作相对简单,适用于普通的数据读写场景。
  • 多路复用 I/O 则是一种高级 I/O 操作模式,能够有效地处理多个文件描述符的 I/O 事件,广泛应用于网络编程中,特别是在高并发服务器中发挥着重要作用。常用的多路复用 I/O 模型包括 selectpollepoll

一、文件 I/O

文件 I/O(Input/Output,输入/输出)指的是计算机程序与文件进行交互的过程,这些文件通常存储在磁盘上。文件 I/O 是程序与外部数据交互的基础,能够实现数据的存储、读取和处理。

1.基本文件 I/O 操作

1.1打开文件

  • 在进行文件操作之前,需要先打开文件。open 函数用于打开文件,并返回一个文件对象,供后续操作使用。
  • 语法:file_object = open(file_name, mode)
  • 常用模式:
    • "r": 读取(默认)
    • "w": 写入(会覆盖原有文件)
    • "a": 追加(在文件末尾追加内容)
    • "b": 二进制模式(如 "rb", "wb" 等)
    • "+": 更新模式(读写都可以,如 "r+", "w+"

1.2读取文件内容 (read)

  • 打开文件后,可以使用 read()readline()readlines() 方法读取文件内容。
  • read(size):读取文件中 size 个字节内容,如果不指定 size,则读取整个文件。
  • readline():按行读取文件,返回文件中的一行。
  • readlines():返回文件中的所有行,结果是一个包含每行内容的列表。
with open("example.txt", "r") as file:content = file.read()print(content)

1.3写入文件 (write)

  • 使用 write() 方法将字符串写入文件中。如果文件是以 "w" 模式打开的,写入时会覆盖文件内容。
  • 如果想逐行写入,可以使用 writelines() 方法,这个方法接受一个字符串列表,每个字符串将作为一行写入文件。
with open("example.txt", "w") as file:file.write("Hello, World!\n")file.writelines(["Line 1\n", "Line 2\n"])

1.4关闭文件 (close)

操作完成后,应当使用 close() 关闭文件,以释放系统资源。不过,使用 with 语句可以自动关闭文件。

file = open("example.txt", "r")
# 执行操作
file.close()  # 关闭文件

2.文件指针

  • 文件指针是指示文件中当前读写位置的标记。
  • tell():获取文件指针当前位置。
  • seek(offset, from_what):移动文件指针到指定位置。offset 表示偏移量,from_what 表示参考点(0: 文件开头, 1: 当前位置, 2: 文件结尾)。

二、多路复用 I/O

多路复用 I/O 是一种高级 I/O 操作模式,允许一个进程同时监听多个文件描述符(如文件、网络连接等),并根据事件发生情况进行相应处理。它的优势在于避免了传统阻塞 I/O 操作的等待时间,提升了并发处理的效率。

1.常用的多路复用 I/O 模型

1.1select

  • select 是一种跨平台的 I/O 多路复用接口,能够监控一组文件描述符(包括套接字),当其中的一个或多个描述符准备好进行 I/O 操作时,select 返回并指示哪些描述符可以进行操作。
  • 它的基本思想是通过单个系统调用等待多个文件描述符中的任何一个变得可读、可写或有错误发生。
import select
import socket# 创建 socket 对象
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8080))
sock.listen(5)inputs = [sock]
while True:readable, writable, exceptional = select.select(inputs, [], [])for s in readable:if s is sock:conn, addr = s.accept()inputs.append(conn)else:data = s.recv(1024)if data:print("Received:", data.decode())else:inputs.remove(s)s.close()

1.2poll

  • pollselect 的改进版本,克服了 select 的一些限制(如文件描述符数量限制)。
  • poll 使用一个 poll 对象来监控多个文件描述符。它的行为和 select 类似,但性能更好,特别是在监控大量描述符时。
import select
import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8080))
sock.listen(5)poller = select.poll()
poller.register(sock, select.POLLIN)fd_to_socket = {sock.fileno(): sock}
while True:events = poller.poll()for fd, flag in events:s = fd_to_socket[fd]if flag & select.POLLIN:if s is sock:conn, addr = s.accept()poller.register(conn, select.POLLIN)fd_to_socket[conn.fileno()] = connelse:data = s.recv(1024)if data:print("Received:", data.decode())else:poller.unregister(fd)s.close()del fd_to_socket[fd]

1.3epoll

  • epoll 是 Linux 提供的一种高效的多路复用 I/O 模型,适用于大量并发连接场景。相比 selectpollepoll 更加高效,因为它采用了事件通知机制,避免了轮询的开销。
  • epoll 可以分为水平触发(Level-Triggered,LT)和边缘触发(Edge-Triggered,ET)两种模式,ET 模式更加高效,但编程更加复杂。
import select
import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8080))
sock.listen(5)epoll = select.epoll()
epoll.register(sock.fileno(), select.EPOLLIN)fd_to_socket = {sock.fileno(): sock}
while True:events = epoll.poll()for fd, event in events:s = fd_to_socket[fd]if event & select.EPOLLIN:if s is sock:conn, addr = s.accept()epoll.register(conn.fileno(), select.EPOLLIN)fd_to_socket[conn.fileno()] = connelse:data = s.recv(1024)if data:print("Received:", data.decode())else:epoll.unregister(fd)s.close()del fd_to_socket[fd]

2.使用 selectpollepoll 进行简单的 I/O 监控

import select
import socket
import sysdef create_server_socket():server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)server_socket.bind(('localhost', 8080))server_socket.listen(5)return server_socketdef handle_select(server_socket):inputs = [server_socket]while True:readable, writable, exceptional = select.select(inputs, [], [])for s in readable:if s is server_socket:conn, addr = s.accept()inputs.append(conn)else:data = s.recv(1024)if data:s.send(data)else:inputs.remove(s)s.close()def handle_poll(server_socket):poller = select.poll()poller.register(server_socket, select.POLLIN)fd_to_socket = {server_socket.fileno(): server_socket}while True:events = poller.poll()for fd, flag in events:s = fd_to_socket[fd]if flag & select.POLLIN:if s is server_socket:conn, addr = s.accept()poller.register(conn.fileno(), select.POLLIN)fd_to_socket[conn.fileno()] = connelse:data = s.recv(1024)if data:s.send(data)else:poller.unregister(fd)s.close()del fd_to_socket[fd]def handle_epoll(server_socket):epoll = select.epoll()epoll.register(server_socket.fileno(), select.EPOLLIN)fd_to_socket = {server_socket.fileno(): server_socket}while True:events = epoll.poll()for fd, event in events:s = fd_to_socket[fd]if event & select.EPOLLIN:if s is server_socket:conn, addr = s.accept()epoll.register(conn.fileno(), select.EPOLLIN)fd_to_socket[conn.fileno()] = connelse:data = s.recv(1024)if data:s.send(data)else:epoll.unregister(fd)s.close()del fd_to_socket[fd]if __name__ == "__main__":if len(sys.argv) != 2:print("Usage: python3 server.py <select|poll|epoll>")sys.exit(1)mode = sys.argv[1]server_socket = create_server_socket()if mode == "select":handle_select(server_socket)elif mode == "poll":handle_poll(server_socket)elif mode == "epoll":handle_epoll(server_socket)else:print("Invalid mode. Use 'select', 'poll', or 'epoll'.")sys.exit(1)

3.selectpollepoll 三种 I/O 多路复用模型的对比

  • select 适用于小规模并发应用,跨平台支持广泛,但在处理大量文件描述符时性能较差。
  • poll 解决了 select 的文件描述符限制问题,适合中小规模并发连接,但在性能上不如 epoll
  • epoll 是处理大规模并发连接的最佳选择,具有出色的扩展性和性能,但仅在 Linux 系统上可用,并且编程复杂度较高。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Flink入门(五)--Flink算子
  • 【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第六篇 嵌入式GUI开发篇-第八十三章 Qt基础
  • Windows系统电脑安装多个Tomcat服务教程
  • 2021年高教社杯国赛a题 详细代码 文章 教学 2024数学建模国赛数模备赛: “FAST”主动反射面的形状调节
  • Android 自适应屏幕技术
  • SpringBootWeb 篇-深入了解 SpringBoot + Vue 的前后端分离项目部署上线与 Nginx 配置文件结构
  • HTML简单了解和基础知识记录
  • 高可用IP地址管理:使用Keepalived和Nginx实现VIP及IP池配置
  • kaggle竞赛宝典 | 量化竞赛第一名的网络模型
  • 【系统架构设计师】论文:论软件开发平台的选择与应用
  • NPJ系列|放射组学与多组学数据整合:推进精准肿瘤学的新模式|文献速递·24-08-25
  • 虚幻5|制作一个木桩,含血量及伤害数字
  • python代码错误集合
  • Linux自旋锁和读写锁
  • Runnable
  • emacs初体验
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JSONP原理
  • js作用域和this的理解
  • leetcode388. Longest Absolute File Path
  • PhantomJS 安装
  • Spring Boot MyBatis配置多种数据库
  • ubuntu 下nginx安装 并支持https协议
  • ucore操作系统实验笔记 - 重新理解中断
  • XForms - 更强大的Form
  • 工作手记之html2canvas使用概述
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 将 Measurements 和 Units 应用到物理学
  • 类orAPI - 收藏集 - 掘金
  • 三栏布局总结
  • 首页查询功能的一次实现过程
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 7行Python代码的人脸识别
  • ​水经微图Web1.5.0版即将上线
  • #laravel 通过手动安装依赖PHPExcel#
  • #pragma data_seg 共享数据区(转)
  • #进阶:轻量级ORM框架Dapper的使用教程与原理详解
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (LLM) 很笨
  • (编译到47%失败)to be deleted
  • (剑指Offer)面试题34:丑数
  • (转)菜鸟学数据库(三)——存储过程
  • ./configure、make、make install 命令
  • .chm格式文件如何阅读
  • .gitignore文件忽略的内容不生效问题解决
  • .gitignore文件---让git自动忽略指定文件
  • .net refrector
  • .Net Remoting常用部署结构
  • .NET Remoting学习笔记(三)信道
  • .net web项目 调用webService
  • .NET 解决重复提交问题
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。