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
上文來自:http://blog.csdn.net/gotosola/article/details/7412409