社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
aiocb
(AIO I/O Control Block)结构。这个结构包含了有关传输的所有信息,包括为数据准备的用户缓冲区。在产生 I/O (称为完成)通知时,aiocb
结构就被用来惟一标识所完成的 I/O 操作。
API 函数 |
说明 |
|
请求异步读操作 |
|
检查异步请求的状态 |
|
获得完成的异步请求的返回状态 |
|
请求异步写操作 |
|
挂起调用进程,直到一个或多个异步请求已经完成(或失败) |
|
取消异步 I/O 请求 |
|
发起一系列 I/O 操作 |
manual手册中的截图
struct aiocb {
int aio_fildes; //文件描述符,可以使socket
int aio_offset; //文件内偏移量
volatile void *aio_buf; //缓冲区的地址(指针)
size_t aio_nbytes; //传输长度
int aio_reqprio; //响应优先级
struct sigevent aio_sigevent; //通知方式
int aio_lio_opcode;
...
};
#include <aio.h>
int aio_read(struct aiocb *aiocbp);
Link with -lrt
本函数从aiocbp.aio_fildes指向的文件中读取aiocbp.aio_nbytes字节数的数据至aiocbp.aio_buf指向的缓冲区。
读取的起始地址由aiocbp.aio_offset指定。
成功返回0,失败返回-1,表示请求失败,并设置errno值。
如果是之后出现的错误,会有aio_return()和aio_error()得到相应返回值和errno 。
#include <aio.h>
int aio_write(struct aiocb *aiocbp);
Link with -lrt
本函数从aiocbp.aio_buf指向的缓冲区中读取aiocbp.aio_nbytes字节数的数据至aiocbp.aio_fildes指向的文件。
写入的起始地址由aiocbp.aio_offset指定。
成功返回0,失败返回-1,表示请求失败,并设置errno值。
如果是之后出现的错误,会有aio_return()和aio_error()得到相应返回值和errno 。
ssize_t aio_return(struct aiocb *aiocbp);
Link with -lrt.
该函数通过参数返回异步IO请求的控制块的最后返回状态。
成功,如果异步IO完成,返回传输的字节数;如果未完成,返回0 。
失败返回-1,设置errno值。
错误情况
1.aiocbp指向非struct aiocb
2.aio_return未实施
int aio_error(const struct aiocb *aiocbp);
Link with -lrt
接收错误的异步IO请求的控制块的状态
返回值
该函数返回下列值:
* EINPROGRESS, 请求还未完成.
* ECANCELED, 请求被取消了.
* 0, 请求成功完成了.
* 一个正的错误值, 如果异步I/O操作失败. 这个值同样会存入errno中
#include <func.h>
#define BUFFER_SIZE 1024
int MAX_LIST = 2;
int main(int argc,char **argv)
{
struct aiocb rd;
int fd,ret,couter;
fd = open("aio_write.i",O_RDONLY);
if(-1 == fd)
{
perror("open");
}
//将rd结构体清空
bzero(&rd,sizeof(rd));
//为rd.aio_buf分配空间
rd.aio_buf = malloc(BUFFER_SIZE + 1);
bzero((void*)rd.aio_buf, BUFFER_SIZE+1);
//填充rd结构体
rd.aio_fildes = fd;
rd.aio_nbytes = BUFFER_SIZE;
rd.aio_offset = 0;
//进行异步读操作
ret = aio_read(&rd);
if(-1 == ret)
{
perror("aio_read");
exit(1);
}
couter = 0;
// 循环等待异步读操作结束
while(aio_error(&rd) == EINPROGRESS)
{
ret = aio_return(&rd);//这个不会阻塞,直接返回0
printf("nn返回值为:%dn",ret);
printf("第%d次n",++couter);
}
//获取异步读返回值
ret = aio_return(&rd);//这个不会阻塞,直接返回0
printf("nn返回值为:%dn",ret);
//printf("%sn", (char*)rd.aio_buf);//不等待异步返回,就会导致直接输出 空字符串
free((void*)rd.aio_buf);
return 0;
}
#include <func.h>
#define BUFFER_SIZE 1024
int main(int argc,char **argv)
{
struct aiocb wr;
int fd,ret,couter;
fd = open("file2",O_RDWR| O_CREAT, 0666);
if(-1 == fd)
{
perror("open");
exit(-1);
}
//将wr结构体清空
bzero(&wr,sizeof(wr));
char buf[20] = {"helloMonkey!"};
//为wr.aio_buf分配空间
wr.aio_buf = buf;//让结构体缓冲区指针指向需要传送的数据
//填充wr结构体
wr.aio_fildes = fd;
wr.aio_nbytes = strlen(buf);//传输的字节数
wr.aio_offset = 0;
//进行异步读操作
ret = aio_write(&wr);
if(-1 == ret)
{
perror("aio_write");
exit(-1);
}
couter = 0;
// 循环等待异步读操作结束
while(aio_error(&wr) == EINPROGRESS)
{
printf("第%d次n",++couter);
}
//获取异步读返回值
ret = aio_return(&wr);
printf("nn返回值为:%dn",ret);
return 0;
}
int aio_suspend(const struct aiocb * const aiocb_list[],
int nitems, const struct timespec *timeout);
Link with -lrt.
阻塞当前进程,直到一个异步请求完成或者设定时间结束或者一个信号到达。
参数aiocb_list[]是struct aiocb的链表,其中任意一个完成都会唤醒进程。
参数nitems直到参数aiocb_list中元素个数。
参数timeout为NULL,表示阻塞等待时间为无限,直到有异步请求完成或者信号到达才继续执行。
不为空,函数会阻塞进程设定好的时间。
上面一个参数表示秒,下面参数表示纳秒。
int aio_cancel(int fd, struct aiocb *aiocbp);
Link with -lrt.
该函数尝试取消对文件描述符fd发起的异步IO申请。
成功返回以下三个值,失败返回-1.
AIO_CANCELED 所有申请被成功取消
AIO_NOTCANCELED 至少有一个没被取消,因为它在进程中。
AIO_ALLDONE 所有申请都已经被完成了
如果参数aiocbp为NULL,所有关于这个文件描述符的申请都会取消。否则,只有aiocbp指定的异步IO申请会取消。
int lio_listio(int mode, struct aiocb *const aiocb_list[],
int nitems, struct sigevent *sevp);
Link with -lrt.
将aiocb_list数组中的每一个异步IO申请初始化。
参数mode为
LIO_WAIT 进程会被阻塞,直到所有异步IO操作(不是申请)完成,这时参数sevp会被忽视
LIO_NOWAIT 所有异步IO申请进入队列,函数立即返回。当所有异步IO操作完成,由参数sevp指定的异步通知发生
参数aiocb_list为一个数组,存放每一个异步IO申请的结构体。
参数nitems指定参数aiocb_list的长度。
参数sevp指定的异步操作完成后的通知方式。
union sigval { /*随通知传送的数据 */
int sival_int; /* 整形 */
void *sival_ptr; /* 指针 */
};
struct sigevent {
int sigev_notify; /* 通知方式 */
int sigev_signo; /* 通知信号 */
union sigval sigev_value; /* 随通知传送的数据 */
void (*sigev_notify_function) (union sigval);
/* 用于线程通知的函数 (SIGEV_THREAD) */
void *sigev_notify_attributes;
/* 线程通知的属性(SIGEV_THREAD) */
pid_t sigev_notify_thread_id;
/* 线程的id (SIGEV_THREAD_ID) */
};
测试例子1:(通过线程回调通知)
#include <func.h>
#define BUFFER_SIZE 1024
void aioHandler(sigval_t sigval){
//这时异步操作结束,释放资源,回收文件描述符
struct aiocb *req;
int ret;
req = (struct aiocb*)sigval.sival_ptr;
if(aio_error(req) == 0){
ret = aio_return(req);
printf("异步IO成功n");
//将数据由缓冲区写入文件,可以由一个自己设的结构体带入想写入文件的fd
write(STDOUT_FILENO, (void*)req->aio_buf, req->aio_nbytes);
}
close(req->aio_fildes);
free((void*)req->aio_buf);
}
int main(int argc,char **argv)
{
struct aiocb rd;
int fd,ret;
fd = open("aio_write.i",O_RDONLY);
if(fd < 0)
{
perror("test.txt");
}
//将rd结构体清空
bzero(&rd,sizeof(rd));
//为rd.aio_buf分配空间
rd.aio_buf = malloc(BUFFER_SIZE + 1);
bzero((void*)rd.aio_buf, BUFFER_SIZE+1);
//填充rd结构体
rd.aio_fildes = fd;
rd.aio_nbytes = BUFFER_SIZE;
rd.aio_offset = 0;
//通知方式设定,函数结束栈空间会释放,堆空间不会
rd.aio_sigevent.sigev_notify = SIGEV_THREAD;
//将rd的地址传进去,也可以传入你想要的结构体指针(堆)
rd.aio_sigevent.sigev_value.sival_ptr = &rd;
rd.aio_sigevent.sigev_notify_function = aioHandler;
rd.aio_sigevent.sigev_notify_attributes = NULL;
//进行异步读操作
ret = aio_read(&rd);
if(ret < 0)
{
perror("aio_read");
exit(1);
}
sleep(2);//防止进程终止,等一下
//获取异步读返回值
ret = aio_return(&rd);//这个不会阻塞,直接返回0
printf("nn返回值为:%dn",ret);
//printf("%sn", (char*)rd.aio_buf);//不等待异步返回,就会导致直接输出 空字符串
return 0;
}
测试例子2(通过信号通知):
#include <func.h>
#define BUFFER_SIZE 1024
void aioHandler(int signo, siginfo_t* info, void* content){
//这时异步操作结束,释放资源,回收文件描述符
struct aiocb *req;
int ret;
if(info->si_signo == SIGIO){
req = (struct aiocb *)info->si_value.sival_ptr;
if(aio_error(req) == 0){
printf("传输完成n");
ret = aio_return(req);
write(STDOUT_FILENO, (void*)req->aio_buf, req->aio_nbytes);
}
close(req->aio_fildes);
free((void*)req->aio_buf);
}
}
int main(int argc,char **argv)
{
struct aiocb rd;
int fd,ret;
fd = open("aio_write.i",O_RDONLY);
if(fd < 0)
{
perror("test.txt");
}
//将rd结构体清空
bzero(&rd,sizeof(rd));
//为rd.aio_buf分配空间
rd.aio_buf = malloc(BUFFER_SIZE + 1);
bzero((void*)rd.aio_buf, BUFFER_SIZE+1);
//填充rd结构体
rd.aio_fildes = fd;
rd.aio_nbytes = BUFFER_SIZE;
rd.aio_offset = 0;
//通知方式设定,函数结束栈空间会释放,堆空间不会
rd.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
//将rd的地址传进去,也可以传入你想要的结构体指针(堆)
rd.aio_sigevent.sigev_value.sival_ptr = &rd;
rd.aio_sigevent.sigev_signo = SIGIO;
//注册信号
struct sigaction sig_act;
sigemptyset(&sig_act.sa_mask);
sig_act.sa_flags = SA_SIGINFO;
sig_act.sa_sigaction = aioHandler;
ret = sigaction(SIGIO, &sig_act, NULL);
//进行异步读操作
ret = aio_read(&rd);
//rd.aio_buf = NULL;//加上这个后,输出失败。
//rd.aio_nbytes = 1;//加上这个后,只输出一个字符
/*
综合上面的结果,内核所拿到的是rd的地址,如果rd在函数空间,然后函数结束
如果有其它数据覆盖了函数栈空间,那么就会出现脏数据和越界访问。
可以通过申请堆空间来避免这一情况,或者函数运行时间足够,或者由上一层来分配rd空间
再通过指针将rd传入。
此外,异步IO过程中,不应该修改rd的值,不然会导致io失败。
*/
if(ret < 0)
{
perror("aio_read");
exit(1);
}
sleep(2);//防止进程终止,等一下
//获取异步读返回值
ret = aio_return(&rd);//这个不会阻塞,直接返回0
printf("nn返回值为:%dn",ret);
//printf("%sn", (char*)rd.aio_buf);//不等待异步返回,就会导致直接输出 空字符串
return 0;
}
问题:调用aio_read时,如果rd是在某个函数空间,那么函数结束后,空间释放了,是否异步IO就失败了?
综合上面的结果,内核所拿到的是rd的地址,如果rd在函数空间,然后函数结束。如果有其它数据覆盖了函数栈空间,那么就会出现脏数据和越界访问。可以通过申请堆空间来避免这一情况,或者函数运行时间足够,或者由上一层来分配rd空间再通过指针将rd传入。此外,异步IO过程中,不应该修改rd的值,不然会导致io失败。
也可以在函数的最后加上检测,保证io执行完毕。
// 循环等待异步读操作结束
while(aio_error(&wr) == EINPROGRESS)
{
printf("第%d次n",++couter);
}
参考资料:
https://www.ibm.com/developerworks/cn/linux/l-async/
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!