異步 IO 訪問
信號機制提供了一種以異步方式通知某種事件已發(fā)生的方法,但是,這種異步 I/O 是有限制的,它們并不能用在所有的文件類型上,而且只能使用一個信號。如果要對一個以上的文件描述符進行異步 I/O,那么在進程收到該信號時并不知道這一信號對應哪一個文件描述符。
SylixOS 支持 POSIX1003.1b 實時擴展協(xié)議規(guī)定的標準異步 I/O 接口,即 aio_read 函數(shù)、aio_write 函數(shù)、aio_fsync 函數(shù)、aio_cancel 函數(shù)、aio_error 函數(shù)、aio_return 函數(shù)、aio_suspend 函數(shù)和 lio_listio 函數(shù)。這組 API 用來操作異步 I/O。異步 I/O 是針對同步 I/O 提出的概念,它不需要線程等待 I/O 結果,只需要請求進行傳輸,系統(tǒng)會自動完成 I/O 傳輸,結束或者出現(xiàn)錯誤時會產(chǎn)生相應的 I/O 信號,用戶程序只需要設置好對應的信號陷入函數(shù),即可處理一個異步 I/O 事件。
POSIX 異步 I/O
POSIX 異步 I/O 接口為不同類型的文件進行異步 I/O 提供了一套一致的方法。這些接口來自實時草案標準,這些異步 I/O 接口使用 AIO 控制塊來描述 I/O 操作。aiocb 結構體定義了 AIO 控制塊,SylixOS 實現(xiàn)該結構體如所示:
struct aiocb {
int aio_fildes; /* file descriptor */
off_t aio_offset; /* file offset */
volatile void *aio_buf; /* Location of buffer. */
size_t aio_nbytes; /* Length of transfer. */
int aio_reqprio; /* Request priority offset. */
struct sigevent aio_sigevent; /* Signal number and value. */
int aio_lio_opcode; /* Operation to be performed. */
……
};
下面對該結構的成員做詳細說明:
- aio_fildes 是要操作的文件描述符,即 open 函數(shù)返回的文件描述符。
- aio_offset 是讀、寫開始的文件偏移量。
- aio_buf 是數(shù)據(jù)緩沖區(qū)指針,對于讀,從該緩沖區(qū)讀出數(shù)據(jù)。對于寫,向該緩沖區(qū)中寫入數(shù)據(jù)。
- aio_nbytes 是讀、寫的字節(jié)數(shù)。
- aio_reqprio 為異步 I/O 請求的優(yōu)先級,該優(yōu)先級決定了讀寫的順序,也就意味著優(yōu)先級越高越先被讀或者寫。
- aio_sigevent 是要發(fā)送的信號,當一次讀或者寫完成后,會發(fā)送應用指定的信號。
- aio_lio_opcode 是異步 I/O 操作的類型,如表 5.10 所示。
表 5.10 異步 I/O 操作類型
操作類型 | 說明 |
---|---|
LIO_NOP | 沒有傳輸請求 |
LIO_READ | 請求一個讀操作 |
LIO_WRITE | 請求一個寫操作 |
LIO_SYNC | 請求異步 I/O 同步 |
在異步 I/O 操作之前我們需要先初始化 AIO 控制塊,然后通過調用 aio_read 函數(shù)請求異步讀操作和調用 aio_write 函數(shù)請求異步寫操作。
#include <aio.h>
int aio_read(struct aiocb *paiocb);
int aio_write(struct aiocb *paiocb);
函數(shù) aio_read 原型分析:
- 此函數(shù)成功返回 0,失敗返回-1 并設置錯誤號。
- 參數(shù) paiocb 是 AIO 控制塊。
函數(shù) aio_write 原型分析:
- 此函數(shù)成功返回 0,失敗返回-1 并設置錯誤號。
- 參數(shù) paiocb 是 AIO 控制塊。
調用 aio_read 函數(shù)和 aio_write 函數(shù)成功之后,異步 I/O 請求便已經(jīng)被操作系統(tǒng)放入等待處理的隊列中了。這些返回值與實際 I/O 操作的結果沒有任何關系,如果需要查看函數(shù)的返回狀態(tài)可調用 aio_error 函數(shù)。
如果想強制所有等待中的異步操作不等待而寫入存儲中,可以建立一個 AIO 控制塊并調用 aio_fsync 函數(shù)。
#include <aio.h>
int aio_fsync(int op, struct aiocb *paiocb);
函數(shù) aio_fsync 原型分析:
- 此函數(shù)成功返回 0,失敗返回-1 并設置錯誤號。
- 參數(shù) op 是操作選項。
- 參數(shù) paiocb 是 AIO 控制塊。
結構體 struct aiocb 中 aio_fildes 成員是要被同步的文件,如果 op 參數(shù)設定為 O_SYNC, aio_fsync 調用類似于 fsync,如果 op 參數(shù)設定為 O_DSYNC,aio_fsync 調用類似于 fdatasync(目前 SylixOS 沒有對這兩種情況做具體的區(qū)分)。
同 aio_read 函數(shù)和 aio_write 函數(shù)一樣,查看 aio_fsync 函數(shù)處理的狀態(tài)可調用 aio_error。
#include <aio.h>
int aio_error(const struct aiocb *paiocb);
函數(shù) aio_error 原型分析:
- 此函數(shù)成功返回 0,失敗返回-1 并設置錯誤號。
- 參數(shù) paiocb 是 AIO 控制塊。
返回值應為下面的 4 種情況之一:
- 0:異步操作成功完成,調用 aio_return 函數(shù)可獲得返回碼。
- -1:對 aio_error 調用失敗。
- EINPROGRESS:異步操作等待中。
- 其他情況:其他任何返回值是相關異步操作失敗返回的錯誤碼。
如果異步操作成功,可以調用 aio_return 函數(shù)來獲得異步操作的返回值。
#include <aio.h>
ssize_t aio_return(struct aiocb *paiocb);
函數(shù) aio_return 原型分析:
- 此函數(shù)成功返回異步操作完成的返回值,失敗返回-1 并設置錯誤號。
- 參數(shù) paiocb 是 AIO 控制塊。
執(zhí)行 I/O 操作時,如果還有其他事務要處理而不想被 I/O 操作阻塞,就可以使用異步 I/O。然而,如果在完成了所有事務時,還有異步操作未完成時,可以調用 aio_suspend 函數(shù)來阻塞進程,直到操作完成。
#include <aio.h>
int aio_suspend(const struct aiocb * const list[], int nent,
const struct timespec *timeout);
函數(shù) aio_suspend 原型分析:
- 此函數(shù)成功返回 0,失敗返回-1 并設置錯誤號。
- 參數(shù) list 是 AIO 控制塊數(shù)組。
- 參數(shù) nent 是數(shù)組元素個數(shù)。
- 參數(shù) timeout 是設定的超時時間。
aio_suspend 可能會返回以下 3 種情況中的一種:
- 如果阻塞超時了,那么 aio_suspend 函數(shù)將返回-1。
- 如果有任何 I/O 操作完成,aio_suspend 函數(shù)將返回 0。
- 如果在調用 aio_suspend 函數(shù)時,所有的異步 I/O 操作都已完成,那么 aio_suspend 函數(shù)將在不阻塞的情況下直接返回。
參數(shù) list 會自動忽略空指針,非空元素是經(jīng)過初始化的 AIO 控制塊。
當還存在不需要再完成的等待中的異步 I/O 操作時,可以調用 aio_cancel 函數(shù)來取消它們。
#include <aio.h>
int aio_cancel(int fildes, struct aiocb *paiocb);
函數(shù) aio_cancel 原型分析:
- 此函數(shù)返回值,如表 5.11 所示。
表 5.11 aio_cancel 返回值類型
返回值類型 | 說明 |
---|---|
AIO_CANCELED | 所有請求的操作已被取消 |
AIO_NOTCANCELED | 至少有一個請求操作沒被取消 |
AIO_ALLDONE | 在請求取消之前已經(jīng)完成 |
-1 | aio_cancel 函數(shù)調用失敗 |
- 參數(shù) fildes 是要操作的文件描述符。
- 參數(shù) paiocb 是 AIO 控制塊。
如果 paiocb 為 NULL,系統(tǒng)將會嘗試取消所有該文件上未完成的異步 I/O 操作。其他情況下,系統(tǒng)將嘗試取消由 paiocb 指定的 AIO 控制塊描述的單個異步 I/O 操作。
如果異步 I/O 操作被成功取消,那么相應的 AIO 控制塊調用 aio_error 函數(shù)將會返回錯誤 ECANCELED。如果操作不能被取消,那么相應的 AIO 控制塊不會因為對 aio_cancel 函數(shù)的調用而被修改。
下面程序展示了以上函數(shù)的使用方法,程序構造一個讀操作的 AIO 控制塊,然后調用 aio_read 請求讀操作,操作完成后會通過信號(見信號章節(jié))通知給 signal_handler 函數(shù),以做進一步的處理。
#include <aio.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define BUFSIZE (64)
void signal_handler (union sigval val)
{
struct aiocb *paio = (struct aiocb *)val.sival_ptr;
ssize_t ret;
ret = aio_return(paio);
if (ret < 0) {
fprintf(stderr, "aio_return error.\n");
return;
}
fprintf(stdout, "len: %ld\n content: %s\n", ret, (char *)(paio->aio_buf));
}
int main (int argc, char *argv[])
{
int fd;
int ret;
static struct aiocb aio;
const struct aiocb *const list[] = {&aio};
fd = open("file", O_RDONLY);
if (fd < 0) {
fprintf(stderr, "open file failed.\n");
return (-1);
}
memset(&aio, 0, sizeof(struct aiocb));
/*
* 設置AIO控制塊
*/
aio.aio_fildes = fd;
aio.aio_buf = malloc(BUFSIZE + 1);
aio.aio_nbytes = BUFSIZE;
aio.aio_offset = 0;
aio.aio_reqprio = 1;
aio.aio_lio_opcode = LIO_READ;
aio.aio_sigevent.sigev_notify = SIGEV_THREAD | SIGEV_SIGNAL;
aio.aio_sigevent.sigev_value.sival_ptr = (void *)&aio;
aio.aio_sigevent.sigev_signo = SIGUSR1;
aio.aio_sigevent.sigev_notify_function = signal_handler;
ret = aio_read(&aio);
if (ret < 0) {
perror("aio_read");
close(fd);
return (-1);
}
aio_suspend(list, 1, NULL);
close(fd);
return (0);
}
在 SylixOS Shell 下運行程序:
# ./Function_AsynchronousIO
len: 64
content: This is a sylixos test file
調用 lio_listio 函數(shù)可以提交一系列由一個 AIO 控制塊列表描述的 I/O 請求。
#include <aio.h>
int lio_listio(int mode, struct aiocb * const list[], int nent,
struct sigevent *sig);
函數(shù) lio_listio 原型分析:
- 此函數(shù)成功返回 0,失敗返回-1 并設置錯誤號。
- 參數(shù) mode 是傳輸模式(LIO_WAIT、LIO_NOWAIT)。
- 參數(shù) list 是請求 AIO 控制塊數(shù)組。
- 參數(shù) nent 是 AIO 控制塊數(shù)組。
- 參數(shù) sig 是所有 I/O 操作完成后產(chǎn)生的信號方法。
mode 參數(shù)決定了 I/O 是否真的是異步的。如果該參數(shù)被設定為 LIO_WAIT,lio_listio 函數(shù)將在所有由列表指定的 I/O 操作完成后返回,這種情況下,參數(shù) sig 將被忽略。如果 mode 參數(shù)被設定為 LIO_NOWAIT,lio_listio 函數(shù)將在 I/O 請求入隊后立即返回。進程將在所有 I/O 操作完成后,按照參數(shù) sig 指定的被異步通知。如果不想被通知,可以把參數(shù) sig 設置為 NULL,需要注意的是,每個異步 I/O 操作可以設定自己的通知方式,而參數(shù) sig 指定的通知方式,是額外的通知方式,并且只有所有操作都完成后才會通知。
在每一個 AIO 控制塊中,aio_lio_opcode 字段指定了該操作是一個讀操作(LIO_READ)、寫操作(LIO_WRITE),還是將被忽略的空操作(LIO_NOP)。
下面程序展示了 lio_listio 函數(shù)的用法,從代碼可以看出一次 lio_listio 函數(shù)調用發(fā)起了多個傳輸,從這一點可以得出 lio_listio 函數(shù)的性能方面得到了提高。
#include <aio.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define BUFSIZE (64)
void signal_handler (union sigval val)
{
fprintf(stdout, "async operator complete.\n");
}
int main (int argc, char *argv[])
{
int fd;
int ret;
struct aiocb aio[2];
struct aiocb *const list[] = {&aio[0], &aio[1]};
static struct sigevent notify;
fd = open("file", O_RDONLY);
if (fd < 0) {
fprintf(stderr, "open file failed.\n");
return (-1);
}
memset(&aio[0], 0, sizeof(struct aiocb));
memset(&aio[1], 0, sizeof(struct aiocb));
/*
* 設置第一個AIO控制塊
*/
aio[0].aio_fildes = fd;
aio[0].aio_buf = malloc(BUFSIZE + 1);
aio[0].aio_nbytes = BUFSIZE;
aio[0].aio_offset = 0;
aio[0].aio_reqprio = 1;
aio[0].aio_lio_opcode = LIO_READ;
/*
* 設置第二個AIO控制塊
*/
aio[1].aio_fildes = fd;
aio[1].aio_buf = malloc(BUFSIZE + 1);
aio[1].aio_nbytes = BUFSIZE;
aio[1].aio_offset = BUFSIZE;
aio[1].aio_reqprio = 2;
aio[1].aio_lio_opcode = LIO_READ;
notify.sigev_signo = SIGUSR1;
notify.sigev_notify_function = signal_handler;
notify.sigev_notify = SIGEV_THREAD | SIGEV_SIGNAL;
ret = lio_listio(LIO_NOWAIT, list, 2, ¬ify);
if (ret < 0) {
perror("lio_listio");
close(fd);
return (-1);
}
sleep(60);
close(fd);
return (0);
}
在 SylixOS Shell 下運行程序:
# ./lio_listio_test
signal handler.
SylixOS 異步 I/O 的實現(xiàn)中,會通過一個額外的線程(代理線程)對 I/O 進行操作,為了能夠設置或者獲取代理線程的棧信息,SylixOS 增加了下面的一組函數(shù),這組函數(shù)并非標準中定義。需要注意的是,設置的棧信息只對將來啟動的線程有效(這通常發(fā)生在將來的 I/O 請求)。
#include <aio.h>
int aio_setstacksize(size_t newsize);
size_t aio_getstacksize(void);
函數(shù) aio_setstacksize 原型分析:
- 此函數(shù)成功返回 0,失敗返回-1 并設置錯誤號。
- 參數(shù) newsize 是新的棧大小。
函數(shù) aio_getstacksize 原型分析:文章來源:http://www.zghlxwxcb.cn/news/detail-467729.html
- 此函數(shù)總是返回當前線程棧的大小。
調用 aio_setstacksize 函數(shù)可以設置將來啟動線程(代理線程)的棧大小,調用 aio_getstacksize 函數(shù)可以獲得當前線程(代理線程)棧的大小。文章來源地址http://www.zghlxwxcb.cn/news/detail-467729.html
到了這里,關于SylixOS 異步 IO 訪問的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!