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/ORWF_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; // 1MB
new_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); // 1MB
for (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 应用程序提供了强大而灵活的文件操作能力,正确使用它们可以显著提高程序的性能和可靠性。