io_cancel系统调用及示例
io_cancel 函数详解
1. 函数介绍
io_cancel
是Linux传统异步I/O (AIO) 系统调用,用于取消先前提交但尚未完成的异步I/O操作。它是AIO框架的重要组成部分,允许应用程序在必要时取消正在进行的异步操作,提供更灵活的I/O控制能力。
2. 函数原型
#include <linux/aio_abi.h>
int io_cancel(aio_context_t ctx_id, struct iocb *iocb, struct io_event *result);
3. 功能
io_cancel
尝试取消指定的异步I/O操作。如果操作仍在排队或尚未开始执行,则可以成功取消;如果操作已经开始执行或已经完成,则取消可能失败。成功取消的操作会返回一个带有ECANCELED错误码的完成事件。
4. 参数
- aio_context_t ctx_id: AIO上下文ID(由io_setup创建)
- *struct iocb iocb: 要取消的异步I/O控制块指针
- *struct io_event result: 用于存储取消结果的事件结构指针
5. 返回值
- 成功: 返回0
- 失败: 返回负的错误码
6. 相似函数,或关联函数
-
io_setup: 初始化AIO上下文
-
io_destroy: 销毁AIO上下文
-
io_submit: 提交异步I/O操作
-
io_getevents: 获取完成的异步I/O事件
-
io_pgetevents: 带信号处理的事件获取
7. 示例代码
示例1:基础io_cancel使用
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>/*** 系统调用包装函数*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {return syscall(__NR_io_setup, nr_events, ctxp);
}static inline int io_destroy(aio_context_t ctx) {return syscall(__NR_io_destroy, ctx);
}static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {return syscall(__NR_io_submit, ctx, nr, iocbpp);
}static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {return syscall(__NR_io_cancel, ctx, iocb, result);
}static inline int io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout) {return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}/*** 演示基础io_cancel使用方法*/
int demo_io_cancel_basic() {aio_context_t ctx;struct iocb iocb;struct io_event result;int fd;int ret;printf("=== 基础io_cancel使用示例 ===\n");// 初始化AIO上下文printf("1. 初始化AIO上下文:\n");ctx = 0;ret = io_setup(128, &ctx);if (ret < 0) {printf(" 初始化AIO上下文失败: %s\n", strerror(-ret));return -1;}printf(" ✓ AIO上下文初始化成功\n");printf(" 上下文ID: %llu\n", (unsigned long long)ctx);// 创建测试文件printf("\n2. 创建测试文件:\n");const char *filename = "aio_cancel_test.txt";fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);if (fd == -1) {perror(" 创建测试文件失败");io_destroy(ctx);return -1;}printf(" ✓ 测试文件创建成功: %s\n", filename);// 准备异步写入操作printf("\n3. 准备异步写入操作:\n");char *test_data = "This is test data for AIO cancel operation.\n";size_t data_size = strlen(test_data);// 初始化iocb结构memset(&iocb, 0, sizeof(iocb));iocb.aio_data = 12345; // 用户数据iocb.aio_key = 0;iocb.aio_rw_flags = 0;iocb.aio_lio_opcode = IOCB_CMD_PWRITE; // 异步写入iocb.aio_reqprio = 0;iocb.aio_fildes = fd;iocb.aio_buf = (uint64_t)(uintptr_t)test_data;iocb.aio_nbytes = data_size;iocb.aio_offset = 0;iocb.aio_flags = 0;iocb.aio_resfd = 0;printf(" 准备异步写入操作:\n");printf(" 文件描述符: %d\n", fd);printf(" 数据大小: %zu 字节\n", data_size);printf(" 用户数据: %llu\n", (unsigned long long)iocb.aio_data);// 提交异步操作printf("\n4. 提交异步操作:\n");struct iocb *iocbs[1] = {&iocb};ret = io_submit(ctx, 1, iocbs);if (ret != 1) {printf(" 提交异步操作失败: %s\n", strerror(-ret));close(fd);unlink(filename);io_destroy(ctx);return -1;}printf(" ✓ 异步操作提交成功\n");// 立即尝试取消操作printf("\n5. 立即尝试取消操作:\n");ret = io_cancel(ctx, &iocb, &result);if (ret == 0) {printf(" ✓ 操作取消成功\n");printf(" 取消结果:\n");printf(" 用户数据: %llu\n", (unsigned long long)result.data);printf(" 结果: %ld\n", result.res);printf(" 结果2: %ld\n", result.res2);if (result.res == -ECANCELED) {printf(" 状态: 操作已取消 (ECANCELED)\n");}} else if (ret == -EAGAIN) {printf(" ℹ 操作无法取消 (可能已开始执行或已完成)\n");// 等待操作完成printf(" 等待操作完成...\n");struct io_event events[1];ret = io_getevents(ctx, 1, 1, events, NULL);if (ret > 0) {printf(" ✓ 操作完成\n");printf(" 用户数据: %llu\n", (unsigned long long)events[0].data);printf(" 结果: %ld 字节\n", events[0].res);}} else {printf(" ✗ 取消操作失败: %s\n", strerror(-ret));}// 演示延时取消printf("\n6. 演示延时取消:\n");// 创建一个更大的写入操作char *large_data = malloc(1024 * 1024); // 1MB数据if (large_data) {// 填充数据for (int i = 0; i < 1024 * 1024; i++) {large_data[i] = 'A' + (i % 26);}// 准备新的iocbstruct iocb large_iocb;memset(&large_iocb, 0, sizeof(large_iocb));large_iocb.aio_data = 54321;large_iocb.aio_lio_opcode = IOCB_CMD_PWRITE;large_iocb.aio_fildes = fd;large_iocb.aio_buf = (uint64_t)(uintptr_t)large_data;large_iocb.aio_nbytes = 1024 * 1024;large_iocb.aio_offset = data_size; // 在之前数据之后printf(" 准备大块数据写入操作 (1MB)\n");struct iocb *large_iocbs[1] = {&large_iocb};ret = io_submit(ctx, 1, large_iocbs);if (ret == 1) {printf(" ✓ 大块数据写入操作提交成功\n");// 短暂延迟后尝试取消printf(" 等待100ms后尝试取消...\n");usleep(100000); // 100msstruct io_event cancel_result;ret = io_cancel(ctx, &large_iocb, &cancel_result);if (ret == 0) {printf(" ✓ 大块数据写入操作取消成功\n");} else if (ret == -EAGAIN) {printf(" ℹ 大块数据写入操作无法取消\n");// 等待操作完成struct io_event events[1];int wait_ret = io_getevents(ctx, 1, 1, events, NULL);if (wait_ret > 0) {printf(" ✓ 大块数据写入完成: %ld 字节\n", events[0].res);}} else {printf(" ✗ 取消大块数据写入失败: %s\n", strerror(-ret));}}free(large_data);}// 清理资源printf("\n7. 清理资源:\n");close(fd);unlink(filename);io_destroy(ctx);printf(" ✓ 资源清理完成\n");return 0;
}int main() {return demo_io_cancel_basic();
}
示例2:批量操作取消
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>/*** 系统调用包装函数*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {return syscall(__NR_io_setup, nr_events, ctxp);
}static inline int io_destroy(aio_context_t ctx) {return syscall(__NR_io_destroy, ctx);
}static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {return syscall(__NR_io_submit, ctx, nr, iocbpp);
}static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {return syscall(__NR_io_cancel, ctx, iocb, result);
}static inline int io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout) {return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}/*** 批量操作取消演示*/
int demo_batch_cancel() {aio_context_t ctx;const int batch_size = 8;struct iocb iocbs[batch_size];struct io_event results[batch_size];int fd;int ret;char *test_files[batch_size];printf("=== 批量操作取消演示 ===\n");// 初始化AIO上下文printf("1. 初始化AIO上下文:\n");ctx = 0;ret = io_setup(128, &ctx);if (ret < 0) {printf(" 初始化AIO上下文失败: %s\n", strerror(-ret));return -1;}printf(" ✓ AIO上下文初始化成功\n");// 创建测试文件printf("\n2. 创建测试文件:\n");for (int i = 0; i < batch_size; i++) {char filename[32];snprintf(filename, sizeof(filename), "batch_cancel_%d.txt", i);test_files[i] = strdup(filename);fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);if (fd != -1) {// 写入一些初始数据使文件不为空char initial_data[256];memset(initial_data, 'A' + i, sizeof(initial_data) - 1);initial_data[sizeof(initial_data) - 1] = '\0';write(fd, initial_data, sizeof(initial_data) - 1);close(fd);printf(" 创建测试文件 %d: %s\n", i, filename);}}// 准备批量异步读取操作printf("\n3. 准备批量异步读取操作:\n");struct iocb *iocb_ptrs[batch_size];for (int i = 0; i < batch_size; i++) {fd = open(test_files[i], O_RDONLY);if (fd == -1) {printf(" 打开文件 %s 失败\n", test_files[i]);continue;}// 初始化iocbmemset(&iocbs[i], 0, sizeof(iocbs[i]));iocbs[i].aio_data = i + 1; // 使用索引作为用户数据iocbs[i].aio_lio_opcode = IOCB_CMD_PREAD;iocbs[i].aio_fildes = fd;// 分配读取缓冲区char *read_buffer = malloc(1024);if (read_buffer) {iocbs[i].aio_buf = (uint64_t)(uintptr_t)read_buffer;iocbs[i].aio_nbytes = 1024;iocbs[i].aio_offset = 0;}iocb_ptrs[i] = &iocbs[i];printf(" 准备读取操作 %d: 文件 %s\n", i + 1, test_files[i]);}// 提交批量操作printf("\n4. 提交批量异步操作:\n");ret = io_submit(ctx, batch_size, iocb_ptrs);if (ret < 0) {printf(" 提交批量操作失败: %s\n", strerror(-ret));goto cleanup;}printf(" ✓ 成功提交 %d 个异步操作\n", ret);// 演示部分取消printf("\n5. 演示部分操作取消:\n");int cancel_count = 0;for (int i = 0; i < batch_size; i += 2) { // 取消偶数索引的操作ret = io_cancel(ctx, &iocbs[i], &results[cancel_count]);if (ret == 0) {printf(" ✓ 操作 %d 取消成功\n", (int)iocbs[i].aio_data);cancel_count++;} else if (ret == -EAGAIN) {printf(" ℹ 操作 %d 无法取消 (可能已开始执行)\n", (int)iocbs[i].aio_data);} else {printf(" ✗ 操作 %d 取消失败: %s\n", (int)iocbs[i].aio_data, strerror(-ret));}}printf(" 总共尝试取消 %d 个操作\n", cancel_count);// 等待剩余操作完成printf("\n6. 等待剩余操作完成:\n");struct io_event completed_events[batch_size];int completed_count = 0;// 等待最多5秒struct timespec timeout = {5, 0};ret = io_getevents(ctx, 1, batch_size, completed_events, &timeout);if (ret > 0) {printf(" ✓ 收到 %d 个完成事件\n", ret);completed_count = ret;for (int i = 0; i < ret; i++) {printf(" 操作 %llu 完成: %ld 字节\n", (unsigned long long)completed_events[i].data,completed_events[i].res);}} else if (ret == 0) {printf(" ⚠ 超时,没有收到完成事件\n");} else {printf(" ✗ 等待完成事件失败: %s\n", strerror(-ret));}// 显示取消结果printf("\n7. 取消结果统计:\n");printf(" 尝试取消的操作数: %d\n", cancel_count);printf(" 完成的操作数: %d\n", completed_count);printf(" 总操作数: %d\n", batch_size);// 清理资源
cleanup:printf("\n8. 清理资源:\n");// 关闭文件描述符和释放缓冲区for (int i = 0; i < batch_size; i++) {if (iocbs[i].aio_fildes > 0) {close(iocbs[i].aio_fildes);}if (iocbs[i].aio_buf) {free((void*)(uintptr_t)iocbs[i].aio_buf);}if (test_files[i]) {unlink(test_files[i]);free(test_files[i]);}}io_destroy(ctx);printf(" ✓ 所有资源清理完成\n");return 0;
}int main() {return demo_batch_cancel();
}
示例3:超时取消机制
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>/*** 系统调用包装函数*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {return syscall(__NR_io_setup, nr_events, ctxp);
}static inline int io_destroy(aio_context_t ctx) {return syscall(__NR_io_destroy, ctx);
}static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {return syscall(__NR_io_submit, ctx, nr, iocbpp);
}static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {return syscall(__NR_io_cancel, ctx, iocb, result);
}static inline int io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout) {return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}/*** 超时取消上下文*/
typedef struct {aio_context_t ctx;struct iocb *iocb;int is_completed;int is_cancelled;time_t start_time;int timeout_seconds;
} timeout_cancel_ctx_t;/*** 超时处理函数*/
void timeout_handler(int sig) {printf("收到超时信号\n");
}/*** 演示超时取消机制*/
int demo_timeout_cancel_mechanism() {aio_context_t ctx;struct iocb iocb;struct io_event result;int fd;int ret;printf("=== 超时取消机制演示 ===\n");// 初始化AIO上下文printf("1. 初始化AIO上下文:\n");ctx = 0;ret = io_setup(64, &ctx);if (ret < 0) {printf(" 初始化AIO上下文失败: %s\n", strerror(-ret));return -1;}printf(" ✓ AIO上下文初始化成功\n");// 创建测试文件printf("\n2. 创建测试文件:\n");const char *filename = "timeout_test.txt";fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);if (fd == -1) {perror(" 创建测试文件失败");io_destroy(ctx);return -1;}// 写入大量数据char *large_data = malloc(10 * 1024 * 1024); // 10MB数据if (large_data) {for (int i = 0; i < 10 * 1024 * 1024; i++) {large_data[i] = 'A' + (i % 26);}write(fd, large_data, 10 * 1024 * 1024);}close(fd);printf(" ✓ 创建了10MB测试文件: %s\n", filename);// 重新打开文件进行读取fd = open(filename, O_RDONLY);if (fd == -1) {perror(" 打开测试文件失败");io_destroy(ctx);unlink(filename);return -1;}// 准备长时间读取操作printf("\n3. 准备长时间读取操作:\n");char *read_buffer = malloc(5 * 1024 * 1024); // 5MB缓冲区if (!read_buffer) {perror(" 分配读取缓冲区失败");close(fd);io_destroy(ctx);unlink(filename);return -1;}// 初始化iocbmemset(&iocb, 0, sizeof(iocb));iocb.aio_data = 99999;iocb.aio_lio_opcode = IOCB_CMD_PREAD;iocb.aio_fildes = fd;iocb.aio_buf = (uint64_t)(uintptr_t)read_buffer;iocb.aio_nbytes = 5 * 1024 * 1024; // 读取5MBiocb.aio_offset = 0;printf(" 准备读取5MB数据\n");// 提交异步读取操作struct iocb *iocbs[1] = {&iocb};ret = io_submit(ctx, 1, iocbs);if (ret != 1) {printf(" 提交异步读取操作失败: %s\n", strerror(-ret));free(read_buffer);close(fd);io_destroy(ctx);unlink(filename);return -1;}printf(" ✓ 异步读取操作提交成功\n");// 设置超时处理printf("\n4. 设置超时处理:\n");time_t start_time = time(NULL);const int timeout_seconds = 3;printf(" 设置超时时间: %d 秒\n", timeout_seconds);// 等待操作完成或超时printf("\n5. 等待操作完成或超时:\n");struct io_event events[1];struct timespec timeout = {1, 0}; // 1秒超时int operation_completed = 0;int cancel_attempted = 0;while (difftime(time(NULL), start_time) < timeout_seconds) {ret = io_getevents(ctx, 0, 1, events, &timeout);if (ret > 0) {printf(" ✓ 操作在超时前完成\n");printf(" 读取字节数: %ld\n", events[0].res);operation_completed = 1;break;} else if (ret == 0) {printf(" 操作仍在进行中...\n");// 每秒检查一次是否需要取消if (!cancel_attempted && difftime(time(NULL), start_time) >= 2) {printf(" 2秒后尝试取消操作\n");struct io_event cancel_result;ret = io_cancel(ctx, &iocb, &cancel_result);if (ret == 0) {printf(" ✓ 操作取消成功\n");cancel_attempted = 1;break;} else if (ret == -EAGAIN) {printf(" ℹ 操作无法取消\n");cancel_attempted = 1;} else {printf(" ✗ 取消操作失败: %s\n", strerror(-ret));cancel_attempted = 1;}}} else {printf(" ✗ 等待事件失败: %s\n", strerror(-ret));break;}}// 超时后的处理if (!operation_completed && !cancel_attempted) {printf("\n6. 超时处理:\n");printf(" 操作超时 (%d 秒)\n", timeout_seconds);// 强制取消操作printf(" 强制取消操作...\n");struct io_event cancel_result;ret = io_cancel(ctx, &iocb, &cancel_result);if (ret == 0) {printf(" ✓ 操作取消成功\n");} else if (ret == -EAGAIN) {printf(" ℹ 操作无法取消\n");} else {printf(" ✗ 取消操作失败: %s\n", strerror(-ret));}}// 等待最终结果printf("\n7. 等待最终结果:\n");timeout.tv_sec = 2; // 2秒超时timeout.tv_nsec = 0;ret = io_getevents(ctx, 0, 1, events, &timeout);if (ret > 0) {printf(" 最终结果:\n");printf(" 用户数据: %llu\n", (unsigned long long)events[0].data);printf(" 结果: %ld\n", events[0].res);if (events[0].res == -ECANCELED) {printf(" 状态: 操作已取消\n");} else if (events[0].res < 0) {printf(" 错误: %s\n", strerror(-events[0].res));} else {printf(" 成功读取: %ld 字节\n", events[0].res);}} else if (ret == 0) {printf(" 超时,无结果返回\n");} else {printf(" 等待结果失败: %s\n", strerror(-ret));}// 清理资源printf("\n8. 清理资源:\n");free(read_buffer);close(fd);unlink(filename);io_destroy(ctx);printf(" ✓ 资源清理完成\n");return 0;
}int main() {return demo_timeout_cancel_mechanism();
}
示例4:条件取消策略
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>/*** 系统调用包装函数*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {return syscall(__NR_io_setup, nr_events, ctxp);
}static inline int io_destroy(aio_context_t ctx) {return syscall(__NR_io_destroy, ctx);
}static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {return syscall(__NR_io_submit, ctx, nr, iocbpp);
}static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {return syscall(__NR_io_cancel, ctx, iocb, result);
}static inline int io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout) {return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}/*** 取消策略枚举*/
typedef enum {CANCEL_STRATEGY_IMMEDIATE, // 立即取消CANCEL_STRATEGY_TIMEOUT, // 超时取消CANCEL_STRATEGY_CONDITIONAL, // 条件取消CANCEL_STRATEGY_NEVER // 从不取消
} cancel_strategy_t;/*** 操作上下文*/
typedef struct {struct iocb iocb;int is_submitted;int is_completed;int is_cancelled;time_t submit_time;cancel_strategy_t strategy;int timeout_seconds;int condition_value;
} operation_context_t;/*** 条件取消检查函数*/
int check_cancel_condition(operation_context_t *ctx) {switch (ctx->strategy) {case CANCEL_STRATEGY_IMMEDIATE:return 1; // 立即取消case CANCEL_STRATEGY_TIMEOUT:if (difftime(time(NULL), ctx->submit_time) > ctx->timeout_seconds) {printf(" 操作超时,触发取消\n");return 1;}return 0;case CANCEL_STRATEGY_CONDITIONAL:// 模拟条件检查if (ctx->condition_value > 100) {printf(" 条件满足,触发取消\n");return 1;}return 0;case CANCEL_STRATEGY_NEVER:default:return 0;}
}/*** 演示条件取消策略*/
int demo_conditional_cancel_strategy() {aio_context_t ctx;operation_context_t operations[5];int fd;int ret;printf("=== 条件取消策略演示 ===\n");// 初始化AIO上下文printf("1. 初始化AIO上下文:\n");ctx = 0;ret = io_setup(64, &ctx);if (ret < 0) {printf(" 初始化AIO上下文失败: %s\n", strerror(-ret));return -1;}printf(" ✓ AIO上下文初始化成功\n");// 创建测试文件printf("\n2. 创建测试文件:\n");const char *filename = "conditional_cancel_test.txt";fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);if (fd == -1) {perror(" 创建测试文件失败");io_destroy(ctx);return -1;}// 写入测试数据char *test_data = malloc(1024 * 1024); // 1MB数据if (test_data) {for (int i = 0; i < 1024 * 1024; i++) {test_data[i] = 'A' + (i % 26);}write(fd, test_data, 1024 * 1024);}close(fd);free(test_data);printf(" ✓ 创建了1MB测试文件: %s\n", filename);// 重新打开文件进行读取fd = open(filename, O_RDONLY);if (fd == -1) {perror(" 打开测试文件失败");io_destroy(ctx);unlink(filename);return -1;}// 初始化操作上下文printf("\n3. 初始化操作上下文:\n");for (int i = 0; i < 5; i++) {operation_context_t *op_ctx = &operations[i];memset(op_ctx, 0, sizeof(*op_ctx));// 设置不同的取消策略switch (i % 4) {case 0:op_ctx->strategy = CANCEL_STRATEGY_IMMEDIATE;printf(" 操作 %d: 立即取消策略\n", i + 1);break;case 1:op_ctx->strategy = CANCEL_STRATEGY_TIMEOUT;op_ctx->timeout_seconds = 2; // 2秒超时printf(" 操作 %d: 超时取消策略 (2秒)\n", i + 1);break;case 2:op_ctx->strategy = CANCEL_STRATEGY_CONDITIONAL;op_ctx->condition_value = i * 50; // 不同的条件值printf(" 操作 %d: 条件取消策略 (条件值: %d)\n", i + 1, op_ctx->condition_value);break;case 3:op_ctx->strategy = CANCEL_STRATEGY_NEVER;printf(" 操作 %d: 从不取消策略\n", i + 1);break;}}// 准备异步读取操作printf("\n4. 准备异步读取操作:\n");struct iocb *iocb_ptrs[5];for (int i = 0; i < 5; i++) {operation_context_t *op_ctx = &operations[i];// 初始化iocbmemset(&op_ctx->iocb, 0, sizeof(op_ctx->iocb));op_ctx->iocb.aio_data = i + 1;op_ctx->iocb.aio_lio_opcode = IOCB_CMD_PREAD;op_ctx->iocb.aio_fildes = fd;// 分配读取缓冲区char *read_buffer = malloc(200 * 1024); // 200KB缓冲区if (read_buffer) {op_ctx->iocb.aio_buf = (uint64_t)(uintptr_t)read_buffer;op_ctx->iocb.aio_nbytes = 200 * 1024;op_ctx->iocb.aio_offset = i * 200 * 1024; // 不同的偏移量iocb_ptrs[i] = &op_ctx->iocb;op_ctx->is_submitted = 1;op_ctx->submit_time = time(NULL);printf(" 准备操作 %d: 读取200KB数据\n", i + 1);}}// 提交异步操作printf("\n5. 提交异步操作:\n");ret = io_submit(ctx, 5, iocb_ptrs);if (ret < 0) {printf(" 提交异步操作失败: %s\n", strerror(-ret));goto cleanup;}printf(" ✓ 成功提交 %d 个异步操作\n", ret);// 执行取消策略printf("\n6. 执行取消策略:\n");time_t start_time = time(NULL);int completed_operations = 0;int cancelled_operations = 0;// 监控和取消操作while (difftime(time(NULL), start_time) < 10) { // 最多等待10秒// 检查取消条件for (int i = 0; i < 5; i++) {operation_context_t *op_ctx = &operations[i];if (op_ctx->is_submitted && !op_ctx->is_completed && !op_ctx->is_cancelled) {if (check_cancel_condition(op_ctx)) {printf(" 尝试取消操作 %d\n", i + 1);struct io_event cancel_result;ret = io_cancel(ctx, &op_ctx->iocb, &cancel_result);if (ret == 0) {printf(" ✓ 操作 %d 取消成功\n", i + 1);op_ctx->is_cancelled = 1;cancelled_operations++;} else if (ret == -EAGAIN) {printf(" ℹ 操作 %d 无法取消\n", i + 1);} else {printf(" ✗ 操作 %d 取消失败: %s\n", i + 1, strerror(-ret));}}}}// 检查完成事件struct io_event events[5];struct timespec timeout = {0, 100000000}; // 100ms超时ret = io_getevents(ctx, 0, 5, events, &timeout);if (ret > 0) {printf(" 收到 %d 个完成事件\n", ret);for (int i = 0; i < ret; i++) {int op_index = events[i].data - 1;if (op_index >= 0 && op_index < 5) {operation_context_t *op_ctx = &operations[op_index];op_ctx->is_completed = 1;completed_operations++;printf(" 操作 %d 完成: %ld 字节\n", op_index + 1, events[i].res);}}}// 检查是否所有操作都已完成或取消int all_done = 1;for (int i = 0; i < 5; i++) {operation_context_t *op_ctx = &operations[i];if (op_ctx->is_submitted && !op_ctx->is_completed && !op_ctx->is_cancelled) {all_done = 0;break;}}if (all_done) {printf(" 所有操作已完成或取消\n");break;}// 更新条件值(模拟条件变化)for (int i = 0; i < 5; i++) {operations[i].condition_value += 10;}usleep(100000); // 100ms延迟}// 最终统计printf("\n7. 最终统计:\n");printf(" 总操作数: 5\n");printf(" 完成操作数: %d\n", completed_operations);printf(" 取消操作数: %d\n", cancelled_operations);printf(" 进行中操作数: %d\n", 5 - completed_operations - cancelled_operations);// 显示每个操作的最终状态printf("\n8. 操作状态详情:\n");for (int i = 0; i < 5; i++) {operation_context_t *op_ctx = &operations[i];printf(" 操作 %d: ", i + 1);if (op_ctx->is_cancelled) {printf("已取消 ");} else if (op_ctx->is_completed) {printf("已完成 ");} else {printf("进行中 ");}switch (op_ctx->strategy) {case CANCEL_STRATEGY_IMMEDIATE:printf("(立即取消策略)");break;case CANCEL_STRATEGY_TIMEOUT:printf("(超时取消策略)");break;case CANCEL_STRATEGY_CONDITIONAL:printf("(条件取消策略)");break;case CANCEL_STRATEGY_NEVER:printf("(从不取消策略)");break;}printf("\n");}// 清理资源
cleanup:printf("\n9. 清理资源:\n");// 释放缓冲区for (int i = 0; i < 5; i++) {if (operations[i].iocb.aio_buf) {free((void*)(uintptr_t)operations[i].iocb.aio_buf);}}close(fd);unlink(filename);io_destroy(ctx);printf(" ✓ 资源清理完成\n");return 0;
}int main() {return demo_conditional_cancel_strategy();
}
示例5:错误处理和恢复
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>/*** 系统调用包装函数*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {return syscall(__NR_io_setup, nr_events, ctxp);
}static inline int io_destroy(aio_context_t ctx) {return syscall(__NR_io_destroy, ctx);
}static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {return syscall(__NR_io_submit, ctx, nr, iocbpp);
}static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {return syscall(__NR_io_cancel, ctx, iocb, result);
}static inline int io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout) {return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}/*** 错误处理上下文*/
typedef struct {int error_code;const char *error_message;int retry_count;int max_retries;time_t error_time;const char *operation_name;
} error_context_t;/*** 记录错误信息*/
void record_error(error_context_t *ctx, int error_code, const char *operation, const char *message) {ctx->error_code = error_code;ctx->error_message = message;ctx->error_time = time(NULL);ctx->operation_name = operation;ctx->retry_count++;printf("错误记录: %s\n", operation);printf(" 错误码: %d\n", error_code);printf(" 错误信息: %s\n", message);printf(" 重试次数: %d/%d\n", ctx->retry_count, ctx->max_retries);
}/*** 错误恢复策略*/
int apply_recovery_strategy(error_context_t *ctx, aio_context_t aio_ctx, struct iocb *iocb) {printf("应用错误恢复策略:\n");switch (ctx->error_code) {case -EAGAIN:printf(" EAGAIN错误,操作稍后重试\n");if (ctx->retry_count < ctx->max_retries) {printf(" 重试操作...\n");struct iocb *iocbs[1] = {iocb};int ret = io_submit(aio_ctx, 1, iocbs);if (ret == 1) {printf(" ✓ 重试成功\n");return 0;} else {printf(" ✗ 重试失败: %s\n", strerror(-ret));record_error(ctx, ret, ctx->operation_name, "重试失败");}}break;case -ECANCELED:printf(" ECANCELED错误,操作已被取消\n");printf(" 尝试重新提交操作...\n");struct iocb *iocbs[1] = {iocb};int ret = io_submit(aio_ctx, 1, iocbs);if (ret == 1) {printf(" ✓ 重新提交成功\n");return 0;} else {printf(" ✗ 重新提交失败: %s\n", strerror(-ret));record_error(ctx, ret, ctx->operation_name, "重新提交失败");}break;case -EBADF:printf(" EBADF错误,文件描述符无效\n");printf(" 建议:检查文件描述符有效性\n");break;case -EINVAL:printf(" EINVAL错误,参数无效\n");printf(" 建议:检查参数设置\n");break;default:printf(" 未知错误: %s\n", strerror(-ctx->error_code));printf(" 建议:记录错误并采取适当措施\n");break;}return -1;
}/*** 演示错误处理和恢复*/
int demo_error_handling_recovery() {aio_context_t ctx;struct iocb iocb;struct io_event result;int fd;int ret;error_context_t error_ctx = {0};printf("=== 错误处理和恢复演示 ===\n");// 设置错误处理上下文error_ctx.max_retries = 3;error_ctx.retry_count = 0;// 初始化AIO上下文printf("1. 初始化AIO上下文:\n");ctx = 0;ret = io_setup(32, &ctx);if (ret < 0) {record_error(&error_ctx, ret, "io_setup", "初始化AIO上下文失败");apply_recovery_strategy(&error_ctx, ctx, NULL);return -1;}printf(" ✓ AIO上下文初始化成功\n");// 创建测试文件printf("\n2. 创建测试文件:\n");const char *filename = "error_recovery_test.txt";fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);if (fd == -1) {record_error(&error_ctx, -errno, "open", "创建测试文件失败");io_destroy(ctx);return -1;}printf(" ✓ 测试文件创建成功\n");// 准备异步写入操作printf("\n3. 准备异步写入操作:\n");const char *test_data = "Error handling and recovery test data.\n";size_t data_size = strlen(test_data);memset(&iocb, 0, sizeof(iocb));iocb.aio_data = 1001;iocb.aio_lio_opcode = IOCB_CMD_PWRITE;iocb.aio_fildes = fd;iocb.aio_buf = (uint64_t)(uintptr_t)test_data;iocb.aio_nbytes = data_size;iocb.aio_offset = 0;printf(" 准备写入操作:\n");printf(" 数据大小: %zu 字节\n", data_size);printf(" 用户数据: %llu\n", (unsigned long long)iocb.aio_data);// 提交异步操作printf("\n4. 提交异步操作:\n");struct iocb *iocbs[1] = {&iocb};ret = io_submit(ctx, 1, iocbs);if (ret != 1) {record_error(&error_ctx, ret, "io_submit", "提交异步操作失败");if (apply_recovery_strategy(&error_ctx, ctx, &iocb) != 0) {close(fd);unlink(filename);io_destroy(ctx);return -1;}}printf(" ✓ 异步操作提交成功\n");// 演示各种错误场景printf("\n5. 演示错误场景:\n");// 场景1: 尝试取消已完成的操作printf(" 场景1: 尝试取消已完成的操作\n");struct io_event events[1];struct timespec timeout = {2, 0}; // 2秒超时ret = io_getevents(ctx, 1, 1, events, &timeout);if (ret > 0) {printf(" 操作已完成,尝试取消...\n");ret = io_cancel(ctx, &iocb, &result);if (ret == -EAGAIN) {printf(" ✓ 取消失败:操作已完成 (EAGAIN)\n");} else if (ret == 0) {printf(" ✓ 操作取消成功\n");} else {printf(" 取消操作返回: %s\n", strerror(-ret));}}// 场景2: 使用无效的iocb尝试取消printf("\n 场景2: 使用无效的iocb尝试取消\n");struct iocb invalid_iocb;memset(&invalid_iocb, 0, sizeof(invalid_iocb));invalid_iocb.aio_data = 9999;ret = io_cancel(ctx, &invalid_iocb, &result);if (ret == -EAGAIN) {printf(" ✓ 取消失败:无效操作 (EAGAIN)\n");} else {printf(" 取消操作返回: %s\n", strerror(-ret));}// 场景3: 使用无效的上下文IDprintf("\n 场景3: 使用无效的上下文ID\n");struct iocb test_iocb;memset(&test_iocb, 0, sizeof(test_iocb));aio_context_t invalid_ctx = (aio_context_t)-1;ret = io_cancel(invalid_ctx, &test_iocb, &result);if (ret == -EINVAL) {printf(" ✓ 操作失败:无效上下文ID (EINVAL)\n");} else {printf(" 取消操作返回: %s\n", strerror(-ret));}// 场景4: 重复取消同一个操作printf("\n 场景4: 重复取消操作\n");ret = io_cancel(ctx, &iocb, &result);if (ret == -EAGAIN) {printf(" ✓ 第二次取消失败:操作已处理 (EAGAIN)\n");} else if (ret == 0) {printf(" ✓ 第二次取消成功\n");ret = io_cancel(ctx, &iocb, &result);if (ret == -EAGAIN) {printf(" ✓ 第三次取消失败:操作已处理 (EAGAIN)\n");}} else {printf(" 第二次取消操作返回: %s\n", strerror(-ret));}// 演示资源清理错误处理printf("\n6. 演示资源清理错误处理:\n");// 正常关闭文件printf(" 正常关闭文件:\n");if (close(fd) == 0) {printf(" ✓ 文件关闭成功\n");} else {record_error(&error_ctx, -errno, "close", "关闭文件失败");printf(" 尝试强制清理...\n");}// 删除测试文件printf(" 删除测试文件:\n");if (unlink(filename) == 0) {printf(" ✓ 测试文件删除成功\n");} else {record_error(&error_ctx, -errno, "unlink", "删除测试文件失败");printf(" 注意:可能需要手动清理测试文件\n");}// 销毁AIO上下文printf(" 销毁AIO上下文:\n");ret = io_destroy(ctx);if (ret == 0) {printf(" ✓ AIO上下文销毁成功\n");} else {record_error(&error_ctx, ret, "io_destroy", "销毁AIO上下文失败");printf(" 销毁操作返回: %s\n", strerror(-ret));}// 显示错误处理总结printf("\n=== 错误处理总结 ===\n");printf("1. 常见错误类型:\n");printf(" ✓ EAGAIN: 操作无法取消(已完成或不存在)\n");printf(" ✓ EINVAL: 参数无效\n");printf(" ✓ EBADF: 文件描述符无效\n");printf(" ✓ ECANCELED: 操作已被取消\n");printf("\n2. 错误处理策略:\n");printf(" ✓ 记录错误信息\n");printf(" ✓ 根据错误类型采取不同措施\n");printf(" ✓ 必要时重试操作\n");printf(" ✓ 优雅降级处理\n");printf("\n3. 恢复策略:\n");printf(" ✓ 重试机制\n");printf(" ✓ 备用方案\n");printf(" ✓ 资源清理\n");printf(" ✓ 状态恢复\n");printf("\n4. 最佳实践:\n");printf(" ✓ 完善的错误记录\n");printf(" ✓ 适当的重试策略\n");printf(" ✓ 资源泄漏预防\n");printf(" ✓ 用户友好提示\n");return 0;
}int main() {return demo_error_handling_recovery();
}
io_cancel 使用注意事项
系统要求:
1. 内核版本: 需要支持AIO的Linux内核
2. 权限要求: 通常不需要特殊权限
3. 架构支持: 支持所有主流架构
取消限制:
1. 操作状态: 只能取消排队或正在执行的操作
2. 时间窗口: 已完成的操作无法取消
3. 资源释放: 取消后需要清理相关资源
错误处理:
1. EAGAIN: 操作无法取消(已完成或不存在)
2. EINVAL: 参数无效
3. EBADF: 文件描述符无效
4. ECANCELED: 操作已被取消
性能考虑:
1. 取消开销: 取消操作本身也有性能开销
2. 批量处理: 批量取消可能更高效
3. 超时设置: 合理设置超时时间
4. 状态检查: 取消前检查操作状态
安全考虑:
1. 参数验证: 验证所有输入参数
2. 内存管理: 确保缓冲区内存有效
3. 资源清理: 及时清理已取消操作的资源
4. 状态同步: 确保多线程环境下的状态一致性
最佳实践:
1. 及时取消: 不需要的操作及时取消
2. 状态跟踪: 跟踪每个操作的状态
3. 错误处理: 妥善处理取消失败的情况
4. 资源管理: 确保资源得到正确释放
5. 超时机制: 实现合理的超时取消机制
io_cancel vs 相似函数对比
io_cancel vs io_destroy:
// io_cancel: 取消单个操作
io_cancel(ctx, &iocb, &result);// io_destroy: 销毁整个AIO上下文(取消所有操作)
io_destroy(ctx);
io_cancel vs io_getevents:
// io_cancel: 主动取消操作
io_cancel(ctx, &iocb, &result);// io_getevents: 被动等待操作完成
io_getevents(ctx, 1, 1, events, &timeout);
常见使用场景
1. 超时处理:
// 异步操作超时后取消
if (operation_timeout) {io_cancel(ctx, &iocb, &result);
}
2. 用户中断:
// 用户取消操作时取消所有相关异步操作
for (int i = 0; i < operation_count; i++) {io_cancel(ctx, &iocbs[i], &results[i]);
}
3. 资源清理:
// 应用程序退出前取消未完成的操作
io_cancel(ctx, &iocb, &result);
总结
io_cancel
是Linux AIO框架中重要的操作取消函数,提供了:
1. 操作控制: 精确控制异步操作的生命周期
2. 资源管理: 及时释放不需要的资源
3. 错误处理: 完善的错误处理机制
4. 灵活性: 支持多种取消策略
通过合理使用 io_cancel
,可以构建更加健壮和灵活的异步I/O应用。在实际应用中,需要注意错误处理、资源管理和性能优化等关键问题。