timerfd_create(2)

الاسم
timerfd_create, timerfd_settime, timerfd_gettime - مؤقتات تُعلم عبر واصفات الملفات

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

موجز
#include <sys/timerfd.h>

int timerfd_create(int clockid, int flags);

int timerfd_settime(int fd, int flags,
                    const struct itimerspec *new_value,
                    struct itimerspec *_Nullable old_value);
int timerfd_gettime(int fd, struct itimerspec *curr_value);

الوصف
تنشئ هذه الاستدعاءات النظامية وتُشغّل مؤقتًا يُسلم إشعارات انتهاء صلاحية المؤقت عبر واصف ملف. توفر بديلاً لاستخدام setitimer(2) أو timer_create(2)، مع ميزة إمكانية مراقبة واصف الملف بواسطة select(2) وpoll(2) وepoll(7).

استخدام هذه الاستدعاءات النظامية الثلاثة مماثل لاستخدام timer_create(2) وtimer_settime(2) وtimer_gettime(2). (لا يوجد مماثل لـ timer_getoverrun(2)، لأن تلك الوظيفة تُوفرها read(2)، كما هو موصوف أدناه.)

  timerfd_create()
ينشئ timerfd_create() كائن مؤقت جديد، ويُعيد واصف ملف يشير إلى ذلك المؤقت. تُحدد الوسيطة clockid الساعة المستخدمة لتعليم تقدم المؤقت، ويجب أن تكون واحدة مما يلي:

CLOCK_REALTIME
    ساعة وقت حقيقي قابلة للضبط على مستوى النظام.

CLOCK_MONOTONIC
    ساعة تزايدية رتيبة غير قابلة للضبط تقيس الوقت من نقطة غير محددة في الماضي لا تتغير بعد بدء تشغيل النظام.

CLOCK_BOOTTIME (منذ لينكس 3.15)
    مثل CLOCK_MONOTONIC، هذه ساعة تتزايد برتابة. ومع ذلك، فبينما لا تقيس ساعة CLOCK_MONOTONIC الوقت أثناء تعليق النظام، فإن ساعة CLOCK_BOOTTIME تشمل الوقت الذي يكون فيه النظام معلقاً. هذا مفيد للتطبيقات التي تحتاج أن تكون مدركة للتعليق. ساعة CLOCK_REALTIME ليست مناسبة لمثل هذه التطبيقات، لأن تلك الساعة تتأثر بالتغيرات غير المستمرة في ساعة النظام.

CLOCK_REALTIME_ALARM (منذ لينكس 3.11)
    هذه الساعة تشبه CLOCK_REALTIME، لكنها ستوقظ النظام إذا كان معلقًا. يجب أن يتمتع المستدعِي بصلاحية CAP_WAKE_ALARM لضبط مؤقت لهذه الساعة.

CLOCK_BOOTTIME_ALARM (منذ لينكس 3.11)
    هذه الساعة تشبه CLOCK_BOOTTIME، لكنها ستوقظ النظام إذا كان معلقًا. يجب أن يتمتع المستدعِي بصلاحية CAP_WAKE_ALARM لضبط مؤقت لهذه الساعة.

انظر clock_getres(2) لمزيد من التفاصيل حول الساعات المذكورة أعلاه.

يمكن استرجاع القيمة الحالية لكل من هذه الساعات باستخدام clock_gettime(2).

بدءًا من لينكس 2.6.27، يمكن إجراء عملية OR بتية على القيم التالية في flags لتغيير سلوك timerfd_create():

TFD_NONBLOCK
    اضبط علامة حالة الملف O_NONBLOCK على وصف الملف المفتوح (انظر open(2)) المشار إليه بواسطة واصف الملف الجديد. استخدام هذه العلامة يوفر استدعاءات إضافية لـ fcntl(2) لتحقيق النتيجة نفسها.

TFD_CLOEXEC
    اضبط علامة الإغلاق عند التنفيذ (FD_CLOEXEC) على واصف الملف الجديد. انظر وصف العلامة O_CLOEXEC في open(2) للأسباب التي تجعل هذا مفيدًا.

في إصدارات لينكس حتى 2.6.26 شاملة، يجب تحديد flags كصفر.

  timerfd_settime()
يُسلّح (يبدأ) أو يُنزع سلاح (يوقف) timerfd_settime() المؤقت المشار إليه بواصف الملف fd.

تُحدد الوسيطة new_value الانتهاء الأولي والفاصل الزمني للمؤقت. بنية itimerspec المستخدمة لهذه الوسيطة موصوفة في itimerspec(3type).

يُحدد new_value.it_value الانتهاء الأولي للمؤقت، بالثواني والنانوثانية. ضبط أي من حقلي new_value.it_value على قيمة غير صفرية يُسلّح المؤقت. ضبط كلا الحقلين على صفر يُنزع سلاح المؤقت.

ضبط أحد حقلَي new_value.it_interval أو كليهما على قيم غير صفرية يُحدد الفترة، بالثواني والنانوثانية، لانتهاءات المؤقت المتكررة بعد الانتهاء الأولي. إذا كان كلا الحقلين صفرًا، ينتهي المؤقت مرة واحدة فقط، في الوقت المُحدد بواسطة new_value.it_value.

مبدئيًا، يُفسر وقت الانتهاء الأولي المُحدد في new_value بالنسبة للوقت الحالي على ساعة المؤقت وقت الاستدعاء (أي، يُحدد new_value.it_value وقتًا نسبيًا للقيمة الحالية للساعة المُحددة بواسطة clockid). يمكن اختيار مهلة مطلقة عبر الوسيطة flags.

الوسيطة flags هي قناع بت يمكن أن يتضمن القيم التالية:

TFD_TIMER_ABSTIME
    يفسر new_value.it_value كقيمة مطلقة على ساعة المؤقت. سينتهي المؤقت عندما تصل قيمة ساعة المؤقت إلى القيمة المُحددة في new_value.it_value.

TFD_TIMER_CANCEL_ON_SET
    إذا تم تحديد هذه العلامة مع TFD_TIMER_ABSTIME وكانت ساعة هذا المؤقت هي CLOCK_REALTIME أو CLOCK_REALTIME_ALARM، فسيتم وضع علامة على هذا المؤقت كقابل للإلغاء إذا خضعت الساعة الفعلية لتغيير غير مستمر (settimeofday(2) أو clock_settime(2) أو ما شابه). عند حدوث مثل هذه التغييرات، سيفشل استدعاء read(2) حالي أو مستقبلي من واصف الملف مع الخطأ ECANCELED.

إذا كانت الوسيطة old_value ليست NULL، فسيتم استخدام بنية itimerspec التي تشير إليها لإعادة إعداد المؤقت الذي كان ساريًا وقت الاستدعاء؛ انظر وصف timerfd_gettime() أدناه.

  timerfd_gettime()
يُعيد timerfd_gettime()، في curr_value، بنية itimerspec تحتوي على الإعداد الحالي للمؤقت المشار إليه بواصف الملف fd.

يُرجع حقل it_value مقدار الوقت المتبقي حتى انتهاء المؤقت التالي. إذا كان كلا حقلي هذه البنية صفرًا، فإن المؤقت غير مسلح حاليًا. يحتوي هذا الحقل دائمًا على قيمة نسبية، بغض النظر عما إذا كانت العلامة TFD_TIMER_ABSTIME قد حُددت عند ضبط المؤقت.

يُرجع حقل it_interval الفاصل الزمني للمؤقت. إذا كان كلا حقلي هذه البنية صفرًا، فإن المؤقت مضبوط لينتهي مرة واحدة فقط، في الوقت المحدد بواسطة curr_value.it_value.

  التشغيل على واصف ملف المؤقت
يدعم واصف الملف الذي يُرجعه timerfd_create() العمليات الإضافية التالية:

read(2)
    إذا كان المؤقت قد انتهى مرة أو أكثر منذ آخر تعديل لإعداداته باستخدام timerfd_settime()، أو منذ آخر read(2) ناجح، فإن المخزن المؤقت المُعطى لـ read(2) يُرجع عددًا صحيحًا غير مُوقع بطول 8 بايت (uint64_t) يحتوي على عدد مرات الانتهاء التي حدثت. (القيمة المُرجعة تكون بترتيب بايت المضيف—أي ترتيب البايت الأصلي للأعداد الصحيحة على جهاز المضيف.)

    إذا لم تحدث أي انتهاءات للمؤقت في وقت read(2)، فإن الاستدعاء إما أن يحظر حتى انتهاء المؤقت التالي، أو يفشل مع الخطأ EAGAIN إذا جُعل واصف الملف غير محظور (باستخدام عملية fcntl(2) F_SETFL لتعيين العلامة O_NONBLOCK).

    يفشل read(2) مع الخطأ EINVAL إذا كان حجم المخزن المؤقت المُقدم أقل من 8 بايت.

    إذا كانت الساعة المرتبطة إما CLOCK_REALTIME أو CLOCK_REALTIME_ALARM، وكان المؤقت مطلقًا (TFD_TIMER_ABSTIME)، وحددت العلامة TFD_TIMER_CANCEL_ON_SET عند استدعاء timerfd_settime()، فإن read(2) يفشل مع الخطأ ECANCELED إذا خضعت ساعة الوقت الحقيقي لتغيير غير مستمر. (يسمح هذا للتطبيق القارئ باكتشاف مثل هذه التغييرات غير المستمرة في الساعة.)

    إذا كانت الساعة المرتبطة إما CLOCK_REALTIME أو CLOCK_REALTIME_ALARM، وكان المؤقت مطلقًا (TFD_TIMER_ABSTIME)، ولم تحدد العلامة TFD_TIMER_CANCEL_ON_SET عند استدعاء timerfd_settime()، فإن تغييرًا سلبيًا غير مستمر في الساعة (مثل clock_settime(2)) قد يتسبب في إلغاء حظر read(2)، ولكن يُرجع قيمة 0 (أي عدم قراءة بايتات)، إذا حدث تغيير الساعة بعد انتهاء الوقت، ولكن قبل read(2) على واصف الملف.

poll(2)
select(2)
(وما شابه ذلك)
    واصف الملف قابل للقراءة (وسيطة select(2) readfds؛ علامة poll(2) POLLIN) إذا حدث انتهاء مؤقت واحد أو أكثر.

    يدعم واصف الملف أيضًا واجهات برمجة التطبيقات الأخرى لتعدد إرسال واصفات الملفات: pselect(2)، ppoll(2)، و epoll(7).

ioctl(2)
    الأمر التالي الخاص بـ timerfd مدعوم:

    TFD_IOC_SET_TICKS (منذ لينكس 3.17)
        يضبط عدد مرات انتهاء المؤقت التي حدثت. الوسيطة هي مؤشر لعدد صحيح غير صفري بطول 8 بايت (uint64_t*) يحتوي على العدد الجديد من مرات الانتهاء. بمجرد تعيين العدد، يُوقظ أي منتظر على المؤقت. الغرض الوحيد من هذا الأمر هو استعادة مرات الانتهاء لغرض نقطة التفتيش/الاستعادة. هذه العملية متاحة فقط إذا كان النواة مُهيأة بخيار CONFIG_CHECKPOINT_RESTORE.

close(2)
    عندما لا يعود واصف الملف مطلوبًا، يجب إغلاقه. عندما تُغلق جميع واصفات الملفات المرتبطة بنفس كائن المؤقت، يُسلح المؤقت وتُحرر موارده بواسطة النواة.

  دلالات fork(2)
بعد fork(2)، يرث الطفل نسخة من واصف الملف الذي أنشأه timerfd_create(). يشير واصف الملف إلى نفس كائن المؤقت الأساسي مثل واصف الملف المقابل في الأب، وستُرجع عمليات read(2) في الطفل معلومات حول انتهاءات المؤقت.

  دلالات execve(2)
يُحافظ على واصف الملف الذي أنشأه timerfd_create() عبر execve(2)، ويستمر في توليد انتهاءات المؤقت إذا كان المؤقت مسلحًا.

قيمة الإرجاع
عند النجاح، يُرجع timerfd_create() واصف ملف جديد. عند الخطأ، يُرجع -1 ويُعين errno للإشارة إلى الخطأ.

يُرجع timerfd_settime() و timerfd_gettime() 0 عند النجاح؛ عند الخطأ يُرجعان -1، ويُعينان errno للإشارة إلى الخطأ.

الأخطاء
يمكن أن يفشل timerfd_create() مع الأخطاء التالية:

EINVAL
    clockid غير صالح.

EINVAL
    flags غير صالح؛ أو، في لينكس 2.6.26 أو أقدم، flags ليس صفرًا.

EMFILE
    وُصل إلى الحد الأقصى لواصفات الملفات المفتوحة لكل عملية.

ENFILE
    وُصل إلى الحد الأقصى لإجمالي عدد الملفات المفتوحة على مستوى النظام.

ENODEV
    تعذر وصل جهاز inode (داخلي) مجهول.

ENOMEM
    ذاكرة نواة غير كافية لإنشاء المؤقت.

EPERM
    كان clockid هو CLOCK_REALTIME_ALARM أو CLOCK_BOOTTIME_ALARM ولكن المستدعي لم يمتلك قدرة CAP_WAKE_ALARM.

يمكن أن يفشل timerfd_settime() و timerfd_gettime() مع الأخطاء التالية:

EBADF
    fd ليس واصف ملف صالح.

EFAULT
    new_value، أو old_value، أو curr_value ليس مؤشراً صالحاً.

EINVAL
    fd ليس واصف ملف timerfd صالحًا.

يمكن أن يفشل timerfd_settime() أيضًا مع الأخطاء التالية:

ECANCELED
    انظر الملاحظات.

EINVAL
    new_value غير مهيأ بشكل صحيح (أحد حقول tv_nsec يقع خارج النطاق من صفر إلى 999,999,999).

EINVAL
    flags غير صالحة.

المعايير
لينكس.

التاريخ
لينكس 2.6.25، glibc 2.8.

ملاحظات
افترض السيناريو التالي لمؤقت CLOCK_REALTIME أو CLOCK_REALTIME_ALARM الذي أُنشئ باستخدام timerfd_create():

(1) بدأ المؤقت (timerfd_settime()) بالعلامتين TFD_TIMER_ABSTIME و TFD_TIMER_CANCEL_ON_SET؛

(2) يُجرى تغيير غير مستمر (مثل settimeofday(2)) لاحقًا على ساعة CLOCK_REALTIME؛ و

(3) يستدعي المتصل مرة أخرى timerfd_settime() لإعادة تسليح المؤقت (دون إجراء read(2) أولاً على واصف الملف).

في هذه الحالة، يحدث ما يلي:

•   تُرجع timerfd_settime() -1 مع تعيين errno إلى ECANCELED. (يُمكّن هذا المتصل من معرفة أن المؤقت السابق تأثر بتغيير غير مستمر في الساعة.)

•   أُعيد تنشيط المؤقت بنجاح باستخدام الإعدادات المُقدمة في استدعاء الدالة timerfd_settime() الثاني. (ربما كان هذا خطأً في التنفيذ، ولكن لن يتم إصلاحه الآن، تحسبًا لوجود تطبيقات تعتمد على هذا السلوك).

العلل
حاليًا، يدعم timerfd_create() أنواعًا أقل من معرفات الساعة مقارنة بـ timer_create(2).

أمثلة
ينشئ البرنامج التالي مؤقتًا ثم يراقب تقدمه. يقبل البرنامج ما يصل إلى ثلاث وسائط لسطر الأوامر. تحدد الوسيطة الأولى عدد الثواني للانتهاء الأولي للمؤقت. تحدد الوسيطة الثانية الفاصل الزمني للمؤقت، بالثواني. تحدد الوسيطة الثالثة عدد المرات التي يجب أن يسمح فيها البرنامج بانتهاء المؤقت قبل الإنهاء. الوسيطتان الثانية والثالثة لسطر الأوامر اختياريتان.

توضح جلسة الشل التالية استخدام البرنامج:

    $ a.out 3 1 100
    0.000:	timer started
    3.000:	read: 1; total=1
    4.000:	read: 1; total=2
    ^Z                  # type control-Z to suspend the program
    [1]+  Stopped                 ./timerfd3_demo 3 1 100
    $ fg                # Resume execution after a few seconds
    a.out 3 1 100
    9.660:	read: 5; total=7
    10.000:	read: 1; total=8
    11.000:	read: 1; total=9
    ^C                  # type control-C to suspend the program

  مصدر البرنامج
    #include <err.h>
    #include <inttypes.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/timerfd.h>
    #include <sys/types.h>
    #include <time.h>
    #include <unistd.h>

    static void
    print_elapsed_time(void)
    {
        int                     secs, nsecs;
        static int              first_call = 1;
        struct timespec         curr;
        static struct timespec  start;

        if (first_call) {
            first_call = 0;
            if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
                err(EXIT_FAILURE, "clock_gettime");
        }

        if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
            err(EXIT_FAILURE, "clock_gettime");

        secs = curr.tv_sec - start.tv_sec;
        nsecs = curr.tv_nsec - start.tv_nsec;
        if (nsecs < 0) {
            secs--;
            nsecs += 1000000000;
        }
        printf("%d.%03d:\t", secs, (nsecs + 500000) / 1000000);
    }

    int
    main(int argc, char *argv[])
    {
        int                fd;
        ssize_t            s;
        uint64_t           expir, tot_expir, max_expir;
        struct timespec    now;
        struct itimerspec  new_value;

        if (argc != 2 && argc != 4) {
            fprintf(stderr, "%s init-secs [interval-secs max-num-expir]\n",
                    argv[0]);
            exit(EXIT_FAILURE);
        }

        if (clock_gettime(CLOCK_REALTIME, &now) == -1)
            err(EXIT_FAILURE, "clock_gettime");

        /* Create a CLOCK_REALTIME absolute timer with initial
           expiration and interval as specified in command line.  */

        new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
        new_value.it_value.tv_nsec = now.tv_nsec;
        if (argc == 2) {
            new_value.it_interval.tv_sec = 0;
            max_expir = 1;
        } else {
            new_value.it_interval.tv_sec = atoi(argv[2]);
            max_expir = atoi(argv[3]);
        }
        new_value.it_interval.tv_nsec = 0;

        fd = timerfd_create(CLOCK_REALTIME, 0);
        if (fd == -1)
            err(EXIT_FAILURE, "timerfd_create");

        if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
            err(EXIT_FAILURE, "timerfd_settime");

        print_elapsed_time();
        printf("timer started\n");

        for (tot_expir = 0; tot_expir < max_expir;) {
            s = read(fd, &expir, sizeof(uint64_t));
            if (s != sizeof(uint64_t))
                err(EXIT_FAILURE, "read");

            tot_expir += expir;
            print_elapsed_time();
            printf("read: %w64u; total=%w64u\n", expir, tot_expir);
        }

        exit(EXIT_SUCCESS);
    }

انظر أيضًا
eventfd(2), poll(2), read(2), select(2), setitimer(2), signalfd(2), timer_create(2), timer_gettime(2), timer_settime(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.