Linux AIO
Linux aio是Linux下的異步讀寫模型。Linux 異步 I/O 是 Linux 內核中提供的一個相當新的增強。它是 2.6 版本內核的一個標準特性。對于文件的讀寫,即使以O_NONBLOCK方式來打開一個文件,也會處于"阻塞"狀態。因為文件時時刻刻處于可讀狀態。而從磁盤到內存所等待的時間是驚人的。為了充份發揮把數據從磁盤復制到內存的時間,引入了aio模型。AIO 背后的基本思想是允許進程發起很多 I/O 操作,而不用阻塞或等待任何操作完成。稍后或在接收到 I/O 操作完成的通知時,進程就可以檢索 I/O 操作的結果。
圖 1. 基本 Linux I/O 模型的簡單矩陣
?
每個 I/O 模型都有自己的使用模式,它們對于特定的應用程序都有自己的優點。本節將簡要對其一一進行介紹。
圖 2. 同步阻塞 I/O 模型的典型流程
?
從應用程序的角度來說,read 調用會延續很長時間。實際上,在內核執行讀操作和其他工作時,應用程序的確會被阻塞。
圖 3. 同步非阻塞 I/O 模型的典型流程
?
非阻塞的實現是 I/O 命令可能并不會立即滿足,需要應用程序調用許多次來等待操作完成。這可能效率不高,因為在很多情況下,當內核執行這個命令時,應用程序必須要進行忙碌等待,直到數據可用為止,或者試圖執行其他工作。正如圖 3 所示的一樣,這個方法可以引入 I/O 操作的延時,因為數據在內核中變為可用到用戶調用 read 返回數據之間存在一定的間隔,這會導致整體數據吞吐量的降低。
圖 4. 異步阻塞 I/O 模型的典型流程 (select)
?
select 調用的主要問題是它的效率不是非常高。盡管這是異步通知使用的一種方便模型,但是對于高性能的 I/O 操作來說不建議使用。
圖 5. 異步非阻塞 I/O 模型的典型流程
?
Linux 上的 AIO 簡介
linux下有aio封裝,aio_*系列的調用是glibc提供的,是glibc用線程+阻塞調用來模擬的,性能很差,為了能更多的控制io行為,可以使用更為低級libaio。
libaio項目: http://oss.oracle.com/projects/libaio-oracle/
一、libaio接口
libaio提供下面五個主要API函數:
int io_setup(int maxevents, io_context_t *ctxp);
int io_destroy(io_context_t ctx);
int io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
五個宏定義:
void io_set_callback(struct iocb *iocb, io_callback_t cb);
void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset);
void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset);
void io_prep_pwritev(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset);
void io_prep_preadv(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset);
這五個宏定義都是操作struct iocb的結構體。struct iocb是libaio中很重要的一個結構體,用于表示IO,但是其結構略顯復雜,為了保持封裝性不建議直接操作其元素而用上面五個宏定義操作。
二、libaio的初始化和銷毀
觀察libaio五個主要API,都用到類型為io_context的變量,這個變量為libaio的工作空間。不用具體去了解這個變量的結構,只需要了解其相關操作。創建和銷毀libaio分別用到io_setup(也可以用io_queue_init,區別只是名字不一樣而已)和io_destroy。
int io_setup(int maxevents, io_context_t *ctxp);
int io_destroy(io_context_t ctx);
三、libaio讀寫請求的下發和回收
1. 請求下發
libaio的讀寫請求都用io_submit下發。下發前通過io_prep_pwrite和io_prep_pread生成iocb的結構體,做為io_submit的參數。這個結構體中指定了讀寫類型、起始扇區、長度和設備標志符。
libaio的初始化不是針對一個具體設備進行初始,而是創建一個libaio的工作環境。讀寫請求下發到哪個設備是通過open函數打開的設備標志符指定。
2. 請求返回
讀寫請求下發之后,使用io_getevents函數等待io結束信號:
int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
io_getevents返回events的數組,其參數events為數組首地址,nr為數組長度(即最大返回的event數),min_nr為最少返回的events數。timeout可填NULL表示無等待超時。io_event結構體的聲明為:
struct io_event {
? ? PADDEDptr(void *data, __pad1);
? ? PADDEDptr(struct iocb *obj, ?__pad2);
? ? PADDEDul(res, ?__pad3);
? ? PADDEDul(res2, __pad4);
};
其中,res為實際完成的字節數;res2為讀寫成功狀態,0表示成功;obj為之前下發的struct iocb結構體。這里有必要了解一下struct iocb這個結構體的主要內容:
iocbp->iocb.u.c.nbytes 字節數
iocbp->iocb.u.c.offset 偏移
iocbp->iocb.u.c.buf 緩沖空間
iocbp->iocb.u.c.flags 讀寫
3. 自定義字段
struct iocb除了自帶的元素外,還留有供用戶自定義的元素,包括回調函數和void *的data指針。如果在請求下發前用io_set_callback綁定用戶自定義的回調函數,那么請求返回后就可以顯示的調用該函數。回調函數的類型為:
void callback_function(io_context_t ctx, struct iocb *iocb, long res, long res2);
另外,還可以通過iocbp->data指針掛上用戶自己的數據。
注意:實際使用中發現回調函數和data指針不能同時用,可能回調函數本身就是使用的data指針。
四、使用例子
通過上面的說明并不能完整的了解libaio的用法,下面通過簡單的例子進一步說明。
#include <stdlib.h>
#include <stdio.h>
#include <libaio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libaio.h>
int srcfd=-1;
int odsfd=-1;
#define AIO_BLKSIZE ?1024
#define AIO_MAXIO 64
static void wr_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
? ? ? ?if(res2 != 0)
? ? ? ?{
? ? ? ? ? ? ? printf(“aio write error\n”);
? ? ? ?}
? ? ? ?if(res != iocb->u.c.nbytes)
? ? ? ?{
? ? ? ? ? ? ? printf( “write missed bytes expect %d got %d\n”, iocb->u.c.nbytes, res);
? ? ? ? ? ? ? exit(1);
? ? ? ?}
?
? ? ? ?free(iocb->u.c.buf);
? ? ? ?free(iocb);
}
static void rd_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
? ? ? ?/*library needs accessors to look at iocb*/
? ? ? ?int iosize = iocb->u.c.nbytes;
? ? ? ?char *buf = (char *)iocb->u.c.buf;
? ? ? ?off_t offset = iocb->u.c.offset;
? ? ? ?int ?tmp;
? ? ? ?char *wrbuff = NULL;
?
? ? ? ?if(res2 != 0)
? ? ? ?{
? ? ? ? ? ? ?printf(“aio read\n”);
? ? ? ?}
? ? ? ?if(res != iosize)
? ? ? ?{
? ? ? ? ? ? ? printf( “read missing bytes expect %d got %d”, iocb->u.c.nbytes, res);
? ? ? ? ? ? ? exit(1);
? ? ? ?}
?
? ? ? ?/*turn read into write*/
? ? ? ?tmp = posix_memalign((void **)&wrbuff, getpagesize(), AIO_BLKSIZE);
? ? ? ?if(tmp < 0)
? ? ? ?{
? ? ? ? ? ? ? printf(“posix_memalign222\n”);
? ? ? ? ? ? ? exit(1);
? ? ? ?}
?
? ? ? ?snprintf(wrbuff, iosize + 1, “%s”, buf);
?
? ? ? ?printf(“wrbuff-len = %d:%s\n”, strlen(wrbuff), wrbuff);
? ? ? ?printf(“wrbuff_len = %d\n”, strlen(wrbuff));
? ? ? ?free(buf);
? ? ? ?
? ? ? ?io_prep_pwrite(iocb, odsfd, wrbuff, iosize, offset);
? ? ? ?io_set_callback(iocb, wr_done);
? ? ? ?
? ? ? ?if(1!= (res=io_submit(ctx, 1, &iocb)))
? ? ? ? ? ? ? printf(“io_submit write error\n”);
? ? ? ?
? ? ? ?printf(“\nsubmit ?%d ?write request\n”, res);
}
void main(int args,void * argv[])
{
? ? int length = sizeof(“abcdefg”);
? ? char * content = (char * )malloc(length);
? ? io_context_t myctx;
? ? int rc;
? ? char * buff=NULL;
? ? int offset=0;
? ? int num,i,tmp;
? ??
? ? if(args<3)
? ? {
? ? ? ? printf(“the number of param is wrong\n”);
? ? ? ? exit(1);
? ? }
? ? ? if((srcfd=open(argv[1],O_RDWR))<0)
? ? ? {
? ? ? ? printf(“open srcfile error\n”);
? ? ? ? exit(1);
? ? ? }
? ? ? printf(“srcfd=%d\n”,srcfd);
? ? ? lseek(srcfd,0,SEEK_SET);
? ? ? write(srcfd,”abcdefg”,length);
? ? ??
? ? ? lseek(srcfd,0,SEEK_SET);
? ? ? read(srcfd,content,length);
? ? ? printf(“write in the srcfile successful,content is %s\n”,content);
? ? ? if((odsfd=open(argv[2],O_RDWR))<0)
? ? ? {
? ? ? ? close(srcfd);
? ? ? ? printf(“open odsfile error\n”);
? ? ? ? exit(1);
? ? ? }
? ? memset(&myctx, 0, sizeof(myctx));
? ? io_queue_init(AIO_MAXIO, &myctx);
?
? ? ?
? ? ? ?struct iocb *io = (struct iocb*)malloc(sizeof(struct iocb));
? ? ? ?int iosize = AIO_BLKSIZE;
? ? ? ?tmp = posix_memalign((void **)&buff, getpagesize(), AIO_BLKSIZE);
? ? ? ? if(tmp < 0)
? ? ? ? {
? ? ? ? ? ? ? printf(“posix_memalign error\n”);
? ? ? ? ? ? ? exit(1);
? ? ? ? ?}
? ? ? ? if(NULL == io)
? ? ? ? {
? ? ? ? ? ? printf( “io out of memeory\n”);
? ? ? ? ? ? ?exit(1);
? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? io_prep_pread(io, srcfd, buff, iosize, offset);
? ? ? ? ? ? ? ? ? ? ??
? ? ? ? io_set_callback(io, rd_done);
? ? ? ? ? ? ? ??
? ? ? ? ?printf(“START…\n\n”);
? ? ? ? ? ? ? ??
? ? ? ? ?rc = io_submit(myctx, 1, &io);
? ??
? ? ? ? ? if(rc < 0)
? ? ? ? ? ? ? ?printf(“io_submit read error\n”);
? ? ? ? ??
? ? ? ? ? printf(“\nsubmit ?%d ?read request\n”, rc);
? ? ? ? ? ? ?
? ? ? ? ? ?
? ? ? ? ? //m_io_queue_run(myctx);
? ? ? ? ??
? ? ? ?struct io_event events[AIO_MAXIO];
? ? ? ? ? io_callback_t cb;
? ? ? ? ?
? ? ? ? ? num = io_getevents(myctx, 1, AIO_MAXIO, events, NULL);
? ? ? ? ? printf(“\n%d io_request completed\n\n”, num);
? ? ??
? ? ? ? ? for(i=0;i<num;i++)
? ? ? ? ?{
? ? ? ? ? ? ? cb = (io_callback_t)events[i].data;
? ? ? ? ? ? ? struct iocb *io = events[i].obj;
? ? ? ? ? ??
? ? ? ? ? ? ? printf(“events[%d].data = %x, res = %d, res2 = %d\n”, i, cb, events[i].res, events[i].res2);
? ? ? ? ? ? ? cb(myctx, io, events[i].res, events[i].res2);
? ? ? ? ? }
? ? ?
}
I/O 模型
在深入介紹 AIO API 之前,讓我們先來探索一下 Linux 上可以使用的不同 I/O 模型。這并不是一個詳盡的介紹,但是我們將試圖介紹最常用的一些模型來解釋它們與異步 I/O 之間的區別。圖 1 給出了同步和異步模型,以及阻塞和非阻塞的模型。圖 1. 基本 Linux I/O 模型的簡單矩陣
?
每個 I/O 模型都有自己的使用模式,它們對于特定的應用程序都有自己的優點。本節將簡要對其一一進行介紹。
同步阻塞 I/O
最常用的一個模型是同步阻塞 I/O 模型。在這個模型中,用戶空間的應用程序執行一個系統調用,這會導致應用程序阻塞。這意味著應用程序會一直阻塞,直到系統調用完成為止(數據傳輸完成或發生錯誤)。調用應用程序處于一種不再消費 CPU 而只是簡單等待響應的狀態,因此從處理的角度來看,這是非常有效的。
圖 2 給出了傳統的阻塞 I/O 模型,這也是目前應用程序中最為常用的一種模型。其行為非常容易理解,其用法對于典型的應用程序來說都非常有效。在調用 read 系統調用時,應用程序會阻塞并對內核進行上下文切換。然后會觸發讀操作,當響應返回時(從我們正在從中讀取的設備中返回),數據就被移動到用戶空間的緩沖區中。然后應用程序就會解除阻塞(read 調用返回)。
圖 2. 同步阻塞 I/O 模型的典型流程
?
從應用程序的角度來說,read 調用會延續很長時間。實際上,在內核執行讀操作和其他工作時,應用程序的確會被阻塞。
同步非阻塞 I/O
同步阻塞 I/O 的一種效率稍低的變種是同步非阻塞 I/O。在這種模型中,設備是以非阻塞的形式打開的。這意味著 I/O 操作不會立即完成,read 操作可能會返回一個錯誤代碼,說明這個命令不能立即滿足(EAGAIN 或 EWOULDBLOCK),如圖 3 所示。圖 3. 同步非阻塞 I/O 模型的典型流程
?
非阻塞的實現是 I/O 命令可能并不會立即滿足,需要應用程序調用許多次來等待操作完成。這可能效率不高,因為在很多情況下,當內核執行這個命令時,應用程序必須要進行忙碌等待,直到數據可用為止,或者試圖執行其他工作。正如圖 3 所示的一樣,這個方法可以引入 I/O 操作的延時,因為數據在內核中變為可用到用戶調用 read 返回數據之間存在一定的間隔,這會導致整體數據吞吐量的降低。
異步阻塞 I/O
另外一個阻塞解決方案是帶有阻塞通知的非阻塞 I/O。在這種模型中,配置的是非阻塞 I/O,然后使用阻塞 select 系統調用來確定一個 I/O 描述符何時有操作。使 select 調用非常有趣的是它可以用來為多個描述符提供通知,而不僅僅為一個描述符提供通知。對于每個提示符來說,我們可以請求這個描述符可以寫數據、有讀數據可用以及是否發生錯誤的通知。圖 4. 異步阻塞 I/O 模型的典型流程 (select)
?
select 調用的主要問題是它的效率不是非常高。盡管這是異步通知使用的一種方便模型,但是對于高性能的 I/O 操作來說不建議使用。
異步非阻塞 I/O(AIO)
最后,異步非阻塞 I/O 模型是一種處理與 I/O 重疊進行的模型。讀請求會立即返回,說明 read 請求已經成功發起了。在后臺完成讀操作時,應用程序然后會執行其他處理操作。當 read 的響應到達時,就會產生一個信號或執行一個基于線程的回調函數來完成這次 I/O 處理過程。圖 5. 異步非阻塞 I/O 模型的典型流程
?
在一個進程中為了執行多個 I/O 請求而對計算操作和 I/O 處理進行重疊處理的能力利用了處理速度與 I/O 速度之間的差異。當一個或多個 I/O 請求掛起時,CPU 可以執行其他任務;或者更為常見的是,在發起其他 I/O 的同時對已經完成的 I/O 進行操作。
Linux 上的 AIO 簡介
linux下有aio封裝,aio_*系列的調用是glibc提供的,是glibc用線程+阻塞調用來模擬的,性能很差,為了能更多的控制io行為,可以使用更為低級libaio。
libaio項目: http://oss.oracle.com/projects/libaio-oracle/
libaio的使用并不復雜,過程為:libaio的初始化,io請求的下發和回收,libaio銷毀。
一、libaio接口
libaio提供下面五個主要API函數:
int io_setup(int maxevents, io_context_t *ctxp);
int io_destroy(io_context_t ctx);
int io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
五個宏定義:
void io_set_callback(struct iocb *iocb, io_callback_t cb);
void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset);
void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset);
void io_prep_pwritev(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset);
void io_prep_preadv(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset);
這五個宏定義都是操作struct iocb的結構體。struct iocb是libaio中很重要的一個結構體,用于表示IO,但是其結構略顯復雜,為了保持封裝性不建議直接操作其元素而用上面五個宏定義操作。
二、libaio的初始化和銷毀
觀察libaio五個主要API,都用到類型為io_context的變量,這個變量為libaio的工作空間。不用具體去了解這個變量的結構,只需要了解其相關操作。創建和銷毀libaio分別用到io_setup(也可以用io_queue_init,區別只是名字不一樣而已)和io_destroy。
int io_setup(int maxevents, io_context_t *ctxp);
int io_destroy(io_context_t ctx);
三、libaio讀寫請求的下發和回收
1. 請求下發
libaio的讀寫請求都用io_submit下發。下發前通過io_prep_pwrite和io_prep_pread生成iocb的結構體,做為io_submit的參數。這個結構體中指定了讀寫類型、起始扇區、長度和設備標志符。
libaio的初始化不是針對一個具體設備進行初始,而是創建一個libaio的工作環境。讀寫請求下發到哪個設備是通過open函數打開的設備標志符指定。
2. 請求返回
讀寫請求下發之后,使用io_getevents函數等待io結束信號:
int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
io_getevents返回events的數組,其參數events為數組首地址,nr為數組長度(即最大返回的event數),min_nr為最少返回的events數。timeout可填NULL表示無等待超時。io_event結構體的聲明為:
struct io_event {
? ? PADDEDptr(void *data, __pad1);
? ? PADDEDptr(struct iocb *obj, ?__pad2);
? ? PADDEDul(res, ?__pad3);
? ? PADDEDul(res2, __pad4);
};
其中,res為實際完成的字節數;res2為讀寫成功狀態,0表示成功;obj為之前下發的struct iocb結構體。這里有必要了解一下struct iocb這個結構體的主要內容:
iocbp->iocb.u.c.nbytes 字節數
iocbp->iocb.u.c.offset 偏移
iocbp->iocb.u.c.buf 緩沖空間
iocbp->iocb.u.c.flags 讀寫
3. 自定義字段
struct iocb除了自帶的元素外,還留有供用戶自定義的元素,包括回調函數和void *的data指針。如果在請求下發前用io_set_callback綁定用戶自定義的回調函數,那么請求返回后就可以顯示的調用該函數。回調函數的類型為:
void callback_function(io_context_t ctx, struct iocb *iocb, long res, long res2);
另外,還可以通過iocbp->data指針掛上用戶自己的數據。
注意:實際使用中發現回調函數和data指針不能同時用,可能回調函數本身就是使用的data指針。
四、使用例子
通過上面的說明并不能完整的了解libaio的用法,下面通過簡單的例子進一步說明。
#include <stdlib.h>
#include <stdio.h>
#include <libaio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libaio.h>
int srcfd=-1;
int odsfd=-1;
#define AIO_BLKSIZE ?1024
#define AIO_MAXIO 64
static void wr_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
? ? ? ?if(res2 != 0)
? ? ? ?{
? ? ? ? ? ? ? printf(“aio write error\n”);
? ? ? ?}
? ? ? ?if(res != iocb->u.c.nbytes)
? ? ? ?{
? ? ? ? ? ? ? printf( “write missed bytes expect %d got %d\n”, iocb->u.c.nbytes, res);
? ? ? ? ? ? ? exit(1);
? ? ? ?}
?
? ? ? ?free(iocb->u.c.buf);
? ? ? ?free(iocb);
}
static void rd_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
? ? ? ?/*library needs accessors to look at iocb*/
? ? ? ?int iosize = iocb->u.c.nbytes;
? ? ? ?char *buf = (char *)iocb->u.c.buf;
? ? ? ?off_t offset = iocb->u.c.offset;
? ? ? ?int ?tmp;
? ? ? ?char *wrbuff = NULL;
?
? ? ? ?if(res2 != 0)
? ? ? ?{
? ? ? ? ? ? ?printf(“aio read\n”);
? ? ? ?}
? ? ? ?if(res != iosize)
? ? ? ?{
? ? ? ? ? ? ? printf( “read missing bytes expect %d got %d”, iocb->u.c.nbytes, res);
? ? ? ? ? ? ? exit(1);
? ? ? ?}
?
? ? ? ?/*turn read into write*/
? ? ? ?tmp = posix_memalign((void **)&wrbuff, getpagesize(), AIO_BLKSIZE);
? ? ? ?if(tmp < 0)
? ? ? ?{
? ? ? ? ? ? ? printf(“posix_memalign222\n”);
? ? ? ? ? ? ? exit(1);
? ? ? ?}
?
? ? ? ?snprintf(wrbuff, iosize + 1, “%s”, buf);
?
? ? ? ?printf(“wrbuff-len = %d:%s\n”, strlen(wrbuff), wrbuff);
? ? ? ?printf(“wrbuff_len = %d\n”, strlen(wrbuff));
? ? ? ?free(buf);
? ? ? ?
? ? ? ?io_prep_pwrite(iocb, odsfd, wrbuff, iosize, offset);
? ? ? ?io_set_callback(iocb, wr_done);
? ? ? ?
? ? ? ?if(1!= (res=io_submit(ctx, 1, &iocb)))
? ? ? ? ? ? ? printf(“io_submit write error\n”);
? ? ? ?
? ? ? ?printf(“\nsubmit ?%d ?write request\n”, res);
}
void main(int args,void * argv[])
{
? ? int length = sizeof(“abcdefg”);
? ? char * content = (char * )malloc(length);
? ? io_context_t myctx;
? ? int rc;
? ? char * buff=NULL;
? ? int offset=0;
? ? int num,i,tmp;
? ??
? ? if(args<3)
? ? {
? ? ? ? printf(“the number of param is wrong\n”);
? ? ? ? exit(1);
? ? }
? ? ? if((srcfd=open(argv[1],O_RDWR))<0)
? ? ? {
? ? ? ? printf(“open srcfile error\n”);
? ? ? ? exit(1);
? ? ? }
? ? ? printf(“srcfd=%d\n”,srcfd);
? ? ? lseek(srcfd,0,SEEK_SET);
? ? ? write(srcfd,”abcdefg”,length);
? ? ??
? ? ? lseek(srcfd,0,SEEK_SET);
? ? ? read(srcfd,content,length);
? ? ? printf(“write in the srcfile successful,content is %s\n”,content);
? ? ? if((odsfd=open(argv[2],O_RDWR))<0)
? ? ? {
? ? ? ? close(srcfd);
? ? ? ? printf(“open odsfile error\n”);
? ? ? ? exit(1);
? ? ? }
? ? memset(&myctx, 0, sizeof(myctx));
? ? io_queue_init(AIO_MAXIO, &myctx);
?
? ? ?
? ? ? ?struct iocb *io = (struct iocb*)malloc(sizeof(struct iocb));
? ? ? ?int iosize = AIO_BLKSIZE;
? ? ? ?tmp = posix_memalign((void **)&buff, getpagesize(), AIO_BLKSIZE);
? ? ? ? if(tmp < 0)
? ? ? ? {
? ? ? ? ? ? ? printf(“posix_memalign error\n”);
? ? ? ? ? ? ? exit(1);
? ? ? ? ?}
? ? ? ? if(NULL == io)
? ? ? ? {
? ? ? ? ? ? printf( “io out of memeory\n”);
? ? ? ? ? ? ?exit(1);
? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? io_prep_pread(io, srcfd, buff, iosize, offset);
? ? ? ? ? ? ? ? ? ? ??
? ? ? ? io_set_callback(io, rd_done);
? ? ? ? ? ? ? ??
? ? ? ? ?printf(“START…\n\n”);
? ? ? ? ? ? ? ??
? ? ? ? ?rc = io_submit(myctx, 1, &io);
? ??
? ? ? ? ? if(rc < 0)
? ? ? ? ? ? ? ?printf(“io_submit read error\n”);
? ? ? ? ??
? ? ? ? ? printf(“\nsubmit ?%d ?read request\n”, rc);
? ? ? ? ? ? ?
? ? ? ? ? ?
? ? ? ? ? //m_io_queue_run(myctx);
? ? ? ? ??
? ? ? ?struct io_event events[AIO_MAXIO];
? ? ? ? ? io_callback_t cb;
? ? ? ? ?
? ? ? ? ? num = io_getevents(myctx, 1, AIO_MAXIO, events, NULL);
? ? ? ? ? printf(“\n%d io_request completed\n\n”, num);
? ? ??
? ? ? ? ? for(i=0;i<num;i++)
? ? ? ? ?{
? ? ? ? ? ? ? cb = (io_callback_t)events[i].data;
? ? ? ? ? ? ? struct iocb *io = events[i].obj;
? ? ? ? ? ??
? ? ? ? ? ? ? printf(“events[%d].data = %x, res = %d, res2 = %d\n”, i, cb, events[i].res, events[i].res2);
? ? ? ? ? ? ? cb(myctx, io, events[i].res, events[i].res2);
? ? ? ? ? }
? ? ?
}
總結
- 上一篇: linux AIO (异步IO) 那点事
- 下一篇: 子网掩码和网关的关系