天天看點

POSIX AIO -- glibc 版本異步 IO 簡介概述IO 模型簡介POSIX AIO -- glibc 版本異步 IO 簡介函數說明

概述

linux 中最常用的 IO 模型是同步 IO,在這個模型中,請求發出後應用程式會阻塞直到滿足條件(阻塞 IO),或在不滿足條件的情況下立即傳回出錯(非阻塞 IO),這樣做的好處是程式在等待 IO 請求完成時不會占用 CPU

POSIX 定義了異步 IO 應用程式接口(AIO API),linux 2.6 以上版本的核心也實作了核心級别的異步 IO 調用

異步 IO 的基本思想是允許程序發起很多 IO 操作,而不用阻塞任何一個,也不用等待任何操作的完成,直到 IO 操作完成時,程序可以檢索 IO 操作的結果

linux 下主要有兩套異步 IO,分别是 glibc 實作版本,和 linux 核心libaio 封裝的版本

IO 模型簡介

write、read 如果沒有設定 O_NONBLOCK 辨別則為同步阻塞式 IO,一旦 IO 發起,則程序一直等待直到操作完成

設定了 O_NONBLOCK 辨別後,write、read 成為非阻塞 IO,調用後如果資源可用則進行操作,并立即傳回,如果資源不可用則直接傳回出錯,這樣的情況下,程式通常需要進入忙等待狀态,反複調用 IO 操作,直到正常傳回

以上兩種 IO 模型雖然可以很好地完成單機的 IO 操作,但是對于并發的請求則無法實作

對于并發的多個請求,可以使用 IO 複用模型,如 select、poll、epoll 等,但是程序必須阻塞直到操作完成

如果需要進行并發、非阻塞的 IO 操作,比如 CPU 密集型應用及較慢的 IO 操作應用場景下,使用異步 IO 是一個很好地選擇

POSIX AIO -- glibc 版本異步 IO 簡介

glibc 版本異步 IO 主要包含以下接口(全部定義于 aio.h 中,調用時必須使用 POSIX 實時擴充庫 librt):

glibc 版本異步 IO 調用接口

函數 功能 原型
aio_read 請求異步讀操作 int aio_read(struct aiocb *aiocbp);
aio_write 請求異步寫操作 int aio_write(struct aiocb *aiocbp);
aio_error 檢查異步請求的狀态 int aio_error(const struct aiocb *aiocbp);
aio_return 獲得完成的異步請求的傳回狀态 ssize_t aio_return(struct aiocb *aiocbp);
aio_suspend 挂起調用程序,直到一個或多個異步請求已經完成(或失敗) int aio_suspend(const struct aiocb * const list[], int nent, const struct timespec *timeout);
aio_cancel 取消異步 I/O 請求 int aio_cancel(int fildes, struct aiocb *aiocbp);
lio_listio 同時發起多個異步IO傳輸 int lio_listio( int mode, struct aiocb *list[], int nent, struct sigevent *sig );

aiocb 結構

上述函數用到了一個 struct aiocb 結構

主要包含以下字段:

struct aiocb {

    int                aio_fildes;        // 要被讀寫的檔案描述符

    volatile void    *aio_buf;        // 讀寫操作的記憶體 buffer

    __off64_t        aio_offset;        // 讀寫操作的檔案偏移

    size_t            aio_nbytes;        // 需要讀寫的位元組長度

    int                aio_reqprio;    // 請求優先級

    struct sigevent aio_sigevent;    // 異步操作完成後的信号或回調函數

    ...

};

上述結構中有一個 aio_sigevent 域,用于定義異步操作完成時通知信号或回調函數

struct sigevent

{

    int                sigev_notify;                    // 響應類型

    int                sigev_signo;                    // 信号

    union sigval    sigev_value;                    // 信号傳遞的參數

    void (*sigev_notify_function)(union sigval);    // 回調函數

    pthread_attr_t    *sigev_notify_attributes;        // 線程回調

}

上述結構中用到了一個聯合體 sigval:

typedef union sigval

{

    int        sival_int;

    void    *sival_ptr;

} sigval_t;

通常被稱為“信号的 4 位元組值”,制定了信号傳遞的參數

函數說明

aio_read、aio_write

int aio_read( struct aiocb *aiocbp );

int aio_write( struct aiocb *aiocbp );

調用成功傳回 0,失敗傳回 -1 并設定 errno

将請求添加到 request_queue

通過參數 aiocbp 指向的結構可以設定檔案描述符、檔案偏移量、緩沖區及大小等屬性,函數執行後立即傳回

對于 aio_write,如果設定了 O_APPEND,則檔案偏移量屬性會被忽略

aio_error

int aio_error( struct aiocb *aiocbp );

用于查詢請求的狀态

傳回值如下:

aio_error 函數傳回值

傳回值 意義
EINPROGRESS 請求尚未完成
ECANCELLED 請求已經被用用程式取消
-1 調用出錯,出錯原因檢視 errno

aio_return

ssize_t aio_return( struct aiocb *aiocbp );

擷取異步 IO 傳回值

調用成功傳回讀寫的字元數,出錯傳回 -1

aio_suspend

int aio_suspend( const struct aiocb *const cblist[],

        int n,

        const struct timespec *timeout );

阻塞程序,直到清單中的某個異步請求完成

cblist 中任何一個異步請求完成,函數都會傳回 0,出錯傳回 -1

aio_cancel

int aio_cancel( int fd, struct aiocb *aiocbp );

取消一個異步請求,第二個參數為 NULL 則取消所有該 fd 上的異步請求

成功取消傳回 AIO_CANCELED,請求已經完成則傳回 AIO_NOTCANCELED

在取消多個請求的情況下,如果至少有一個請求沒有被取消,則傳回 AIO_NOT_CANCELED,如果沒有一個請求可以被取消,則傳回 AIO_ALLDONE

lio_listio

int lio_listio( int mode,

        struct aiocb *list[],

        int nent,

        struct sigevent *sig );

同時發起多個異步請求,可以很大程度上提高系統的性能

mode 參數可選 LIO_WAIT 或 LIO_NOWAIT 來聲明該函數是否阻塞

nent 參數定義了 list 清單的最大元素個數

list 清單中可以有值為 NULL 的請求,則該請求被忽略

sigevent 的指針定義了在所有 IO 操作都完成時産生的信号或調用的回調函數