基础 IO - O_APPEND 详解

一、效果

追加写,每次调用 write() 写数据前,文件偏移量都会被定位到文件末尾,效果等同于调用了 lseek()

注意

文件偏移量的调整和写入操作会作为一个原子步骤执行

二、实操验证

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main()
{
  const char *filename = "test.txt";
  const char *content = "O_TRUNC\n";
  int         fd, len;

  fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
  if (fd == -1)
  {
    perror("open");
    return EXIT_FAILURE;
  }

  len = strlen(content);

  if (len != write(fd, content, len))
  {
    perror("write");
    return EXIT_FAILURE;
  }

  close(fd);

  return EXIT_SUCCESS;
}
$ cat test.txt
aaaaaaaa
$ ./O_APPEND_test
$ cat test.txt
aaaaaaaa
O_APPEND
$
<think>我们被要求详细解释 `io_uring_prep_read` 函数。这个函数是 `liburing` 库中的一个辅助函数,用于准备一个读取操作的 SQE(提交队列条目)。 根据用户之前的上下文,我们已经介绍了 `io_uring_get_sqe`,而 `io_uring_prep_read` 就是用来设置这个 SQE 的。 注意:用户提供的引用中并没有直接关于 `io_uring_prep_read` 的说明,但我们可以根据对 io_uring 的理解和标准文档来回答。 函数原型(来自 liburing 库): void io_uring_prep_read(struct io_uring_sqe *sqe, int fd, void *buf, unsigned nbytes, off_t offset); 参数说明: sqe: 指向由 `io_uring_get_sqe` 获取的 SQE 的指针。 fd: 要读取的文件描述符。 buf: 用于存放读取数据的缓冲区地址。 nbytes: 要读取的字节数。 offset: 读取的起始偏移量(对于常规文件,从文件开头计算的偏移量)。 功能:该函数将 SQE 设置为一个读取操作(相当于 pread 系统调用)。它不会提交该操作,只是填充 SQE 的各个字段。 内部操作(大致): 1. 将 sqe 的 opcode 设置为 IORING_OP_READ。 2. 设置 fd、buf、nbytes、offset 到 SQE 的相应字段。 3. 将其他相关字段(如用户数据 user_data、标志 flags 等)设置为默认值(通常为0),但注意,这个函数不会修改 user_data,因此用户通常需要单独设置。 注意: 1. 该函数不会检查参数的有效性(例如 fd 是否有效,buf 是否可写等),这些由内核在执行时检查。 2. 在调用该函数后,用户还可以设置 SQE 的其他字段,例如 user_data(用于在完成事件中标识请求)或者 flags(如 IOSQE_IO_LINK 用于链接操作)等。 3. 该函数只是准备操作,必须调用 `io_uring_submit` 提交到内核才会执行。 示例: struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); io_uring_prep_read(sqe, fd, buf, 4096, 0); sqe->user_data = (uintptr_t) my_data; // 可选:设置用户数据 io_uring_submit(&ring); 与系统调用的区别:io_uring_prep_read 对应的是 pread 系统调用(带偏移量),而不是 read(使用文件指针的当前位置)。如果需要使用当前文件位置,可以使用 `io_uring_prep_readv`(但注意,readv 是向量读写,单缓冲区也可以用)或者使用不带偏移量的版本?实际上,liburing 没有提供直接对应于 read(使用当前偏移)的函数,因为 pread 更明确。如果需要在当前偏移量读取,可以在打开文件时设置 O_APPEND?或者使用 lseek 获取当前位置?但更常见的做法是使用偏移量。 另外,io_uring 支持异步的 lseek 操作(IORING_OP_LSEEK),可以结合使用。 重要:在设置 SQE 时,必须确保在提交之前,该 SQE 所使用的缓冲区(buf)和文件描述符(fd)保持有效,直到操作完成。 错误处理:在提交后,完成事件(CQE)中会返回操作结果(读取的字节数或错误码)。 根据用户提供的引用[1],我们知道 io_uring 可以直接执行 I/O 操作(如读、写),而 epoll 则不能。 现在,我们按照要求组织答案: </think> ### `io_uring_prep_read()` 函数详解 `io_uring_prep_read()` 是 `liburing` 库中用于**初始化读操作 SQE(Submission Queue Entry)** 的辅助函数。它将 SQE 配置为异步文件读取操作,相当于异步版的 `pread()` 系统调用[^1]。 --- #### 函数原型 ```c void io_uring_prep_read( struct io_uring_sqe *sqe, // 目标 SQE 指针 int fd, // 文件描述符 void *buf, // 数据缓冲区 unsigned nbytes, // 读取字节数 off_t offset // 文件偏移量 ); ``` --- #### 参数说明 | 参数 | 类型 | 说明 | |---------------|-----------------------|----------------------------------------------------------------------| | **`sqe`** | `struct io_uring_sqe*`| 由 `io_uring_get_sqe()` 获取的 SQE 指针[^4] | | **`fd`** | `int` | 要读取的文件描述符(支持文件、套接字等) | | **`buf`** | `void*` | 数据存储缓冲区地址(需在操作完成前保持有效) | | **`nbytes`** | `unsigned` | 最大读取字节数 | | **`offset`** | `off_t` | 文件读取起始偏移量(字节) | --- #### 内部操作流程 当调用此函数时,它会执行以下关键配置: 1. **设置操作类型** 将 `sqe->opcode` 赋值为 `IORING_OP_READ`,表示异步读操作。 2. **填充参数** 将函数参数(`fd`, `buf`, `nbytes`, `offset`)映射到 SQE 的对应字段: ```c sqe->fd = fd; sqe->addr = (unsigned long)buf; sqe->len = nbytes; sqe->off = offset; ``` 3. **重置标志位** 清除 `sqe->flags` 和 `sqe->user_data`(用户需手动设置额外标志或用户数据)[^3]。 --- #### 使用示例 ```c #include <liburing.h> int main() { struct io_uring ring; io_uring_queue_init(128, &ring, 0); // 初始化 io_uring int fd = open("data.txt", O_RDONLY); // 打开文件 char buffer[4096]; // 数据缓冲区 // 获取并配置 SQE struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); io_uring_prep_read(sqe, fd, buffer, sizeof(buffer), 0); // 从偏移量0读取 sqe->user_data = 0x1234; // 设置用户标识符 // 提交并等待完成 io_uring_submit(&ring); struct io_uring_cqe *cqe; io_uring_wait_cqe(&ring, &cqe); // 等待操作完成 // 检查结果 if (cqe->res > 0) { printf("Read %d bytes\n", cqe->res); } else { perror("Read error"); } io_uring_queue_exit(&ring); // 清理资源 } ``` --- #### 关键注意事项 1. **缓冲区生命周期** 缓冲区 `buf` 必须保持有效,直到操作完成(通过 CQE 确认)。通常使用堆内存或注册的固定缓冲区。 2. **偏移量行为** - 对于**常规文件**:`offset` 指定精确读取位置(类似 `pread`)[^1]。 - 对于**流式文件**(如套接字):`offset` 被忽略(类似 `read`)。 3. **错误处理** 操作结果通过完成队列项(CQE)返回: - `cqe->res > 0`:实际读取的字节数 - `cqe->res < 0`:错误码(如 `-EBADF` 表示无效文件描述符) 4. **性能优化** - 结合 `IORING_SETUP_IOPOLL` 标志初始化 `io_uring`,可启用轮询模式减少延迟。 - 使用 `io_uring_register()` 注册固定缓冲区和文件描述符,避免内核映射开销。 --- #### 与相关函数对比 | 函数 | 作用 | 特点 | |--------------------------|-------------------------------|----------------------------------------------------------------------| | `io_uring_prep_readv()` | 准备分散读(多缓冲区) | 使用 `iovec` 结构支持非连续缓冲区 | | `io_uring_prep_read_fixed()` | 使用注册的固定缓冲区读取 | 需预先注册缓冲区,零拷贝优化 | | `io_uring_prep_write()` | 初始化写操作 | 参数结构与 `read` 类似,操作码为 `IORING_OP_WRITE` | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值