如何在不使用忙等的情况下检测AIO操作何时完成?
C语言AIO:替代忙等的两种方案(回调/主动等待)
Linux的POSIX AIO确实支持主动阻塞等待和异步回调通知两种方式,完全不用通过忙等浪费CPU资源。
一、主动阻塞等待(类似pthread_join)
可以使用aio_suspend()函数,它会阻塞当前线程,直到指定的一个或多个AIO操作完成,或者超时触发。用法示例:
#include <aio.h> #include <stdio.h> #include <errno.h> #include <string.h> int main() { struct aiocb cb; int fd = open("test_file.txt", O_RDONLY); if (fd == -1) { perror("open failed"); return 1; } // 初始化AIO控制块 char read_buf[1024]; cb.aio_fildes = fd; cb.aio_buf = read_buf; cb.aio_nbytes = sizeof(read_buf); cb.aio_offset = 0; cb.aio_reqprio = 0; cb.aio_sigevent.sigev_notify = SIGEV_NONE; // 不使用信号通知 // 启动异步读操作 if (aio_read(&cb) == -1) { perror("aio_read failed"); close(fd); return 1; } // 准备等待的AIO控制块列表(以NULL结尾) const struct aiocb* cblist[] = {&cb, NULL}; // 阻塞等待操作完成,第三个参数为NULL表示无限等待 if (aio_suspend(cblist, 1, NULL) == -1) { perror("aio_suspend failed"); close(fd); return 1; } // 检查操作结果 int error = aio_error(&cb); if (error != 0) { fprintf(stderr, "AIO operation failed: %s\n", strerror(error)); close(fd); return 1; } ssize_t bytes_read = aio_return(&cb); printf("Successfully read %zd bytes\n", bytes_read); close(fd); return 0; }
aio_suspend()的逻辑和pthread_join完全一致:挂起当前线程,直到目标AIO操作完成,期间不会占用CPU做无用循环。
二、异步回调通知
POSIX AIO本身没有直接提供回调函数接口,但可以结合信号机制实现类似效果:在aiocb结构体中指定一个信号,当AIO操作完成时,系统会向进程发送该信号,你可以在信号处理函数中执行后续逻辑。
示例代码:
#include <aio.h> #include <stdio.h> #include <signal.h> #include <errno.h> #include <unistd.h> #include <string.h> // 信号处理函数,接收AIO完成通知 void aio_completion_handler(int signum, siginfo_t* info, void* context) { if (info->si_code == SI_ASYNCIO) { struct aiocb* cb = info->si_value.sival_ptr; // 检查操作结果 int error = aio_error(cb); if (error != 0) { fprintf(stderr, "AIO failed: %s\n", strerror(error)); return; } ssize_t bytes = aio_return(cb); printf("Callback triggered: Read %zd bytes\n", bytes); // 这里可以执行后续业务逻辑 } } int main() { struct aiocb cb; int fd = open("test_file.txt", O_RDONLY); if (fd == -1) { perror("open failed"); return 1; } // 初始化AIO控制块 char read_buf[1024]; cb.aio_fildes = fd; cb.aio_buf = read_buf; cb.aio_nbytes = sizeof(read_buf); cb.aio_offset = 0; cb.aio_reqprio = 0; // 设置信号通知:使用SIGUSR1,传递cb指针作为附加信息 cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; cb.aio_sigevent.sigev_signo = SIGUSR1; cb.aio_sigevent.sigev_value.sival_ptr = &cb; // 注册带参数的信号处理函数 struct sigaction sa; sa.sa_sigaction = aio_completion_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; if (sigaction(SIGUSR1, &sa, NULL) == -1) { perror("sigaction failed"); close(fd); return 1; } // 启动异步读操作 if (aio_read(&cb) == -1) { perror("aio_read failed"); close(fd); return 1; } // 主进程可以继续执行其他任务,无需忙等 printf("Doing other work while AIO runs...\n"); sleep(3); // 模拟耗时业务操作 close(fd); return 0; }
注意:信号处理函数中只能调用异步安全函数(比如write、_exit等),如果需要复杂逻辑,建议在信号处理函数中设置一个全局标志,然后在主循环中检查标志并处理。
另外,FreeBSD等系统的AIO支持SIGEV_THREAD模式,可以直接指定一个线程函数作为回调,系统会在操作完成时自动创建线程执行该函数,用法更简洁:
cb.aio_sigevent.sigev_notify = SIGEV_THREAD; cb.aio_sigevent.sigev_notify_function = my_aio_callback; cb.aio_sigevent.sigev_notify_attributes = NULL; // 使用默认线程属性
但目前Linux的POSIX AIO暂不支持该模式,只能通过信号模拟回调。
总结
- 主动等待AIO完成:使用
aio_suspend(),完全替代忙等的阻塞等待逻辑。 - 异步回调通知:通过
SIGEV_SIGNAL结合信号处理函数实现,Linux下需自行处理信号,BSD系统支持直接线程回调。
内容的提问来源于stack exchange,提问作者user22200698
相关产品推荐
相关产品推荐

