preadv 函数
1. 函数介绍
preadv
是 pread
的分散读取版本,它允许一次性从文件的指定位置读取数据到多个不连续的缓冲区中。这是分散/聚集I/O操作的一部分。
2. 函数原型
#define _GNU_SOURCE
#include <sys/uio.h>
ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
3. 功能
从文件描述符 fd
指定的文件中,从 offset
位置开始读取数据到由 iov
描述的多个缓冲区中。该操作不会改变文件的当前读写位置。
4. 参数
- int fd: 文件描述符,必须是已打开的文件
- *const struct iovec iov: iovec结构体数组,描述多个缓冲区
- int iovcnt: iov数组中的元素个数
- off_t offset: 文件中的偏移量(从文件开始处计算)
5. 返回值
- 成功: 返回实际读取的总字节数
- 文件末尾: 返回0
- 失败: 返回-1,并设置errno
6. 相似函数,或关联函数
- readv: 基本的分散读取函数
- pread: 单缓冲区定位读取函数
- pwritev: 对应的写入函数
7. 示例代码
#define _GNU_SOURCE
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/**
* 使用preadv进行分散读取
*/
int demo_preadv_basic() {
int fd;
struct iovec iov[3];
char buf1[20], buf2[15], buf3[30];
ssize_t total_bytes;
// 创建测试文件
fd = open("test_preadv.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}
// 写入测试数据
const char *test_data = "This is a long test string for preadv demonstration purposes.";
write(fd, test_data, strlen(test_data));
close(fd);
// 打开文件进行分散读取
fd = open("test_preadv.txt", O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
return -1;
}
printf("=== preadv 基本使用示例 ===\n");
printf("测试数据: %s\n", test_data);
printf("数据长度: %zu 字节\n\n", strlen(test_data));
// 设置iovec数组
iov[0].iov_base = buf1;
iov[0].iov_len = sizeof(buf1) - 1;
iov[1].iov_base = buf2;
iov[1].iov_len = sizeof(buf2) - 1;
iov[2].iov_base = buf3;
iov[2].iov_len = sizeof(buf3) - 1;
// 从偏移量0开始分散读取
total_bytes = preadv(fd, iov, 3, 0);
if (total_bytes == -1) {
perror("preadv 失败");
close(fd);
return -1;
}
printf("preadv 读取了 %zd 字节到3个缓冲区:\n", total_bytes);
// 添加字符串结束符并显示结果
buf1[iov[0].iov_len] = '\0';
buf2[iov[1].iov_len] = '\0';
buf3[iov[2].iov_len] = '\0';
printf("缓冲区1 (%zu字节): %s\n", iov[0].iov_len, buf1);
printf("缓冲区2 (%zu字节): %s\n", iov[1].iov_len, buf2);
printf("缓冲区3 (%zu字节): %s\n", iov[2].iov_len, buf3);
close(fd);
unlink("test_preadv.txt");
return 0;
}
/**
* 演示preadv读取结构体数据
*/
struct Person {
int id;
char name[20];
float score;
};
int demo_preadv_struct() {
int fd;
struct iovec iov[3];
struct Person person = {1001, "Alice Johnson", 95.5};
int read_id;
char read_name[20];
float read_score;
printf("\n=== preadv 读取结构体数据示例 ===\n");
// 创建包含结构体数据的文件
fd = open("person_data.bin", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建数据文件失败");
return -1;
}
write(fd, &person, sizeof(person));
close(fd);
// 使用preadv分别读取结构体的各个字段
fd = open("person_data.bin", O_RDONLY);
if (fd == -1) {
perror("打开数据文件失败");
return -1;
}
// 设置iovec读取结构体的各个部分
iov[0].iov_base = &read_id;
iov[0].iov_len = sizeof(read_id);
iov[1].iov_base = read_name;
iov[1].iov_len = sizeof(read_name);
iov[2].iov_base = &read_score;
iov[2].iov_len = sizeof(read_score);
ssize_t bytes_read = preadv(fd, iov, 3, 0);
if (bytes_read == -1) {
perror("preadv 读取结构体失败");
close(fd);
return -1;
}
printf("读取了 %zd 字节的结构体数据:\n", bytes_read);
printf("ID: %d\n", read_id);
printf("Name: %s\n", read_name);
printf("Score: %.1f\n", read_score);
close(fd);
unlink("person_data.bin");
return 0;
}
int main() {
if (demo_preadv_basic() == 0) {
demo_preadv_struct();
printf("\n=== preadv 使用总结 ===\n");
printf("优点:一次系统调用读取多个缓冲区,减少系统调用开销\n");
printf("适用场景:读取结构化数据,协议解析,网络数据包处理\n");
}
return 0;
}