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

  1. 概述

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

  1. 系统调用详细介绍

2.1 pread/pwrite - 位置指定读写

1
2
3
4
5
#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: 向指定位置写入数据,不改变文件指针位置

优势:

  • 原子操作(读/写 + 位置指定)

  • 线程安全

  • 不影响其他读写操作

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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&#91;20];
ssize_t bytes_read = pread(fd, buffer, 10, 0);
buffer&#91;bytes_read] = '\0';
printf("读取内容: %s\n", buffer);

close(fd);
return 0;
}

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

1
2
3
4
5
#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 结构体:

1
2
3
4
5
struct iovec {
void *iov_base; // 缓冲区地址
size_t iov_len; // 缓冲区长度
};

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#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&#91;3];
char *data1 = "First part ";
char *data2 = "Second part ";
char *data3 = "Third part\n";

iov_write&#91;0].iov_base = data1;
iov_write&#91;0].iov_len = strlen(data1);
iov_write&#91;1].iov_base = data2;
iov_write&#91;1].iov_len = strlen(data2);
iov_write&#91;2].iov_base = data3;
iov_write&#91;2].iov_len = strlen(data3);

// 分散写入
pwritev(fd, iov_write, 3, 0);

// 分散读取
struct iovec iov_read&#91;3];
char buf1&#91;20], buf2&#91;20], buf3&#91;20];

iov_read&#91;0].iov_base = buf1;
iov_read&#91;0].iov_len = sizeof(buf1) - 1;
iov_read&#91;1].iov_base = buf2;
iov_read&#91;1].iov_len = sizeof(buf2) - 1;
iov_read&#91;2].iov_base = buf3;
iov_read&#91;2].iov_len = sizeof(buf3) - 1;

ssize_t total_bytes = preadv(fd, iov_read, 3, 0);
printf("总共读取 %zd 字节\n", total_bytes);

buf1&#91;iov_read&#91;0].iov_len] = '\0';
buf2&#91;iov_read&#91;1].iov_len] = '\0';
buf3&#91;iov_read&#91;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

1
2
3
4
5
6
7
8
#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: 追加模式

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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&#91;2];
char *data1 = "Enhanced ";
char *data2 = "I/O operation\n";

iov&#91;0].iov_base = data1;
iov&#91;0].iov_len = strlen(data1);
iov&#91;1].iov_base = data2;
iov&#91;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 - 资源限制控制

1
2
3
4
5
6
#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: 栈大小限制

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#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;
}

  1. 性能对比测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#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;
}

  1. 实际应用场景

4.1 数据库存储引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <sys/uio.h>
#include <fcntl.h>

// 模拟数据库页读取
typedef struct {
int page_id;
char data&#91;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&#91;i].iov_base = &pages&#91;i];
iov&#91;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 网络协议处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#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&#91;1024];
} packet_payload_t;

int process_network_packets(int socket_fd) {
// 准备接收多个网络包
struct iovec iov&#91;5]; // 最多5个包
packet_header_t headers&#91;5];
packet_payload_t payloads&#91;5];

// 设置分散接收缓冲区
for (int i = 0; i < 5; i++) {
iov&#91;i*2].iov_base = &headers&#91;i];
iov&#91;i*2].iov_len = sizeof(packet_header_t);

iov&#91;i*2+1].iov_base = &payloads&#91;i];
iov&#91;i*2+1].iov_len = sizeof(packet_payload_t);
}

// 一次性接收多个包(简化示例)
printf("准备接收网络数据包...\n");
return 0;
}

  1. 编译和运行
1
2
3
4
5
6
7
8
9
10
# 编译示例
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

  1. 使用建议

6.1 选择指南

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

6.2 最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 安全的 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 应用程序提供了强大而灵活的文件操作能力,正确使用它们可以显著提高程序的性能和可靠性。

data-ad-format="auto" data-full-width-responsive="true">