poll(2)

الاسم
poll, ppoll - انتظار حدث ما على واصف ملف

المكتبة
مكتبة سي المعيارية (libc، -lc)

موجز
#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

#define _GNU_SOURCE         /* انظر feature_test_macros(7) */
#include <poll.h>

int ppoll(struct pollfd *fds, nfds_t nfds,
          const struct timespec *_Nullable tmo_p,
          const sigset_t *_Nullable sigmask);

الوصف
تقوم poll() بمهمة مشابهة لـ select(2): فهي تنتظر حتى يصبح أحد مجموعة من واصفات الملفات جاهزًا لأداء الإدخال/الإخراج. تؤدي واجهة برمجة التطبيقات الخاصة بلينكس epoll(7) مهمة مشابهة، ولكنها تقدم ميزات تتجاوز تلك الموجودة في poll().

يتم تحديد مجموعة واصفات الملفات المراد مراقبتها في الوسيط fds، وهو مصفوفة من الهياكل بالشكل التالي:

    struct pollfd {
        int   fd;         /* واصف ملف */
        short events;     /* الأحداث المطلوبة */
        short revents;    /* الأحداث المعادة */
    };

يجب على المستدعي تحديد عدد العناصر في مصفوفة fds في nfds.

يحتوي الحقل fd على واصف ملف لملف مفتوح. إذا كان هذا الحقل سالبًا، فسيتم تجاهل حقل events المقابل ويعيد حقل revents صفرًا. (يوفر هذا طريقة سهلة لتجاهل واصف ملف لاستدعاء poll() واحد: ببساطة اضبط حقل fd على مكمله بالبت.)

الحقل events هو معامل إدخال، قناع بت يحدد الأحداث التي يهتم بها التطبيق لواصف الملف fd. يمكن تحديد هذا الحقل كصفر، وفي هذه الحالة تكون الأحداث الوحيدة التي يمكن إعادتها في revents هي POLLHUP و POLLERR و POLLNVAL (انظر أدناه).

الحقل revents هو معامل إخراج، يملؤه النواة بالأحداث التي حدثت فعليًا. يمكن أن تتضمن البتات المعادة في revents أيًا من تلك المحددة في events، أو إحدى القيم POLLERR أو POLLHUP أو POLLNVAL. (هذه البتات الثلاث لا معنى لها في حقل events، وسيتم تعيينها في حقل revents كلما كان الشرط المقابل صحيحًا.)

إذا لم يحدث أي من الأحداث المطلوبة (ولا خطأ) لأي من واصفات الملفات، فإن poll() تحظر حتى يقع أحد الأحداث.

تحدد الوسيطة timeout عدد الميلي ثانية التي يجب أن تحظر فيها poll() في انتظار أن يصبح واصف الملف جاهزًا. ستحظر الاستدعاء حتى:

•   يصبح واصف الملف جاهزًا؛

•   الاستدعاء قوطع بواسطة معالج إشارة؛ أو

•   انتهاء المهلة.

كونه "جاهزًا" يعني أن العملية المطلوبة لن تحظر؛ وبالتالي، فإن استقصاء poll() للملفات العادية وأجهزة الكتل والملفات الأخرى التي لا تحتوي على دلالات استقصاء معقولة يعود دائمًا فورًا كجاهز للقراءة والكتابة.

لاحظ أن الفاصل الزمني timeout سيتم تقريبه لأعلى إلى دقة ساعة النظام، وتأخيرات جدولة النواة تعني أن فترة الحظر قد تتجاوز بمقدار صغير. تحديد قيمة سالبة في timeout يعني مهلة غير محدودة. تحديد timeout بقيمة صفر يتسبب في عودة poll() فورًا، حتى لو لم تكن أي واصفات ملفات جاهزة.

البتات التي يمكن تعيينها/إعادتها في events و revents معرفة في <poll.h>:

POLLIN
    توجد بيانات للقراءة.

POLLPRI
    توجد حالة استثنائية على واصف الملف. تشمل الاحتمالات:

    •   توجد بيانات خارج النطاق على مقبس TCP (انظر tcp(7)).

    •   رأى رئيس طرفي زائف في وضع الحزمة تغيير حالة على التابع (انظر ioctl_tty(2)).

    •   تم تعديل ملف cgroup.events (انظر cgroups(7)).

POLLOUT
    الكتابة ممكنة الآن، على الرغم من أن كتابة أكبر من المساحة المتاحة في مقبس أو أنبوب ستظل تحظر (ما لم يتم تعيين O_NONBLOCK).

POLLRDHUP (منذ لينكس 2.6.17)
    أغلق نظير مقبس الدفق الاتصال، أو أوقف كتابة نصف الاتصال. يجب تعريف ماكرو اختبار الميزة _GNU_SOURCE (قبل تضمين أي ملفات رأس) للحصول على هذا التعريف.

POLLERR
    حالة خطأ (تُعاد فقط في revents؛ تُتجاهل في events). يتم تعيين هذه البتة أيضًا لواصف ملف يشير إلى نهاية الكتابة لأنبوب عندما يتم إغلاق نهاية القراءة.

POLLHUP
    تعليق (يُعاد فقط في revents؛ يُتجاهل في events). لاحظ أنه عند القراءة من قناة مثل أنبوب أو مقبس دفق، يشير هذا الحدث فقط إلى أن النظير أغلق نهايته من القناة. ستعيد القراءات اللاحقة من القناة 0 (نهاية الملف) فقط بعد استهلاك جميع البيانات المعلقة في القناة.

POLLNVAL
    طلب غير صالح: fd غير مفتوح (يُعاد فقط في revents؛ يُتجاهل في events).

عند الترجمة مع تعريف _XOPEN_SOURCE، تتوفر أيضًا القيم التالية، التي لا تنقل معلومات إضافية تتجاوز البتات المذكورة أعلاه:

POLLRDNORM
    مكافئ لـ POLLIN.

POLLRDBAND
    يمكن قراءة بيانات النطاق ذي الأولوية (غير مستخدمة عمومًا على لينكس).

POLLWRNORM
    مكافئ لـ POLLOUT.

POLLWRBAND
    يمكن كتابة بيانات ذات أولوية.

لينكس يعرف أيضًا POLLMSG، لكنه لا يستخدمه.

  ppoll()
العلاقة بين poll() وppoll() مماثلة للعلاقة بين select(2) وpselect(2): مثل pselect(2)، يسمح ppoll() للتطبيق بالانتظار بأمان حتى يصبح واصف ملف جاهزًا أو حتى يتم التقاط إشارة.

بخلاف الاختلاف في دقة وسيط timeout، فإن استدعاء ppoll() التالي:

    ready = ppoll(&fds, nfds, tmo_p, &sigmask);

يكاد يكون مكافئًا لتنفيذ الاستدعاءات التالية بشكل ذري:

    sigset_t origmask;
    int timeout;

    timeout = (tmo_p == NULL) ? -1 :
              (tmo_p->tv_sec * 1000 + tmo_p->tv_nsec / 1000000);
    pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
    ready = poll(&fds, nfds, timeout);
    pthread_sigmask(SIG_SETMASK, &origmask, NULL);

يُوصف مقطع الكود أعلاه بأنه يكاد يكون مكافئًا لأنه بينما تُفسر قيمة timeout السالبة لـ poll() على أنها مهلة لا نهائية، تؤدي القيمة السالبة المعبر عنها في *tmo_p إلى خطأ من ppoll().

انظر وصف pselect(2) لشرح سبب ضرورة ppoll().

إذا تم تحديد وسيط sigmask كـ NULL، فلا يتم إجراء أي معالجة لقناع الإشارة (وبالتالي يختلف ppoll() عن poll() فقط في دقة وسيط timeout).

يحدد وسيط tmo_p حدًا أعلى لمقدار الوقت الذي سينتظر فيه ppoll() بشكل محظور. هذا الوسيط هو مؤشر لبنية timespec(3).

إذا تم تحديد tmo_p كـ NULL، فيمكن لـ ppoll() الانتظار بشكل محظور إلى أجل غير مسمى.

قيمة الإرجاع
عند النجاح، تُرجع poll() قيمة غير سالبة وهي عدد العناصر في pollfds التي تم تعيين حقول revents فيها إلى قيمة غير صفرية (تشير إلى حدث أو خطأ). تشير قيمة الإرجاع صفر إلى أن استدعاء النظام انتهت مهلة قبل أن يصبح أي واصف ملف جاهزًا.

عند الخطأ، تُعاد القيمة -1، ويُضبط errno للإشارة إلى الخطأ.

الأخطاء

EFAULT
    يشير fds إلى خارج مساحة العنوان القابلة للوصول للعملية. المصفوفة المعطاة كوسيط لم تكن موجودة في مساحة عنوان البرنامج المستدعي.

EINTR
    حدثت إشارة قبل أي حدث مطلوب؛ انظر signal(7).

EINVAL
    قيمة nfds تتجاوز قيمة RLIMIT_NOFILE.

EINVAL
    (ppoll()) قيمة المهلة المعبر عنها في *tmo_p غير صالحة (سالبة).

ENOMEM
    غير قادر على تخصيص ذاكرة لهياكل بيانات النواة.

الإصدارات
على بعض أنظمة UNIX الأخرى، يمكن أن يفشل poll() مع الخطأ EAGAIN إذا فشل النظام في تخصيص موارد داخلية للنواة، بدلاً من ENOMEM كما يفعل لينكس. يسمح POSIX بهذا السلوك. قد ترغب البرامج المحمولة في التحقق من EAGAIN والتكرار، تمامًا كما هو الحال مع EINTR.

تعرف بعض التطبيقات الثابت غير القياسي INFTIM بالقيمة -1 لاستخدامه كـ timeout لـ poll(). هذا الثابت غير متوفر في glibc.

  الاختلافات بين مكتبة C والنواة
استدعاء النظام ppoll() في لينكس يُعدّل وسيطته tmo_p. لكن دالة الغلاف glibc تُخفي هذا السلوك باستخدام متغير محلي لوسيطة المهلة التي تُمرّر لاستدعاء النظام. وبالتالي، دالة ppoll() في glibc لا تُعدّل وسيطتها tmo_p.

استدعاء النظام الخام ppoll() يمتلك وسيطة خامسة، size_t sigsetsize، التي تُحدد الحجم بالبايت لوسيطة sigmask. دالة الغلاف ppoll() في glibc تُحدد هذه الوسيطة بقيمة ثابتة (تساوي sizeof(kernel_sigset_t)). انظر sigprocmask(2) لمناقشة الفروقات بين مفهوم النواة و libc لـ sigset.

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

التاريخ

poll()
    POSIX.1-2001. لينكس 2.1.23.

    على النوى القديمة التي تفتقر لاستدعاء النظام هذا، دالة الغلاف poll() في glibc تُوفّر محاكاة باستخدام select(2).

ppoll()
    POSIX.1-2024. لينكس 2.6.16، glibc 2.4.

ملاحظات
عملية poll() و ppoll() لا تتأثر بالعلامة O_NONBLOCK.

لمناقشة ما قد يحدث إذا أُغلق واصف ملف يُراقَب بواسطة poll() في خيط آخر، انظر select(2).

العلل
انظر مناقشة إشعارات الجاهزية الزائفة تحت قسم BUGS في select(2).

أمثلة
البرنامج أدناه يفتح كل ملف مُسمّى في وسائط سطر الأوامر ويُراقب واصفات الملف الناتجة للجاهزية للقراءة (POLLIN). البرنامج يُكرر، مستخدماً poll() بشكل متكرر لمراقبة واصفات الملف، طابعاً عدد واصفات الملف الجاهزة عند العودة. لكل واصف ملف جاهز، البرنامج:

•   يعرض حقل revents المُعاد بصيغة قابلة للقراءة البشرية؛

•   إذا كان واصف الملف قابلاً للقراءة، يقرأ بعض البيانات منه، ويعرض تلك البيانات على المخرجات القياسية؛ و

•   إذا لم يكن واصف الملف قابلاً للقراءة، لكن حدث آخر وقع (يفترض POLLHUP)، يُغلق واصف الملف.

لنفترض أننا نشغّل البرنامج في طرفية واحدة، نطلب منه فتح FIFO:

    $ mkfifo myfifo;
    $ ./poll_input myfifo;

في نافذة طرفية ثانية، نفتح FIFO للكتابة، نكتب بعض البيانات إليه، ونُغلق FIFO:

    $ echo aaaaabbbbbccccc > myfifo;

في الطرفية حيث نشغّل البرنامج، سنرى بعدها:

    Opened "myfifo" on fd 3
    About to poll()
    Ready: 1
      fd=3; events: POLLIN POLLHUP
        read 10 bytes: aaaaabbbbb
    About to poll()
    Ready: 1
      fd=3; events: POLLIN POLLHUP
        read 6 bytes: ccccc

    About to poll()
    Ready: 1
      fd=3; events: POLLHUP
        closing fd 3
    All file descriptors closed.  Bye.

في المخرجات أعلاه، نرى أن poll() عاد ثلاث مرات:

•   في العودة الأولى، البتات المُعادة في حقل revents كانت POLLIN، مشيرة إلى أن واصف الملف قابل للقراءة، و POLLHUP، مشيرة إلى أن الطرف الآخر من FIFO أُغلق. البرنامج استهلك بعدها بعضاً من المُدخلات المتاحة.

•   العودة الثانية من poll() أشارت أيضًا إلى POLLIN و POLLHUP؛ البرنامج استهلك بعدها آخر المُدخلات المتاحة.

•   في العودة النهائية، poll() أشار فقط إلى POLLHUP على FIFO، وعندها أُغلق واصف الملف وانتهى البرنامج.

  مصدر البرنامج
    /* poll_input.c

       Licensed under GNU General Public License v2 or later.
    */
    #include <err.h>
    #include <fcntl.h>
    #include <poll.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>

    int
    main(int argc, char *argv[])
    {
        int            ready;
        char           buf[10];
        nfds_t         num_open_fds, nfds;
        ssize_t        s;
        struct pollfd  *pfds;

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

        num_open_fds = nfds = argc - 1;
        pfds = calloc(nfds, sizeof(struct pollfd));
        if (pfds == NULL)
            err(EXIT_FAILURE, "malloc");

        /* Open each file on command line, and add it to 'pfds' array.  */

        for (nfds_t j = 0; j < nfds; j++) {
            pfds[j].fd = open(argv[j + 1], O_RDONLY);
            if (pfds[j].fd == -1)
                err(EXIT_FAILURE, "open");

            printf("Opened \"%s\" on fd %d\n", argv[j + 1], pfds[j].fd);

            pfds[j].events = POLLIN;
        }

        /* Keep calling poll() as long as at least one file descriptor is
           open.  */

        while (num_open_fds > 0) {
            printf("About to poll()\n");
            ready = poll(pfds, nfds, -1);
            if (ready == -1)
                err(EXIT_FAILURE, "poll");

            printf("Ready: %d\n", ready);

            /* Deal with array returned by poll().  */

            for (nfds_t j = 0; j < nfds; j++) {
                if (pfds[j].revents != 0) {
                    printf("  fd=%d; events: %s%s%s\n", pfds[j].fd,
                           (pfds[j].revents & POLLIN)  ? "POLLIN "  : "",
                           (pfds[j].revents & POLLHUP) ? "POLLHUP " : "",
                           (pfds[j].revents & POLLERR) ? "POLLERR " : "");

                    if (pfds[j].revents & POLLIN) {
                        s = read(pfds[j].fd, buf, sizeof(buf));
                        if (s == -1)
                            err(EXIT_FAILURE, "read");
                        printf("    read %zd bytes: %.*s\n",
                               s, (int) s, buf);
                    } else {                /* POLLERR | POLLHUP */
                        printf("    closing fd %d\n", pfds[j].fd);
                        if (close(pfds[j].fd) == -1)
                            err(EXIT_FAILURE, "close");
                        num_open_fds--;
                    }
                }
            }
        }

        printf("All file descriptors closed.  Bye.\n");
        exit(EXIT_SUCCESS);
    }

انظر أيضًا
restart_syscall(2), select(2), select_tut(2), timespec(3), epoll(7), time(7)

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

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

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