AIO - Linux手册页

时间:2019-08-20 18:01:51  来源:igfitidea点击:

Linux程序员手册 第7部分
更新日期: 2020-08-13

名称

aio-POSIX异步I / O概述

说明

POSIX异步I / O(AIO)接口允许应用程序启动一个或多个异步执行的I / O操作(即在后台)。可以通过多种方式选择通过I / O操作完成来通知应用程序:通过传递信号,通过线程实例化,或者完全不通知。

POSIX AIO接口包含以下功能:

aio_read(3)
排队读取请求。这是read(2)的异步模拟。
aio_write(3)
排队写请求。这是write(2)的异步模拟。
aio_fsync(3)
使对文件描述符的I / O操作的同步请求入队。这是fsync(2)和fdatasync(2)的异步模拟。
aio_error(3)
获取排队的I / O请求的错误状态。
aio_return(3)
获取已完成的I / O请求的返回状态。
aio_suspend(3)
挂起呼叫者,直到完成一组指定的I / O请求中的一个或多个。
aio_cancel(3)
尝试取消对指定文件描述符的未完成I / O请求。
lio_listio(3)
使用单个函数调用排队多个I / O请求。

aiocb("异步I / O控制块")结构定义控制I / O操作的参数。上面列出的所有函数都使用此类型的参数。该结构具有以下形式:

#include <aiocb.h>

struct aiocb {
    /* The order of these fields is implementation-dependent */

    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 */
    struct sigevent aio_sigevent;   /* Notification method */
    int             aio_lio_opcode; /* Operation to be performed;
                                       lio_listio() only */

    /* Various implementation-internal fields not shown */
};

/* Operation codes for aqaio_lio_opcodeaq: */

enum { LIO_READ, LIO_WRITE, LIO_NOP };

此结构的字段如下:

aio_fildes
要对其执行I / O操作的文件描述符。
aio_offset
这是要执行I / O操作的文件偏移量。
aio_buf
这是用于传输数据以进行读或写操作的缓冲区。
aio_nbytes
这是aio_buf指向的缓冲区的大小。
aio_reqprio
该字段指定从调用线程的实时优先级中减去的值,以确定执行此I / O请求的优先级(请参阅pthread_setschedparam(3))。指定的值必须介于0和sysconf(_SC_AIO_PRIO_DELTA_MAX)返回的值之间。对于文件同步操作,将忽略此字段。
aio_sigevent
此字段是一种结构,用于指定异步I / O操作完成时如何通知调用方。 aio_sigevent.sigev_notify的可能值为SIGEV_NONE,SIGEV_SIGNAL和SIGEV_THREAD。有关更多详细信息,请参见sigevent(7)。
aio_lio_opcode
要执行的操作类型;仅用于lio_listio(3)。

除了上面列出的标准功能之外,GNU C库还为POSIX AIO API提供了以下扩展:

aio_init(3)
设置参数以调整glibc POSIX AIO实现的行为。

错误说明

EINVAL
aiocb结构的aio_reqprio字段小于0,或大于调用sysconf(_SC_AIO_PRIO_DELTA_MAX)返回的限制。

版本

从2.1版开始,glibc提供POSIX AIO接口。

遵循规范

POSIX.1-2001,POSIX.1-2008。

备注

在使用之前将控制块缓冲区清零是一个好主意(请参阅memset(3))。在I / O操作进行期间,不得更改控制块缓冲区和aio_buf指向的缓冲区。这些缓冲区必须保持有效,直到I / O操作完成。

使用相同的aiocb结构进行的同时异步读取或写入操作会产生不确定的结果。

glibc在用户空间中提供了当前的Linux POSIX AIO实现。这有很多限制,最明显的是,维护多个线程来执行I / O操作非常昂贵,并且伸缩性很差。基于内核状态机的异步I / O实现已经进行了一段时间(请参阅io_submit(2),io_setup(2),io_cancel(2),io_destroy(2),io_getevents(2)),但是该实现尚未成熟到可以使用内核系统调用完全重新实现POSIX AIO实现的地步。

示例

下面的程序打开在其命令行参数中命名的每个文件,并使用aio_read(3)在生成的文件描述符上对请求进行排队。然后,程序循环运行,并使用aio_error(3)定期监视仍在进行的每个I / O操作。每个I / O请求都设置为通过传递信号来提供通知。完成所有I / O请求后,程序将使用aio_return(3)检索其状态。

SIGQUIT信号(通过键入control- \生成)使程序使用aio_cancel(3)请求取消每个未完成的请求。

这是运行该程序时可能看到的示例。在此示例中,程序将两个请求排队到标准输入中,并且这些请求由包含" abc"和" x"的两行输入满足。

$ ./a.out /dev/stdin /dev/stdin
opened /dev/stdin on descriptor 3
opened /dev/stdin on descriptor 4
aio_error():
    for request 0 (descriptor 3): In progress
    for request 1 (descriptor 4): In progress
abc
I/O completion signal received
aio_error():
    for request 0 (descriptor 3): I/O succeeded
    for request 1 (descriptor 4): In progress
aio_error():
    for request 1 (descriptor 4): In progress
x
I/O completion signal received
aio_error():
    for request 1 (descriptor 4): I/O succeeded
All I/O requests completed
aio_return():
    for request 0 (descriptor 3): 4
    for request 1 (descriptor 4): 2

Program source

#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <aio.h>
#include <signal.h>

#define BUF_SIZE 20     /* Size of buffers for read operations */

#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

#define errMsg(msg)  do { perror(msg); } while (0)

struct ioRequest {      /* Application-defined structure for tracking
                           I/O requests */
    int           reqNum;
    int           status;
    struct aiocb *aiocbp;
};

static volatile sig_atomic_t gotSIGQUIT = 0;
                        /* On delivery of SIGQUIT, we attempt to
                           cancel all outstanding I/O requests */

static void             /* Handler for SIGQUIT */
quitHandler(int sig)
{
    gotSIGQUIT = 1;
}

#define IO_SIGNAL SIGUSR1   /* Signal used to notify I/O completion */

static void                 /* Handler for I/O completion signal */
aioSigHandler(int sig, siginfo_t *si, void *ucontext)
{
    if (si->si_code == SI_ASYNCIO) {
        write(STDOUT_FILENO, "I/O completion signal received\n", 31);

        /* The corresponding ioRequest structure would be available as
               struct ioRequest *ioReq = si->si_value.sival_ptr;
           and the file descriptor would then be available via
               ioReq->aiocbp->aio_fildes */
    }
}

int
main(int argc, char *argv[])
{
    struct ioRequest *ioList;
    struct aiocb *aiocbList;
    struct sigaction sa;
    int s, j;
    int numReqs;        /* Total number of queued I/O requests */
    int openReqs;       /* Number of I/O requests still in progress */

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <pathname> <pathname>...\n",
                argv[0]);
        exit(EXIT_FAILURE);
    }

    numReqs = argc - 1;

    /* Allocate our arrays */

    ioList = calloc(numReqs, sizeof(struct ioRequest));
    if (ioList == NULL)
        errExit("calloc");

    aiocbList = calloc(numReqs, sizeof(struct aiocb));
    if (aiocbList == NULL)
        errExit("calloc");

    /* Establish handlers for SIGQUIT and the I/O completion signal */

    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);

    sa.sa_handler = quitHandler;
    if (sigaction(SIGQUIT, &sa, NULL) == -1)
        errExit("sigaction");

    sa.sa_flags = SA_RESTART | SA_SIGINFO;
    sa.sa_sigaction = aioSigHandler;
    if (sigaction(IO_SIGNAL, &sa, NULL) == -1)
        errExit("sigaction");

    /* Open each file specified on the command line, and queue
       a read request on the resulting file descriptor */

    for (j = 0; j < numReqs; j++) {
        ioList[j].reqNum = j;
        ioList[j].status = EINPROGRESS;
        ioList[j].aiocbp = &aiocbList[j];

        ioList[j].aiocbp->aio_fildes = open(argv[j + 1], O_RDONLY);
        if (ioList[j].aiocbp->aio_fildes == -1)
            errExit("open");
        printf("opened %s on descriptor %d\n", argv[j + 1],
                ioList[j].aiocbp->aio_fildes);

        ioList[j].aiocbp->aio_buf = malloc(BUF_SIZE);
        if (ioList[j].aiocbp->aio_buf == NULL)
            errExit("malloc");

        ioList[j].aiocbp->aio_nbytes = BUF_SIZE;
        ioList[j].aiocbp->aio_reqprio = 0;
        ioList[j].aiocbp->aio_offset = 0;
        ioList[j].aiocbp->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
        ioList[j].aiocbp->aio_sigevent.sigev_signo = IO_SIGNAL;
        ioList[j].aiocbp->aio_sigevent.sigev_value.sival_ptr =
                                &ioList[j];

        s = aio_read(ioList[j].aiocbp);
        if (s == -1)
            errExit("aio_read");
    }

    openReqs = numReqs;

    /* Loop, monitoring status of I/O requests */

    while (openReqs > 0) {
        sleep(3);       /* Delay between each monitoring step */

        if (gotSIGQUIT) {

            /* On receipt of SIGQUIT, attempt to cancel each of the
               outstanding I/O requests, and display status returned
               from the cancellation requests */

            printf("got SIGQUIT; canceling I/O requests: \n");

            for (j = 0; j < numReqs; j++) {
                if (ioList[j].status == EINPROGRESS) {
                    printf("    Request %d on descriptor %d:", j,
                            ioList[j].aiocbp->aio_fildes);
                    s = aio_cancel(ioList[j].aiocbp->aio_fildes,
                            ioList[j].aiocbp);
                    if (s == AIO_CANCELED)
                        printf("I/O canceled\n");
                    else if (s == AIO_NOTCANCELED)
                        printf("I/O not canceled\n");
                    else if (s == AIO_ALLDONE)
                        printf("I/O all done\n");
                    else
                        errMsg("aio_cancel");
                }
            }

            gotSIGQUIT = 0;
        }

        /* Check the status of each I/O request that is still
           in progress */

        printf("aio_error():\n");
        for (j = 0; j < numReqs; j++) {
            if (ioList[j].status == EINPROGRESS) {
                printf("    for request %d (descriptor %d): ",
                        j, ioList[j].aiocbp->aio_fildes);
                ioList[j].status = aio_error(ioList[j].aiocbp);

                switch (ioList[j].status) {
                case 0:
                    printf("I/O succeeded\n");
                    break;
                case EINPROGRESS:
                    printf("In progress\n");
                    break;
                case ECANCELED:
                    printf("Canceled\n");
                    break;
                default:
                    errMsg("aio_error");
                    break;
                }

                if (ioList[j].status != EINPROGRESS)
                    openReqs--;
            }
        }
    }

    printf("All I/O requests completed\n");

    /* Check status return of all I/O requests */

    printf("aio_return():\n");
    for (j = 0; j < numReqs; j++) {
        ssize_t s;

        s = aio_return(ioList[j].aiocbp);
        printf("    for request %d (descriptor %d): %zd\n",
                j, ioList[j].aiocbp->aio_fildes, s);
    }

    exit(EXIT_SUCCESS);
}

另外参见

io_cancel(2),io_destroy(2),io_getevents(2),io_setup(2),io_submit(2),aio_cancel(3),aio_error(3),aio_init(3),aio_read(3),aio_return(3), aio_write(3),lio_listio(3)

" Linux 2.5中的异步I / O支持",Bhattacharya,Pratt,Pulavarty和Morgan,Linux研讨会论文集,2003年,

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/