AIO(7)

الاسم
aio - نظرة عامة على الإدخال/الإخراج غير المتزامن وفق معيار POSIX

الوصف
واجهة الإدخال/الإخراج غير المتزامن (AIO) وفق معيار POSIX تسمح للتطبيقات ببدء عملية إدخال/إخراج واحدة أو أكثر تُنفذ بشكل غير متزامن (أي في الخلفية). يمكن للتطبيق اختيار الإشعار بإتمام عملية الإدخال/الإخراج بطرق متنوعة: عبر تسليم إشارة، أو عبر إنشاء خيط، أو بدون أي إشعار على الإطلاق.

تتكون واجهة POSIX AIO من الدوال التالية:

aio_read(3)
    إدراج طلب قراءة في قائمة الانتظار. هذا هو النظير غير المتزامن لـ read(2).

aio_write(3)
    إدراج طلب كتابة في قائمة الانتظار. هذا هو النظير غير المتزامن لـ write(2).

aio_fsync(3)
    إدراج طلب مزامنة لعمليات الإدخال/الإخراج على واصف ملف. هذا هو النظير غير المتزامن لـ fsync(2) و fdatasync(2).

aio_error(3)
    الحصول على حالة الخطأ لطلب إدخال/إخراج مُدرج في قائمة الانتظار.

aio_return(3)
    الحصول على حالة الإرجاع لطلب إدخال/إخراج مُكتمل.

aio_suspend(3)
    تعليق المستدعي حتى اكتمال طلب إدخال/إخراج واحد أو أكثر من مجموعة محددة.

aio_cancel(3)
    محاولة إلغاء طلبات الإدخال/الإخراج المعلقة على واصف ملف محدد.

lio_listio(3)
    إدراج طلبات إدخال/إخراج متعددة في قائمة الانتظار باستخدام استدعاء دالة واحد.

هيكل aiocb ("كتلة التحكم في الإدخال/الإخراج غير المتزامن") يُعرّف المعاملات التي تتحكم في عملية إدخال/إخراج. يُستخدم وسيط من هذا النوع مع جميع الدوال المذكورة أعلاه. هذا الهيكل له الشكل التالي:

    #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 'aio_lio_opcode': */

    enum { LIO_READ, LIO_WRITE, LIO_NOP };

حقول هذا الهيكل هي كالتالي:

aio_fildes
    واصف الملف الذي سيجري عليه الإدخال/الإخراج.

aio_offset
    هذه هي إزاحة الملف التي ستُجرى عندها عملية الإدخال/الإخراج.

aio_buf
    هذه هي الخبيئة المستخدمة لنقل البيانات لعملية قراءة أو كتابة.

aio_nbytes
    هذا هو حجم الوسيط الذي يشير إليه aio_buf.

aio_reqprio
    يحدد هذا الحقل قيمة تُطرح من أولوية الوقت الفعلي للخيط المستدعي لتحديد أولوية تنفيذ طلب الإدخال/الإخراج هذا (انظر pthread_setschedparam(3)). يجب أن تكون القيمة المحددة بين 0 والقيمة التي يُرجعها sysconf(_SC_AIO_PRIO_DELTA_MAX). يُتجاهل هذا الحقل لعمليات مزامنة الملف.

aio_sigevent
    هذا الحقل هو هيكل يحدد كيفية إشعار المستدعي عند اكتمال عملية الإدخال/الإخراج غير المتزامن. القيم الممكنة لـ aio_sigevent.sigev_notify هي SIGEV_NONE و SIGEV_SIGNAL و SIGEV_THREAD. انظر sigevent(3type) لمزيد من التفاصيل.

aio_lio_opcode
    نوع العملية المراد تنفيذها؛ يُستخدم فقط لـ lio_listio(3).

بالإضافة إلى الدوال القياسية المذكورة أعلاه، توفر مكتبة GNU C الامتداد التالي لواجهة برمجة تطبيقات POSIX AIO:

aio_init(3)
    ضبط المعاملات لضبط سلوك تطبيق glibc POSIX AIO.

الأخطاء

EINVAL
    حقل aio_reqprio في بنية aiocb كان أقل من 0، أو أكبر من الحد الذي أعادته استدعاء sysconf(_SC_AIO_PRIO_DELTA_MAX).

المعايير
POSIX.1-2008.

التاريخ
POSIX.1-2001. glibc 2.1.

ملاحظات
من الجيد تصفير مخزن كتلة التحكم قبل الاستخدام (انظر memset(3)). لا يجب تغيير مخزن كتلة التحكم والمخزن المشار إليه بواسطة aio_buf أثناء تنفيذ عملية الإدخال/الإخراج. يجب أن تظل هذه المخازن صالحة حتى اكتمال عملية الإدخال/الإخراج.

عمليات القراءة أو الكتابة غير المتزامنة المتزامنة باستخدام نفس بنية aiocb تنتج نتائج غير محددة.

تطبيق Linux POSIX AIO الحالي يُقدم في مساحة المستخدم بواسطة glibc. هذا له عدد من القيود، أبرزها أن الحفاظ على خيوط متعددة لتنفيذ عمليات الإدخال/الإخراج مكلف ويواجه صعوبة في التوسع. العمل جارٍ منذ بعض الوقت على تطبيق قائم على آلة حالة النواة للإدخال/الإخراج غير المتزامن (انظر io_submit(2)، io_setup(2)، io_cancel(2)، io_destroy(2)، io_getevents(2))، لكن هذا التطبيق لم ينضج بعد إلى النقطة التي يمكن فيها إعادة تطبيق POSIX AIO بالكامل باستخدام استدعاءات نظام النواة.

أمثلة
البرنامج أدناه يفتح كل ملف مسمى في وسائط سطر الأوامر ويصطف طلبًا على واصف الملف الناتج باستخدام aio_read(3). ثم يتكرر البرنامج، مراقبًا دوريًا كل عملية إدخال/إخراج لا تزال قيد التنفيذ باستخدام aio_error(3). كل طلب إدخال/إخراج يُعد لتوفير إشعار عن طريق تسليم إشارة. بعد اكتمال جميع طلبات الإدخال/الإخراج، يسترجع البرنامج حالتها باستخدام 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

  مصدر البرنامج
    #include <err.h>
    #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 */

    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 sigaction sa;
        int s;
        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.  */

        struct ioRequest *ioList = calloc(numReqs, sizeof(*ioList));
        if (ioList == NULL)
            err(EXIT_FAILURE, "calloc");

        struct aiocb *aiocbList = calloc(numReqs, sizeof(*aiocbList));
        if (aiocbList == NULL)
            err(EXIT_FAILURE, "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)
            err(EXIT_FAILURE, "sigaction");

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

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

        for (size_t 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)
                err(EXIT_FAILURE, "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)
                err(EXIT_FAILURE, "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)
                err(EXIT_FAILURE, "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 cancelation requests.  */

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

                for (size_t j = 0; j < numReqs; j++) {
                    if (ioList[j].status == EINPROGRESS) {
                        printf("    Request %zu 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
                            perror("aio_cancel");
                    }
                }

                gotSIGQUIT = 0;
            }

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

            printf("aio_error():\n");
            for (size_t j = 0; j < numReqs; j++) {
                if (ioList[j].status == EINPROGRESS) {
                    printf("    for request %zu (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:
                        perror("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 (size_t j = 0; j < numReqs; j++) {
            ssize_t s;

            s = aio_return(ioList[j].aiocbp);
            printf("    for request %zu (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)

”دعم عمليات الإدخال/الإخراج غير المتزامنة في لينكس 2.5“، باتشاريا، برات، بولافارتي، ومورغان، وقائع ندوة لينكس، 2003، https://www.kernel.org/doc/ols/2003/ols2003-pages-351-366.pdf

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.