preadv1系统调用及示例

preadv 函数

(https://www.calcguide.tech/2025/08/16/preadv1%e7%b3%bb%e7%bb%9f%e8%b0%83%e7%94%a8%e5%8f%8a%e7%a4%ba%e4%be%8b/)

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;
}
此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注