POSIX 非同步IO介紹
POSIX.1b定義了一系列新的IO操作,能較大地減少應用程式的IO等待時間。這些新函式允許應用程式發起一個或多個IO操作請求後立即返回做其他事情不必等到IO完成,IO操作可以跟應用程式並行執行。AIO控制塊 struct aiocb 描述:
int aio_fildes
需要操作的檔案描述符,所關聯的檔案必須能支援seek操作。像pipe、socket等裝置不能使用aio。
off_t aio_offset
偏移量,aio將在檔案的這個位置進行操作(讀/寫)。
volatile void* aio_buf
指向緩衝區的指標,對於讀操作該緩衝區用於儲存讀出來的資料;對於寫操作則把需要寫到檔案的資料放在此緩衝區裡面。
size_t aio_nbytes
aio_buf所指向的緩衝區的長度。
int aio_reqprio
AIO操作的優先順序。
struct sigevent aio_sigevent
當AIO操作結束(完成或者出錯)時以什麼樣的方式通知呼叫aio的程序。aio_sigevent.sigev_notify為SIGEV_NONE表示不通知。SIGEV_SIGNAL表示以訊號方式通知,aio_sigevent.sigev_signo指定用於通知的訊號(一般用SIGIO)。除此以外,aio_sigevent.sigev_notify只能為SIGEV_THREAD,表示AIO操作結束時以執行緒的方式執行aio_sigevent.sigev_notify_function指定的函式。
int aio_lio_opcode
這個引數僅當使用lio_listio時會用到,因為lio_listio允許同時發起一系列AIO操作,每個操作都可以是讀或寫,因此這個引數用來指定需要執行的操作,可以是:LIO_READ表示讀,LIO_WRITE表示寫,LIO_NOP表示無操作。
基本API:
int aio_read(struct aiocb *aiocbp)
發起一個非同步讀操作,當請求開始排隊或者排隊之前發現錯誤時函式立即返回,不會像read一樣阻塞。
函式會從檔案(aiocbp->aio_fildes)的指定位置(aiocbp->aio_offset)最多讀取aiocbp->aio_nbytes位元組資料到aiocbp->aio_buf中。如果系統支援把IO區別優先次序的話,進入排隊之前會根據aiocbp->aio_reqprio調整優先順序。
讀操作完成時會根據aiocbp->aio_sigevent指定的方式通知呼叫程序。
aio_read返回0表示沒有錯誤發生,否則返回-1,errno指出以下幾種出錯情況:
EAGAIN
資源(臨時)不足,請求無法進入排隊。
ENOSYS
aio_read沒有實現。
EBADF
aiocbp->aio_fildes指定的檔案描述符無效。
EINVAL
aiocbp->aio_offset 或 aiocbp->aio_reqprio 的值無效。
當aio_read返回成功後可以用aio_error和aio_return查詢請求狀態。如果aio_error返回EINPROGRESS說明操作還沒有完成,如果aio_error返回0說明請求已經成功完成,否則返回值代表錯誤碼。如果請求已經完成,可以用aio_return取得操作結果。返回值跟read函式相同。aio_error可能返回的錯誤碼有:
EBADF
aiocbp->aio_fildes指定的檔案描述符無效。
ECANCELED
請求在完成前被取消。
EINVAL
aiocbp->aio_offset的值無效。
int aio_write(struct aiocb *aiocbp)
發起一個非同步寫操作,當請求開始排隊或者排隊之前發現錯誤時函式立即返回。函式將aiocbp->aio_buf中的前aiocbp->aio_nbytes位元組資料寫到aiocbp->aio_fildes指定檔案的aiocbp->aio_offset位置。
出錯返回等資訊基本同aio_read.
int lio_listio(int mode, struct aiocb *const list[], int nent, struct sigevent *sig)
同時發起多個IO操作,每個操作都可以是讀或者寫,也可以同上面描述的LIO_NOP表示沒有操作。這些操作可以針對同一個fd也可以針對不同的fd。
引數mode可以為LIO_WAIT或者LIO_NOWAIT。前者表示lio_listio函式阻塞,直到list中的所有操作完成lio_listio返回,0表示全部成功,-1表示出錯,如果要知道那個操作出錯則必須使用aio_error遍歷list;後者則不阻塞,當list中所有操作排隊成功後立即返回0,返回-1表示出錯。LIO_NOWAIT模式下,如果全部操作都完成(或出錯)則會依照最後一個引數sig的設定通知呼叫程序,如果sig為NULL則表示不通知。
int aio_error(const struct aiocb *aiocbp)
查詢aio操作的狀態,返回0表示已經完成,返回EINPROGRESS表示未完成,返回其他值表示出錯。
int aio_return(const struct aiocb *aiocbp)
獲得aio操作的返回狀態,對一個aio_error返回的狀態為EINPROGRESS的aiocb呼叫aio_return,其結果是未定義的,所以呼叫aio_return之前一定要先用aio_error查詢其狀態。
如果aio操作已經完成(aio_error返回0),則可以呼叫aio_return獲得其操作結果,返回值同read,write,即返回成功讀寫的資料長度。
注意每個已完成io請求aio_return只能呼叫一次,第二次呼叫導致的結果是未定義的。
下面這個demo演示怎麼通過訊號的方式通知呼叫程序AIO已經完成:
程式使用aio_read讀一個檔案,當IO完成時程序會收到SIGIO訊號,從而進入訊號處理程式sigio_handler將讀到的內容輸出到標準輸出。在訊號處理函式裡面呼叫write是不安全的,因為這不是一個可重入函式,但是這僅是一個demo,圖個方便了。
下面這個例子是通過回撥函式的方式通知呼叫程序AIO操作已經完成:
程式也是以只讀方式開啟檔案,發出讀請求後就pause,當讀操作完成後AIO會以執行緒的方式執行read_notify。
參考資料: