signalfd(2)

الاسم
signalfd - إنشاء واصف ملف لقبول الإشارات

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

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

int signalfd(int fd, const sigset_t *mask, int flags);

الوصف
تنشئ signalfd() واصف ملف يمكن استخدامه لقبول الإشارات الموجهة للمستدعي. يوفر هذا بديلاً لاستخدام معالج إشارة أو sigwaitinfo(2)، وله ميزة إمكانية مراقبة واصف الملف بواسطة select(2) وpoll(2) وepoll(7).

تحدد الوسيطة mask مجموعة الإشارات التي يرغب المستدعي في قبولها عبر واصف الملف. هذه الوسيطة هي مجموعة إشارات يمكن تهيئة محتوياتها باستخدام الكليّات الموصوفة في sigsetops(3). عادةً، يجب حظر مجموعة الإشارات المراد استقبالها عبر واصف الملف باستخدام sigprocmask(2)، لمنع معالجة الإشارات وفقًا لترتيباتها المبدئية. لا يمكن استقبال إشارات SIGKILL أو SIGSTOP عبر واصف ملف signalfd؛ يتم تجاهل هذه الإشارات بصمت إذا تم تحديدها في mask.

إذا كانت الوسيطة fd هي -1، فإن الاستدعاء ينشئ واصف ملف جديدًا ويربط مجموعة الإشارات المحددة في mask بذلك الواصف. إذا لم تكن fd هي -1، فيجب أن تحدد واصف ملف signalfd موجودًا صالحًا، ويتم استخدام mask لاستبدال مجموعة الإشارات المرتبطة بذلك الواصف.

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

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

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

حتى لينكس 2.6.26، معطى flags غير مستخدم، ويجب تحديده كصفر.

ترجع signalfd() واصف ملف يدعم العمليات التالية:

read(2)
    إذا كانت إشارة واحدة أو أكثر من الإشارات المحددة في mask معلقة للعملية، يتم استخدام المخزن المؤقت المقدم إلى read(2) لإرجاع بنية أو أكثر من بنى signalfd_siginfo (انظر أدناه) التي تصف الإشارات. يرجع read(2) معلومات لعدد الإشارات المعلقة والتي تناسب المخزن المؤقت المقدم. يجب أن يكون المخزن المؤقت على الأقل sizeof(struct signalfd_siginfo) بايت. قيمة إرجاع read(2) هي إجمالي عدد البايتات المقروءة.

    نتيجة لـ read(2)، يتم استهلاك الإشارات، بحيث لا تبقى معلقة للعملية (أي لن يتم التقاطها بواسطة معالجات الإشارات، ولا يمكن قبولها باستخدام sigwaitinfo(2)).

    إذا لم تكن أي من الإشارات في mask معلقة للعملية، فإن read(2) إما أن يحظر حتى يتم توليد إشارة من الإشارات في mask للعملية، أو يفشل مع الخطأ EAGAIN إذا تم جعل واصف الملف غير محظور.

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

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

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

  بنية signalfd_siginfo
تنسيق بنية (بنى) signalfd_siginfo التي ترجعها عمليات read(2) من واصف ملف signalfd هو كما يلي:

    struct signalfd_siginfo {
        uint32_t ssi_signo;    /* Signal number */
        int32_t  ssi_errno;    /* Error number (unused) */
        int32_t  ssi_code;     /* Signal code */
        uint32_t ssi_pid;      /* PID of sender */
        uint32_t ssi_uid;      /* Real UID of sender */
        int32_t  ssi_fd;       /* File descriptor (SIGIO) */
        uint32_t ssi_tid;      /* Kernel timer ID (POSIX timers)
        uint32_t ssi_band;     /* Band event (SIGIO) */
        uint32_t ssi_overrun;  /* POSIX timer overrun count */
        uint32_t ssi_trapno;   /* Trap number that caused signal */
        int32_t  ssi_status;   /* Exit status or signal (SIGCHLD) */
        int32_t  ssi_int;      /* Integer sent by sigqueue(3) */
        uint64_t ssi_ptr;      /* Pointer sent by sigqueue(3) */
        uint64_t ssi_utime;    /* User CPU time consumed (SIGCHLD) */
        uint64_t ssi_stime;    /* System CPU time consumed
                                  (SIGCHLD) */
        uint64_t ssi_addr;     /* Address that generated signal
                                  (for hardware-generated signals) */
        uint16_t ssi_addr_lsb; /* Least significant bit of address
                                  (SIGBUS; since Linux 2.6.37) */
        uint8_t  pad[X];       /* Pad size to 128 bytes (allow for
                                  additional fields in the future) */
    };

كل حقل من الحقول في هذه البنية مماثل للحقل المسمى بالمثل في بنية siginfo_t. يتم وصف بنية siginfo_t في sigaction(2). لن تكون جميع الحقول في بنية signalfd_siginfo المرتجعة صالحة لإشارة محددة؛ يمكن تحديد مجموعة الحقول الصالحة من القيمة المرتجعة في حقل ssi_code. هذا الحقل هو مماثل لحقل si_code في siginfo_t؛ انظر sigaction(2) للتفاصيل.

  دلالات fork(2)
بعد fork(2)، يرث الطفل نسخة من واصف ملف signalfd. سترجع عملية read(2) من واصف الملف في الطفل معلومات حول الإشارات المنتظرة للطفل.

  دلالات تمرير واصف الملف
كما هو الحال مع واصفات الملف الأخرى، يمكن تمرير واصفات ملف signalfd إلى عملية أخرى عبر مقبس نطاق UNIX (انظر unix(7)). في العملية المستقبلة، سترجع عملية read(2) من واصف الملف المستلم معلومات حول الإشارات المنتظرة لتلك العملية.

  دلالات execve(2)
تمامًا مثل أي واصف ملف آخر، يظل واصف ملف signalfd مفتوحًا عبر execve(2)، ما لم يتم وضع علامة عليه للإغلاق عند التنفيذ (انظر fcntl(2)). أي إشارات كانت متاحة للقراءة قبل execve(2) تظل متاحة للبرنامج المحمل حديثًا. (هذا مماثل لدلالات الإشارات التقليدية، حيث تظل الإشارة المحظورة المعلقة معلقة عبر execve(2).)

  دلالات الخيوط
دلالات واصفات ملف signalfd في برنامج متعدد الخيوط تعكس الدلالات القياسية للإشارات. بعبارة أخرى، عندما يقرأ خيط من واصف ملف signalfd، سيقرأ الإشارات الموجهة للخيط نفسه والإشارات الموجهة للعملية (أي مجموعة الخيوط بأكملها). (لن يتمكن الخيط من قراءة الإشارات الموجهة لخيوط أخرى في العملية.)

  دلالات epoll(7)
إذا أضافت عملية ما (عبر epoll_ctl(2)) مُعرف ملف signalfd إلى مثيل epoll(7)، فإن epoll_wait(2) تُرجع الأحداث الخاصة فقط بالإشارات المرسلة إلى تلك العملية. وعلى وجه الخصوص، إذا استخدمت العملية بعد ذلك fork(2) لإنشاء عملية فرعية، فستتمكن العملية الفرعية من read(2) الإشارات المرسلة إليها باستخدام واصف الملف signalfd، ولكن epoll_wait(2) لن تشير إلى أن واصف الملف signalfd جاهز. في هذا السيناريو، هناك حل بديل ممكن وهو أنه بعد fork(2)، يمكن للعملية الفرعية إغلاق واصف الملف signalfd الذي ورثته من العملية الأم ثم إنشاء واصف ملف signalfd آخر وإضافته إلى مثيل epoll. بدلاً من ذلك، يمكن للعملية الأم والعملية الفرعية تأخير إنشاء واصفات ملفات signalfd (المنفصلة) الخاصة بهما وإضافتها إلى مثيل epoll حتى ما بعد استدعاء fork(2).

قيمة الإرجاع
في حالة النجاح، تُرجع الدالة signalfd() مُعرف ملف signalfd؛ ويكون هذا إما مُعرف ملف جديد (إذا كان fd يساوي -1)، أو fd نفسه إذا كان fd مُعرف ملف signalfd صالحًا. وفي حالة حدوث خطأ، تُرجع الدالة القيمة -1 ويتم تعيين errno للإشارة إلى الخطأ.

الأخطاء

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

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

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

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

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

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

ENOMEM
    ذاكرة غير كافية لإنشاء واصف ملف signalfd جديد.

الإصدارات

  الاختلافات بين مكتبة C والنواة
استدعاء نظام Linux الأساسي يتطلب وسيطة إضافية، size_t sizemask، التي تحدد حجم وسيطة mask. دالة الغلاف signalfd() في glibc لا تتضمن هذه الوسيطة، لأنها توفر القيمة المطلوبة لاستدعاء النظام الأساسي.

هناك استدعاءان أساسيان لنظام Linux: signalfd() والأحدث signalfd4(). استدعاء النظام الأول لا ينفذ وسيطة flags. استدعاء النظام الثاني ينفذ قيم flags الموصوفة أعلاه. بدءًا من glibc 2.9، ستستخدم دالة الغلاف signalfd() signalfd4() حيثما كان متاحًا.

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

التاريخ

signalfd()
    لينكس 2.6.22، glibc 2.8.

signalfd4()
    لينكس 2.6.27.

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

محاولات تضمين SIGKILL و SIGSTOP في mask يتم تجاهلها بصمت.

يمكن عرض قناع الإشارة المستخدم بواسطة واصف ملف signalfd عبر الإدخال لواصف الملف المقابل في دليل /proc/pid/fdinfo للعملية. راجع proc(5) لمزيد من التفاصيل.

  القيود
لا يمكن استخدام آلية signalfd لاستقبال الإشارات المولدة بشكل متزامن، مثل إشارة SIGSEGV الناتجة عن الوصول إلى عنوان ذاكرة غير صالح أو إشارة SIGFPE الناتجة عن خطأ حسابي. يمكن التقاط هذه الإشارات فقط عبر معالج إشارة.

كما هو موصوف أعلاه، في الاستخدام العادي يقوم المرء بحظر الإشارات التي سيتم قبولها عبر signalfd(). إذا تم إنشاء عملية فرعية لتنفيذ برنامج مساعد (لا يحتاج إلى واصف ملف signalfd)، فبعد استدعاء fork(2)، سترغب عادةً في إلغاء حظر تلك الإشارات قبل استدعاء execve(2)، حتى يتمكن البرنامج المساعد من رؤية أي إشارات يتوقع رؤيتها. ومع ذلك، كن على علم أن هذا لن يكون ممكنًا في حالة برنامج مساعد تم إنشاؤه خلف الكواليس بواسطة أي دالة مكتبة قد يستدعيها البرنامج. في مثل هذه الحالات، يجب اللجوء إلى استخدام معالج إشارة تقليدي يكتب إلى واصف ملف يتم مراقبته بواسطة select(2) أو poll(2) أو epoll(7).

العلل
قبل لينكس 2.6.25، لا يتم ملء حقلي ssi_ptr و ssi_int بالبيانات المصاحبة لإشارة مرسلة بواسطة sigqueue(3).

أمثلة
البرنامج أدناه يقبل الإشارات SIGINT و SIGQUIT عبر واصف ملف signalfd. ينتهي البرنامج بعد قبول إشارة SIGQUIT. جلسة الصدفة التالية توضح استخدام البرنامج:

    $ ./signalfd_demo
    ^C                   # Control-C generates SIGINT
    Got SIGINT
    ^C
    Got SIGINT
    ^\                    # Control-\ generates SIGQUIT
    Got SIGQUIT
    $

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

    int
    main(void)
    {
        int                      sfd;
        ssize_t                  s;
        sigset_t                 mask;
        struct signalfd_siginfo  fdsi;

        sigemptyset(&mask);
        sigaddset(&mask, SIGINT);
        sigaddset(&mask, SIGQUIT);

        /* Block signals so that they aren't handled
           according to their default dispositions.  */

        if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
            err(EXIT_FAILURE, "sigprocmask");

        sfd = signalfd(-1, &mask, 0);
        if (sfd == -1)
            err(EXIT_FAILURE, "signalfd");

        for (;;) {
            s = read(sfd, &fdsi, sizeof(fdsi));
            if (s != sizeof(fdsi))
                err(EXIT_FAILURE, "read");

            if (fdsi.ssi_signo == SIGINT) {
                printf("Got SIGINT\n");
            } else if (fdsi.ssi_signo == SIGQUIT) {
                printf("Got SIGQUIT\n");
                exit(EXIT_SUCCESS);
            } else {
                printf("Read unexpected signal\n");
            }
        }
    }

انظر أيضًا
eventfd(2), poll(2), read(2), select(2), sigaction(2), sigprocmask(2), sigwaitinfo(2), timerfd_create(2), sigsetops(3), sigwait(3), epoll(7), signal(7)

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

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

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