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

Linux 高级 I/O 系统调用详解

Linux 高级 I/O 系统调用详解

1. 概述

这些是 Linux 系统中一组高级 I/O 操作系统调用,它们提供了比传统 read/write 更强大和灵活的功能。每种调用都有其特定的用途和优势。

2. 系统调用详细介绍

2.1 pread/pwrite - 位置指定读写

#include <unistd.h>ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);

功能

  • pread: 从指定位置读取数据,不改变文件指针位置
  • pwrite: 向指定位置写入数据,不改变文件指针位置

优势

  • 原子操作(读/写 + 位置指定)
  • 线程安全
  • 不影响其他读写操作

示例

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>int main() {int fd = open("test.txt", O_CREAT | O_RDWR, 0644);// 写入数据const char *data = "Hello World!";pwrite(fd, data, strlen(data), 0);// 从指定位置读取char buffer[20];ssize_t bytes_read = pread(fd, buffer, 10, 0);buffer[bytes_read] = '\0';printf("读取内容: %s\n", buffer);close(fd);return 0;
}

2.2 preadv/pwritev - 分散/聚集 I/O

#include <sys/uio.h>ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);

功能

  • preadv: 从指定位置读取到多个缓冲区(分散读取)
  • pwritev: 从多个缓冲区写入到指定位置(聚集写入)

iov 结构体

struct iovec {void  *iov_base;    // 缓冲区地址size_t iov_len;     // 缓冲区长度
};

示例

#include <stdio.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <string.h>int main() {int fd = open("scatter_gather.txt", O_CREAT | O_RDWR, 0644);// 准备分散写入数据struct iovec iov_write[3];char *data1 = "First part ";char *data2 = "Second part ";char *data3 = "Third part\n";iov_write[0].iov_base = data1;iov_write[0].iov_len = strlen(data1);iov_write[1].iov_base = data2;iov_write[1].iov_len = strlen(data2);iov_write[2].iov_base = data3;iov_write[2].iov_len = strlen(data3);// 分散写入pwritev(fd, iov_write, 3, 0);// 分散读取struct iovec iov_read[3];char buf1[20], buf2[20], buf3[20];iov_read[0].iov_base = buf1;iov_read[0].iov_len = sizeof(buf1) - 1;iov_read[1].iov_base = buf2;iov_read[1].iov_len = sizeof(buf2) - 1;iov_read[2].iov_base = buf3;iov_read[2].iov_len = sizeof(buf3) - 1;ssize_t total_bytes = preadv(fd, iov_read, 3, 0);printf("总共读取 %zd 字节\n", total_bytes);buf1[iov_read[0].iov_len] = '\0';buf2[iov_read[1].iov_len] = '\0';buf3[iov_read[2].iov_len] = '\0';printf("缓冲区1: %s\n", buf1);printf("缓冲区2: %s\n", buf2);printf("缓冲区3: %s\n", buf3);close(fd);return 0;
}

2.3 preadv2/pwritev2 - 增强版分散/聚集 I/O

#define _GNU_SOURCE
#include <sys/uio.h>ssize_t preadv2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags);
ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags);

功能

  • preadv/pwritev 基础上增加标志控制
  • 支持更细粒度的 I/O 控制

支持的标志

  • RWF_HIPRI: 高优先级 I/O
  • RWF_DSYNC: 数据同步写入
  • RWF_SYNC: 同步写入
  • RWF_NOWAIT: 非阻塞操作
  • RWF_APPEND: 追加模式

示例

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>int main() {int fd = open("enhanced_io.txt", O_CREAT | O_RDWR, 0644);if (fd == -1) {perror("open");return 1;}// 准备数据struct iovec iov[2];char *data1 = "Enhanced ";char *data2 = "I/O operation\n";iov[0].iov_base = data1;iov[0].iov_len = strlen(data1);iov[1].iov_base = data2;iov[1].iov_len = strlen(data2);// 使用增强版写入(带标志)ssize_t bytes_written = pwritev2(fd, iov, 2, 0, RWF_SYNC);if (bytes_written == -1) {if (errno == ENOSYS) {printf("系统不支持 pwritev2,回退到 pwritev\n");bytes_written = pwritev(fd, iov, 2, 0);} else {perror("pwritev2");close(fd);return 1;}}printf("写入 %zd 字节\n", bytes_written);close(fd);return 0;
}

2.4 prlimit64 - 资源限制控制

#include <sys/resource.h>int prlimit64(pid_t pid, int resource, const struct rlimit64 *new_limit, struct rlimit64 *old_limit);

功能

  • 获取和设置进程资源限制
  • 支持 64 位资源限制值
  • 可以操作其他进程的资源限制

常用资源类型

  • RLIMIT_AS: 虚拟内存地址空间限制
  • RLIMIT_CORE: 核心转储文件大小限制
  • RLIMIT_CPU: CPU 时间限制
  • RLIMIT_DATA: 数据段大小限制
  • RLIMIT_FSIZE: 文件大小限制
  • RLIMIT_NOFILE: 打开文件描述符数量限制
  • RLIMIT_NPROC: 进程数量限制
  • RLIMIT_STACK: 栈大小限制

示例

#include <stdio.h>
#include <sys/resource.h>
#include <errno.h>int main() {struct rlimit64 limit;// 获取当前进程的文件大小限制if (prlimit64(0, RLIMIT_FSIZE, NULL, &limit) == 0) {printf("文件大小限制:\n");if (limit.rlim_cur == RLIM64_INFINITY) {printf("  软限制: 无限制\n");} else {printf("  软限制: %lld 字节\n", (long long)limit.rlim_cur);}if (limit.rlim_max == RLIM64_INFINITY) {printf("  硬限制: 无限制\n");} else {printf("  硬限制: %lld 字节\n", (long long)limit.rlim_max);}}// 获取打开文件数限制if (prlimit64(0, RLIMIT_NOFILE, NULL, &limit) == 0) {printf("文件描述符限制:\n");printf("  软限制: %lld\n", (long long)limit.rlim_cur);printf("  硬限制: %lld\n", (long long)limit.rlim_max);}// 设置新的文件大小限制(仅作为示例)struct rlimit64 new_limit;new_limit.rlim_cur = 1024 * 1024;  // 1MBnew_limit.rlim_max = 1024 * 1024;  // 1MB// 注意:修改资源限制通常需要适当权限if (prlimit64(0, RLIMIT_FSIZE, &new_limit, NULL) == 0) {printf("成功设置文件大小限制为 1MB\n");} else {if (errno == EPERM) {printf("权限不足,无法修改资源限制\n");} else {perror("设置资源限制失败");}}return 0;
}

3. 性能对比测试

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <time.h>
#include <string.h>#define ITERATIONS 10000
#define BUFFER_SIZE 1024// 性能测试函数
double benchmark_function(const char *name, ssize_t (*func)(int, void*, size_t, off_t)) {int fd = open("benchmark_test.dat", O_CREAT | O_RDWR, 0644);char *buffer = malloc(BUFFER_SIZE);// 准备测试数据memset(buffer, 'A', BUFFER_SIZE);write(fd, buffer, BUFFER_SIZE);struct timespec start, end;clock_gettime(CLOCK_MONOTONIC, &start);// 执行测试for (int i = 0; i < ITERATIONS; i++) {func(fd, buffer, BUFFER_SIZE, 0);}clock_gettime(CLOCK_MONOTONIC, &end);double elapsed = (end.tv_sec - start.tv_sec) * 1000000.0 +(end.tv_nsec - start.tv_nsec) / 1000.0;free(buffer);close(fd);unlink("benchmark_test.dat");printf("%-15s: %.2f 微秒 (平均 %.3f 纳秒/次)\n", name, elapsed, elapsed * 1000.0 / ITERATIONS);return elapsed;
}// 模拟不同函数的包装器
ssize_t wrapper_pread(int fd, void *buf, size_t count, off_t offset) {return pread(fd, buf, count, offset);
}int main() {printf("=== I/O 系统调用性能对比 ===\n\n");// 创建大测试文件int test_fd = open("large_test_file.dat", O_CREAT | O_WRONLY | O_TRUNC, 0644);char *large_buffer = malloc(1024 * 1024);  // 1MBfor (int i = 0; i < 10; i++) {  // 10MB 文件write(test_fd, large_buffer, 1024 * 1024);}free(large_buffer);close(test_fd);printf("创建 10MB 测试文件完成\n\n");// 测试不同的读取方式benchmark_function("pread", wrapper_pread);printf("\n性能分析:\n");printf("1. pread/pwrite: 适合随机访问场景\n");printf("2. preadv/pwritev: 适合多缓冲区操作\n");printf("3. preadv2/pwritev2: 提供更多控制选项\n");printf("4. prlimit64: 用于资源管理而非 I/O 操作\n");unlink("large_test_file.dat");return 0;
}

4. 实际应用场景

4.1 数据库存储引擎

#include <stdio.h>
#include <sys/uio.h>
#include <fcntl.h>// 模拟数据库页读取
typedef struct {int page_id;char data[4096];int checksum;
} db_page_t;int read_database_pages(const char *db_file, int *page_ids, int count) {int fd = open(db_file, O_RDONLY);if (fd == -1) return -1;struct iovec *iov = malloc(count * sizeof(struct iovec));db_page_t *pages = malloc(count * sizeof(db_page_t));// 设置分散读取for (int i = 0; i < count; i++) {iov[i].iov_base = &pages[i];iov[i].iov_len = sizeof(db_page_t);}// 一次性读取多个数据库页ssize_t bytes_read = preadv(fd, iov, count, 0);printf("读取 %d 个数据库页,共 %zd 字节\n", count, bytes_read);free(pages);free(iov);close(fd);return bytes_read > 0 ? 0 : -1;
}

4.2 网络协议处理

#include <sys/uio.h>
#include <stdio.h>// 模拟网络包处理
typedef struct {uint32_t header;uint16_t type;uint16_t length;
} packet_header_t;typedef struct {char payload[1024];
} packet_payload_t;int process_network_packets(int socket_fd) {// 准备接收多个网络包struct iovec iov[5];  // 最多5个包packet_header_t headers[5];packet_payload_t payloads[5];// 设置分散接收缓冲区for (int i = 0; i < 5; i++) {iov[i*2].iov_base = &headers[i];iov[i*2].iov_len = sizeof(packet_header_t);iov[i*2+1].iov_base = &payloads[i];iov[i*2+1].iov_len = sizeof(packet_payload_t);}// 一次性接收多个包(简化示例)printf("准备接收网络数据包...\n");return 0;
}

5. 编译和运行

# 编译示例
gcc -o io_examples example1.c
gcc -o performance_test performance_test.c
gcc -o advanced_examples advanced_examples.c# 运行示例
./io_examples
./performance_test
./advanced_examples

6. 使用建议

6.1 选择指南

场景推荐函数原因
简单顺序读写read/write简单直接
随机位置访问pread/pwrite原子操作,线程安全
多缓冲区操作readv/writev减少系统调用
位置+多缓冲区preadv/pwritev功能最强
需要高级控制preadv2/pwritev2支持标志控制
资源限制管理prlimit64专门用途

6.2 最佳实践

// 安全的 pread 封装
ssize_t safe_pread(int fd, void *buf, size_t count, off_t offset) {if (fd < 0 || !buf || count == 0) {errno = EINVAL;return -1;}ssize_t result;do {result = pread(fd, buf, count, offset);} while (result == -1 && errno == EINTR);return result;
}// 安全的 preadv 封装
ssize_t safe_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) {if (fd < 0 || !iov || iovcnt <= 0) {errno = EINVAL;return -1;}ssize_t result;do {result = preadv(fd, iov, iovcnt, offset);} while (result == -1 && errno == EINTR);return result;
}

这些高级 I/O 系统调用为 Linux 应用程序提供了强大而灵活的文件操作能力,正确使用它们可以显著提高程序的性能和可靠性。

http://www.dtcms.com/a/311505.html

相关文章:

  • Vue 响应式基础全解析2
  • Node.js中path模块的使用指南
  • InfluxDB 与 Node.js 框架:Express 集成方案(二)
  • 如何在`<link type=“icon“ href=`的`href`中写SVG并使用path标签? 笔记250802
  • 嵌入式 C 语言入门:递归与变量作用域学习笔记 —— 从概念到内存特性
  • 深入 Go 底层原理(十三):interface 的内部表示与动态派发
  • Javaweb————Apache Tomcat服务器介绍及Windows,Linux,MAC三种系统搭建Apache Tomcat
  • 技术文章:覆铜板的阻燃性
  • UniappDay07
  • 【AI】AIService(基本使用与指令定制)
  • cv快速input
  • 【云计算】云主机的亲和性策略(三):云主机 宿主机
  • Springboot原理和Maven高级
  • 操作系统:远程过程调用( Remote Procedure Call,RPC)
  • MQTT 入门教程:三步从 Docker 部署到 Java 客户端实现
  • Linux基础学习笔记二
  • MySQL PostgreSQL JDBC URL 配置允许批量操作
  • C语言输入安全10大边界漏洞解析与防御
  • 基于LSTM模型与加权链路预测的动态热门商品成长性分析
  • SpringBoot相关注解
  • 项目管理平台是什么?概念、定义、作用、主流厂商解读
  • docker:将python开发的大模型应用,打成docker容器
  • C#中的除法
  • PostGIS面试题及详细答案120道之 (081-090 )
  • cuda编程笔记(12)--学习cuFFT的简单使用
  • 【Mybatis】MyBatis分页的三种实现方式,Log4j的使用
  • Elasticsearch 混合检索一句 `retriever.rrf`,把语义召回与关键词召回融合到极致
  • 模拟激光相机工作站版本6.0 5.2.32 6.0.44 6.031 5.2.20
  • 题解:P4447 [AHOI2018初中组] 分组
  • 归并排序(简单讲解)