异步I/O之用户空间
Linux2.6异步I/O AIO的基本思想: 允许进程发起很多I/O操作,而不用阻塞或等待任何操作完成,稍后或在 接收到I/O操作完成通知时,进程可以检索I/O操作结果 在异步非阻塞I/O中,我们可以同时发起多个传输操作,这需要每个传输操作都有唯一的上下文 ,这样我们才能在他们完成时区分到底是哪个传输操作完成了,这个 工作可以通过aiocb结构体进行
Linux2.6异步I/O
AIO的基本思想:
允许进程发起很多I/O操作,而不用阻塞或等待任何操作完成,稍后或在
接收到I/O操作完成通知时,进程可以检索I/O操作结果
在异步非阻塞I/O中,我们可以同时发起多个传输操作,这需要每个传输操作都有唯一的上下文
,这样我们才能在他们完成时区分到底是哪个传输操作完成了,这个
工作可以通过aiocb结构体进行区分
其中,struct aiocb主要包含以下字段:
int aio_fildes; /* 要被读写的fd */
void * aio_buf; /* 读写操作对应的内存buffer */
__off64_t aio_offset; /* 读写操作对应的文件偏移 */
size_t aio_nbytes; /* 需要读写的字节长度 */
int aio_reqprio; /* 请求的优先级 */
struct sigevent aio_sigevent; /* 异步事件,定义异步操作完成时的通知信号或回调函数 */
异步阻塞I/O的思想:
一个典型的例子就是select调用,具体请看下图
异步非阻塞I/O
在一个进程中为了执行多个I/O请求而对计算操作和I/O处理进行重叠处理的能力,利用了处理速度和I/O速度之间的差异。
当一个或多个I/O请求挂起时,CPU可以执行其他任务
具体见图
AIO相关操作
用户空间:
1、aio_read
请求对一个有效的文件描述符进行异步读操作,这个文件描述符可以表示一个文件、套接字甚至管道
int aio_read(struct aiocb *aiocbp);
aio_read函数在请求进行排队之后会立即返回,执行成功返回0,出错返回-1,并设置errno的值
2、aio_write
aio_write函数用来请求一个异步的写操作
int aio_write(struct aiocb *aiocbp);
aio_write函数会立即返回,具体与aio_read相同
3、aio_error
被用来确定请求状态
int aio_error(struct aiocb * aiocbp);
这个函数可以返回以下内容
EINPROGRESS:说明请求尚未完成
ECANCELLED:说明请求被应用程序取消
4、aio_return
异步I/O和标准I/O方式之间的另外一个区别是不能立即访问这个函数的返回状态,因为
异步I/O并没有阻塞在read()调用上,在标准的read调用中返回状态时在该函数返回时提供的
。但是在异步I/O中,我们要使用aio_return函数
ssize_t aio_return(struct aiocb *aiocbp);
此函数只有在aio_error调用确定请求已经完成之后才会调用这个函数
/*用户空间异步读例程*/
#include <aio.h>
...
int fd,ret;
struct aiocb my_aiocb;
fd = open("file.txt",O_RDONLY);
if(fd<0)
perror("open");
/*清零aiocb结构体*/
bzero((char *)&my_aiocb,sizeof(struct aiocb));
/*为aiocb请求分配数据缓冲区*/
my_aiocb.aio_buf = malloc(BUFSIZE+1);
if(!my_aiocb.aio_buf)
perror("malloc");
/*初始化aiocb的成员*/
my_Aiocb.aio_filds = fd;
my_aiocb.aio_nbytes = BUFSIZE;
my_aiocb.aio_offset = 0;
ret = aio_read(&my_aiocb);
if(ret < 0)
perror("aio_read");
while(aio_error(&my_aiocb) == EINPROCESS)
continue;
if((ret = aio_return(&my_iocb))>0){
/*获得异步读的返回值*/
}else{
/*读失败,分析errorno*/
}
5.aio_suspend
用户可以使用aio_suspend()函数来挂起或阻塞掉用进程,直到异步请求完成为止,此时产生一个信号
,或者在发生其他超市操作时会导致aio_suspend()返回
int aio_suspend(const struct aiocb *const cblist[],
int n,const struct timespec *timeout);
此函数在用户空间的使用例程:
struct aioct *cblist[MAX_LIST]
/*清空aioct结构体链表*/
bzero((char *)cblist,sizeof(cblist));
/*将一个或更多的aiocb放入aioct结构体链表*/
cblist[0] = &my_aiocb;
ret = aio_read(&my_aiocb);
ret = aio_suspend(cblist,MAX_LIST,NULL);
6.aio_cancel
允许用户取消对某个文件描述符执行的一个或所有的I.O请求
int aio_cancel(int fd,struct aiocb *aiocbp);
//用户需要提供一个aiocb指针,如果这个请求被成功取消,那么这个函数就会返回
AIO_CANCELED,如果请求完成,此函数就会返回AIO_TCANCELED
aiocbp参数设置为NULL,如果所有的请求都被取消了,这个函数将返回
AIO_CANCELED,如果至少有一个请求被取消,那么这个函数就会返回AIO_NOT_CANCELED
如果一个也没被取消就会返回AIO_ALLODNOE
7.lio_listio
listio函数可以用于同时发起多个请求(很重要),它使得用户可以在
一个系统调用中启动大量的I/O操作。
int lio_listio(int mode,struct aiocb *list[],int nent,struct sigevent *sig);
参数介绍:
mode参数可以是LIO_WAIT或LIO_NOWAIT。
LIO_WAIT会阻塞这个调用,直到所有的I/O都完成为止
LIO_NOWAIT立即返回
list是aiocb的引用列表
nent最大元素个数
此函数的使用里程:
struct aiocb aiocb1,aiocb2;
struct aiocb *list[MAX_LIST];
...
/*准备第一个aiocb*/
aiocb1.aio_fields = fd;
aiocb1.aio_buf = malloc(BUFSIZE+1);
aiocb1.aio_nbytes = BUFSIZE;
aiocb1.aio_offset = next_offset;
aiocb1.aio_lio_opcode = LIO_READ;/*异步读操作,写操作为LIO_WRITE\
空操作为LIO_NOP*/
.../*贮备多个aiocb*/
bzero((char *)'list,sizeof(list));
/*将aiocb填入链表*/
list[0] = &aiocb1;
list[1] = &aiocb2;
...
ret = lio_listio(LIO_WAIT,list,MAX_LIST,NULL);
下面接收两种作为AIO通知的方法
一.使用信号作为AIO的通知
此时使用AIO的应用程序同样需要定义信号处理函数,在指定的信号被产生时
会触发这个处理程序,而aiocb被提供给信号处理函数用来区分AIO请求
/*例程*/
void setup_io(...)
{
int fd;
struct sigaction sig_act;
struct aiocb my_aiocb;
...
/*设置信号处理函数*/
sigemptyset(&sig_act.sa_mask);
sig_act.sa_flags = AS_SIGINFO;
sig_act.sa_sigaction = aio_completion_handler;
/*设置AIO请求*/
bzero((char*)&my_aiocb,sizeof(struct aiocb));
my_aiocb.aio_files = fd;
my_aiocb.aio_buf = malloc(BUF_SIZE + 1);
my_aiocb.aio_nbytes = BUF_SIZE;
my_aiocb.aio_offset = next_offset;
/*连接AIO请求和信号处理函数(通过信号SIGIO)*/
my_aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
my_aiocb.aio_sigevent.sigev_signo = SIGIO;
my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;
/*将信号与信号处理函数绑定*/
ret = sigaction(SIGIO,&sig_act,NULL);
...
ret = aio_read(&my_aiocb);/*发出异步读请求*/
}
/*信号处理函数*/
void aio_completion_handler(int signo,siginfo_t *info,void *context)
{
struct aiocb *req;
/*确定是我们需要的信号*/
if(info->si_signo == SIGIO)
{
req = (struct aiocb*)info->si_value.sival_ptr;/*获得aiocb*/
/*请求的操作完成了吗?*/
if(aio_error(req)==0){
/*请求的操作完成,获取返回值*/
ret = aio_return(req);
}
}
}
二.使用回调函数作为AIO的通知
/*设置异步I/O请求*/
void setup_io(...)
{
int fd;
struct aiocb my_aiocb;
...
/*设置AIO请求*/
bzero((char*) &my_aiocb,sizeof(struct aiocb));
my_aiocb.aio_fildes = fd;
my_aiocb.aio_buf = malloc(BUF_SIZE + 1);
my_aiocb.aio_nbytes = BUF_SIZE;
my_aiocb.aio_offset = next_offset;
/*连接AIO请求和线程回调函数*/
my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
my_aiocb.aio_sigevent.notify_function = aio_completion_handler;
/*设置回调函数*/
my_aiocb.aio_sigevent.notify_attributes = NULL;
my_aiocb.aio_sigevent.sigev_value.sigval_ptr = &my_aiocb;
...
ret = aio_read(&my_aiocb);/*发起AIO请求*/
}
/*异步I/O完成回调函数*/
void aio_completion_handler(sigval_t sigval)
{
struct aiocb*req;
req = (struct aiocb*)sigval_ptr;
/*AIO请求完成*/
if(aio_error(req) == 0)
{
/*请求完成,获得返回值*/
ret = aio_return(req);
}
}
//历程解析:上述程序在创建aiocb、请求之后,使用SIGEV_THREAD请求一个线程回调函数
来作为通知方法,在回调函数中,通过(struct aiocb*)sigval.sival_ptr可以获得对应的aiocb指针,使用
AIO函数验证请求是否完成
/**************************************************最后附上我所做的实验代码*******************************************************/
/************************使用回调函数的异步I/O**************************/
#include <aio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
void async_read(sigval_t val)
{
struct aiocb *ptr = (struct aiocb *)val.sival_ptr;
printf("read=%s", (char *)ptr->aio_buf);
}
int main(void)
{
int fd;
struct aiocb cb;
char sbuf[100];
int ret;
fd = open("hello",O_RDWR,S_IRUSR|S_IWUSR);
bzero(&cb, sizeof(cb));
cb.aio_fildes = fd;
cb.aio_buf = sbuf;
cb.aio_nbytes = 100;
cb.aio_offset = 0;
cb.aio_sigevent.sigev_value.sival_ptr = &cb;
/*设置回调函数*/
cb.aio_sigevent.sigev_notify = SIGEV_THREAD;
cb.aio_sigevent.sigev_notify_function = async_read;
cb.aio_sigevent.sigev_notify_attributes = NULL;
//发送异步读请求
ret = aio_read(&cb);
if (ret == -1) {
perror("aio_read");
exit(1);
}
int i = 0;
while (1) {
printf("%d\n",i++);
sleep(1);
}
return 0;
}
执行结果如下图所示
/*****************************使用信号通知的异步IO**********************************/
/*使用信号作为AIO的通知*/
#include <aio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
//信号处理函数
void async_read(int s, siginfo_t * info, void * context)
{
struct aiocb *ptr = (struct aiocb *)info->si_value.sival_ptr;
printf("read=%s", (char *)ptr->aio_buf);
}
int main(void)
{
struct aiocb cb;
int fd;
char sbuf[100];
int ret;
struct sigaction act;
fd = open("hello",O_RDWR,S_IRUSR|S_IWUSR);
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART | SA_SIGINFO;
act.sa_sigaction = async_read;
sigaction(SIGIO, &act, NULL);
bzero(&cb, sizeof(cb));
cb.aio_fildes = fd;
cb.aio_buf = sbuf;
cb.aio_nbytes = 100;
cb.aio_offset = 0;
cb.aio_sigevent.sigev_value.sival_ptr = &cb;
cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
cb.aio_sigevent.sigev_signo = SIGIO;
ret = aio_read(&cb);
if (ret == -1) {
perror("aio_read");
exit(1);
}
int i = 0;
while (1) {
printf("%d\n",i++);
sleep(3);
}
return 0;
}
执行结果如下:
最后需要注意的是再编译aio.c的时候
要加相应的库,-lrt
gcc -o test aio_signal.c -lrt
更多推荐
所有评论(0)