Linux 非同步IO機制 Linux的I/O機制經曆了一下幾個階段的演化: 1. 同步阻塞I/O: 使用者進程進行I/O操作,一直阻塞到I/O操作完成為止。 2. 同步非阻塞I/O: 使用者程式可以通過設定檔案描述符的屬性O_NONBLOCK,I/O操作可以立即返回,但是並不保證I/O操作成功。 3. 非同步事件阻塞I/O: 使用者進程可以對I/O事件進行阻塞,但是I/O操作並不阻塞。通過select/poll/epoll等函數調用來達到此目的。 4. 非同步時間非阻塞I/O: 也叫做非同步I/O(AIO),使用者程式可以通過向核心發出I/O請求命令,不用等帶I/O事件真正發生,可以繼續做 另外的事情,等I/O操作完成,核心會通過函數回調或者訊號機制通知使用者進程。這樣很大程度提高了系統輸送量。 下面就AIO做詳細介紹: 要使用aio的功能,需要include標頭檔aio.h,在編譯串連的時候需要加入POSIX即時擴充庫rt.下面就aio庫的使用做介紹。 1. AIO整個過程所使用的資料存放在一個結構體中,struct aiocb,aio control block.看看標頭檔中的定義: /* Asynchronous I/O control block. */ struct aiocb { int aio_fildes; /* File desriptor. */ 需要在哪個檔案描述符上進行I/O int aio_lio_opcode; /* Operation to be performed. */ 這個是針對批量I/O的情況有效,讀寫操作類型 int aio_reqprio; /* Request priority offset. */ 請求優先順序(If _POSIX_PRIORITIZED_IO is defined, and this file supports it, then the asynchronous operation is submitted at a priority equal to that of the calling process minus aiocbp->aio_reqprio.) volatile void *aio_buf; /* Location of buffer. */ 具體內容,資料緩衝 size_t aio_nbytes; /* Length of transfer. */ 資料緩衝的長度 struct sigevent aio_sigevent; /* Signal number and value. */ 用於非同步I/O完成後的通知。 內部實現使用的資料成員。 /* Internal members. */ struct aiocb *__next_prio; int __abs_prio; int __policy; int __error_code; __ssize_t __return_value; #ifndef __USE_FILE_OFFSET64 __off_t aio_offset; /* File offset. */ char __pad[sizeof (__off64_t) - sizeof (__off_t)]; #else __off64_t aio_offset; /* File offset. */ 檔案讀寫位移 #endif char __unused[32]; }; 2. int aio_read(struct aiocb *aiocbp); 非同步讀操作,向核心發出讀的命令,傳入的參數是一個aiocb的結構,比如 struct aiocb myaiocb; memset(&aiocb , 0x00 , sizeof(myaiocb)); myaiocb.aio_fildes = fd; myaiocb.aio_buf = new char[1024]; myaiocb.aio_nbytes = 1024; if (aio_read(&myaiocb) != 0) { printf("aio_read error:%s/n" , strerror(errno)); return false; } 3. int aio_write(struct aiocb *aiocbp); 非同步寫操作,向核心發出寫的命令,傳入的參數仍然是一個aiocb的結構,當檔案描述符的O_APPEND 標誌位設定後,非同步寫操作總是將資料添加到檔案末尾。如果沒有設定,則添加到aio_offset指定的 地方,比如: struct aiocb myaiocb; memset(&aiocb , 0x00 , sizeof(myaiocb)); myaiocb.aio_fildes = fd; myaiocb.aio_buf = new char[1024]; myaiocb.aio_nbytes = 1024; myaiocb.aio_offset = 0; if (aio_write(&myaiocb) != 0) { printf("aio_read error:%s/n" , strerror(errno)); return false; } 4. int aio_error(const struct aiocb *aiocbp); 如果該函數返回0,表示aiocbp指定的非同步I/O操作請求完成。 如果該函數返回EINPROGRESS,表示aiocbp指定的非同步I/O操作請求正在處理中。 如果該函數返回ECANCELED,表示aiocbp指定的非同步I/O操作請求已經取消。 如果該函數返回-1,表示發生錯誤,檢查errno。 5. ssize_t aio_return(struct aiocb *aiocbp); 這個函數的傳回值相當於同步I/O中,read/write的傳回值。只有在aio_error調用後 才能被調用。 6. int aio_cancel(int fd, struct aiocb *aiocbp); 取消在檔案描述符fd上的aiocbp所指定的非同步I/O請求。 如果該函數返回AIO_CANCELED,表示操作成功。 如果該函數返回AIO_NOTCANCELED,表示取消操作不成功,使用aio_error檢查一下狀態。 如果返回-1,表示發生錯誤,檢查errno. 7. int lio_listio(int mode, struct aiocb *restrict const list[restrict], int nent, struct sigevent *restrict sig); 使用該函數,在很大程度上可以提高系統的效能,因為再一次I/O過程中,OS需要進行 使用者態和核心態的切換,如果我們將更多的I/O操作都放在一次使用者太和核心太的切換中, 減少切換次數,換句話說在核心盡量做更多的事情。這樣可以提高系統的效能。 使用者程式提供一個struct aiocb的數組,每個元素表示一次AIO的請求操作。需要設定struct aiocb 中的aio_lio_opcode資料成員的值,有LIO_READ,LIO_WRITE和LIO_NOP。 nent表示數組中元素的個數。最後一個參數是對AIO操作完成後的通知機制的設定。 8. 設定AIO的通知機制,有兩種通知機制:訊號和回調 (1).訊號機制 首先我們應該捕獲SIGIO訊號,對其作處理: struct sigaction sig_act; sigempty(&sig_act.sa_mask); sig_act.sa_flags = SA_SIGINFO; sig_act.sa_sigaction = aio_handler; struct aiocb myaiocb; bzero( (char *)&myaiocb, sizeof(struct aiocb) ); myaiocb.aio_fildes = fd; myaiocb.aio_buf = malloc(BUF_SIZE+1); myaiocb.aio_nbytes = BUF_SIZE; myaiocb.aio_offset = next_offset; myaiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; myaiocb.aio_sigevent.sigev_signo = SIGIO; myaiocb.aio_sigevent.sigev_value.sival_ptr = &myaiocb; ret = sigaction( SIGIO, &sig_act, NULL ); 訊號處理函數的實現: void aio_handler( int signo, siginfo_t *info, void *context ) { struct aiocb *req; if (info->si_signo == SIGIO) { req = (struct aiocb *)info->si_value.sival_ptr; if (aio_error( req ) == 0) { ret = aio_return( req ); } } return; } (2). 回調機制 需要設定: myaiocb.aio_sigevent.sigev_notify = SIGEV_THREAD my_aiocb.aio_sigevent.notify_function = aio_handler; 回呼函數的原型: typedef void (* FUNC_CALLBACK)(sigval_t sigval); AIO機製為伺服器端高並發應用程式提供了一種效能最佳化的手段。加大了系統輸送量。 |