歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

異步I/O之用戶空間

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

Copyright © Linux教程網 All Rights Reserved