wait(2)

الاسم
wait، waitpid، waitid - انتظار تغيير حالة العملية

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

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

pid_t wait(int *_Nullable wstatus);
pid_t waitpid(pid_t pid, int *_Nullable wstatus, int options);

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
                /* This is the glibc and POSIX interface; see
                   VERSIONS for information on the raw system call.  */

    متطلبات ماكروات اختبار الميزات لـ glibc (انظر feature_test_macros(7)):

waitid():
    Since glibc 2.26:
        _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
    glibc 2.25 and earlier:
        _XOPEN_SOURCE
            || /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200809L
            || /* glibc <= 2.19: */ _BSD_SOURCE

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

إذا كانت عملية تابعة قد غيرت حالتها بالفعل، فإن هذه الاستدعاءات تعود فورًا. وإلا، فإنها تمنع التنفيذ حتى تغير عملية تابعة حالتها أو يقاطع معالج إشارة الاستدعاء (بافتراض أن استدعاءات النظام لا يُعاد تشغيلها آليًا باستخدام علم SA_RESTART الخاص بـ sigaction(2)). في بقية هذه الصفحة، يُطلق على العملية التابعة التي تغيرت حالتها ولم يتم انتظارها بعد بواسطة أحد استدعاءات النظام هذه مصطلح قابلة للانتظار waitable.

  wait() و waitpid()
يعلق استدعاء النظام wait() تنفيذ سلسلة التعليمات المستدعية حتى تنتهي إحدى عملياتها التابعة. الاستدعاء wait(&wstatus) مكافئ لـ:

    waitpid(-1, &wstatus, 0);

يعلق استدعاء النظام waitpid() تنفيذ سلسلة التعليمات المستدعية حتى تغير عملية تابعة محددة بواسطة وسيطة pid حالتها. بشكل مبدئي، ينتظر waitpid() العمليات التابعة المنتهية فقط، ولكن يمكن تعديل هذا السلوك عبر وسيطة options، كما هو موضح أدناه.

يمكن أن تكون قيمة pid:

< -1
    بمعنى انتظار أي عملية تابعة يكون معرف مجموعة العمليات الخاص بها مساويًا للقيمة المطلقة لـ pid.

-1  بمعنى انتظار أي عملية تابعة.

0   بمعنى انتظار أي عملية تابعة يكون معرف مجموعة العمليات الخاص بها مساويًا لمعرف مجموعة عمليات عملية الاستدعاء في وقت استدعاء waitpid().

> 0 بمعنى انتظار العملية التابعة التي يكون معرف العملية الخاص بها مساويًا لقيمة pid.

قيمة options هي عملية OR لصفر أو أكثر من الثوابت التالية:

WNOHANG
    العودة فورًا إذا لم تخرج أي عملية تابعة.

WUNTRACED
    العودة أيضًا إذا توقفت عملية تابعة (ولكن لم يتم تتبعها عبر ptrace(2)). يتم توفير الحالة للعمليات التابعة المتتبعة التي توقفت حتى إذا لم يتم تحديد هذا الخيار.

WCONTINUED (منذ Linux 2.6.10)
    العودة أيضًا إذا تم استئناف عملية تابعة متوقفة عن طريق تسليم SIGCONT.

(للخيارات الخاصة بـ Linux فقط، انظر أدناه.)

إذا لم يكن wstatus NULL، يقوم wait() و waitpid() بتخزين معلومات الحالة في int الذي يشير إليه. يمكن فحص هذا العدد الصحيح باستخدام وحدات الماكرو التالية (التي تأخذ العدد الصحيح نفسه كوسيطة، وليس مؤشرًا إليه، كما هو الحال في wait() و waitpid()!):

WIFEXITED(wstatus)
    يعيد true إذا انتهت العملية التابعة بشكل طبيعي، أي عن طريق استدعاء exit(3) أو _exit(2)، أو عن طريق العودة من main().

WEXITSTATUS(wstatus)
    يعيد حالة الخروج للعملية التابعة. يتكون هذا من أقل 8 بتات دلالة من وسيطة status التي حددتها العملية التابعة في استدعاء لـ exit(3) أو _exit(2) أو كوسيطة لعبارة return في main(). يجب استخدام وحدة الماكرو هذه فقط إذا أعادت WIFEXITED قيمة true.

WIFSIGNALED(wstatus)
    ترجع قيمة صحيحة إذا تم إنهاء العملية التابعة بواسطة إشارة.

WTERMSIG(wstatus)
    ترجع رقم الإشارة التي تسببت في إنهاء العملية التابعة. يجب استخدام هذه الكلية فقط إذا أعادت WIFSIGNALED قيمة صحيحة.

WCOREDUMP(wstatus)  // POSIX.1-2024
    ترجع قيمة صحيحة إذا أنتجت العملية التابعة تفريغًا أساسيًا (انظر core(5)). يجب استخدام هذه الكلية فقط إذا أعادت WIFSIGNALED قيمة صحيحة.

WIFSTOPPED(wstatus)  // POSIX.1-2024
    ترجع قيمة صحيحة إذا تم إيقاف العملية التابعة بواسطة إشارة؛ هذا ممكن فقط إذا تم الاستدعاء باستخدام WUNTRACED أو عندما تكون التابعة قيد التتبع (انظر ptrace(2)).

WSTOPSIG(wstatus)
    ترجع رقم الإشارة التي تسببت في إيقاف التابعة. يجب استخدام هذه الكلية فقط إذا أعادت WIFSTOPPED قيمة صحيحة.

WIFCONTINUED(wstatus)
    (منذ لينكس 2.6.10) ترجع قيمة صحيحة إذا تم استئناف العملية التابعة بواسطة إشارة SIGCONT.

  waitid()
توفّر استدعاء النظام waitid() (المتوفر منذ لينكس 2.6.9) تحكمًا أكثر دقة في تغييرات حالة التابعة التي تنتظرها.

تحدد الوسيطتان idtype و id التابعة (أو التوابع) التي تنتظرها، كما يلي:

idtype == P_PID
    انتظر التابعة التي يتطابق معرف عمليتها مع id.

idtype == P_PIDFD (منذ لينكس 5.4)
    انتظر التابعة المشار إليها بواصف ملف PID المحدد في id. (انظر pidfd_open(2) لمزيد من المعلومات حول واصفات ملفات PID.)

idtype == P_PGID
    انتظر أي تابعة يتطابق معرف مجموعة عمليتها مع id. منذ لينكس 5.4، إذا كان id صفرًا، فانتظر أي تابعة في نفس مجموعة العملية كمجموعة عملية المستدعي وقت الاستدعاء.

idtype == P_ALL
    انتظر أي تابعة؛ يتم تجاهل id.

يتم تحديد تغييرات حالة التابعة التي تنتظرها عن طريق إجراء عملية OR لواحد أو أكثر من الأعلام التالية في options:

WEXITED
    انتظر التوابع التي تم إنهاؤها.

WSTOPPED
    انتظر التوابع التي تم إيقافها بواسطة إشارة.

WCONTINUED
    انتظر التوابع (الموقوفة سابقًا) التي تم استئنافها بواسطة إشارة SIGCONT.

يمكن أيضًا إجراء عملية OR للأعلام التالية في options:

WNOHANG
    كما هو الحال مع waitpid().

WNOWAIT
    اترك التابعة في حالة قابلة للانتظار؛ يمكن استخدام استدعاء انتظار لاحق لاسترجاع معلومات حالة التابعة مرة أخرى.

عند الإرجاع الناجح، تملأ waitid() الحقول التالية من بنية siginfo_t المشار إليها بواسطة infop:

si_pid
    معرف العملية (PID) للطفل.

si_uid
    معرف المستخدم الحقيقي للطفل. (لا يُضبط هذا الحقل في معظم التطبيقات الأخرى.)

si_signo
    يُضبط دائمًا على SIGCHLD.

si_status
    إما حالة الخروج للطفل، كما أعطيت لـ _exit(2) (أو exit(3))، أو الإشارة التي تسببت في إنهاء الطفل أو إيقافه أو استمراره. يمكن استخدام حقل si_code لتحديد كيفية تفسير هذا الحقل.

si_code
    يُضبط على واحد من: CLD_EXITED (استدعى الطفل _exit(2))؛ CLD_KILLED (قُتل الطفل بإشارة)؛ CLD_DUMPED (قُتل الطفل بإشارة، وأفرغ النواة)؛ CLD_STOPPED (أُوقف الطفل بإشارة)؛ CLD_TRAPPED (الطفل المتعقب قد حوصر)؛ أو CLD_CONTINUED (استمر الطفل بواسطة SIGCONT).

إذا تم تحديد WNOHANG في options ولم يكن هناك أطفال في حالة قابلة للانتظار، فإن waitid() تُرجع 0 فورًا وتعتمد حالة بنية siginfo_t المشار إليها بواسطة infop على التطبيق. للتمييز (بشكل محمول) بين هذه الحالة والحالة التي كان فيها طفل في حالة قابلة للانتظار، امسح حقل si_pid قبل الاستدعاء وتحقق من قيمة غير صفرية في هذا الحقل بعد عودة الاستدعاء.

يضيف التصحيح الفني POSIX.1-2008 رقم 1 (2013) الشرط أنه عند تحديد WNOHANG في options ولم يكن هناك أطفال في حالة قابلة للانتظار، يجب على waitid() مسح حقلي si_pid و si_signo من البنية. في لينكس والتطبيقات الأخرى التي تلتزم بهذا الشرط، ليس من الضروري مسح حقل si_pid قبل استدعاء waitid(). ومع ذلك، لا تتبع جميع التطبيقات مواصفات POSIX.1 في هذه النقطة.

قيمة الإرجاع
wait(): عند النجاح، تُرجع معرف العملية للطفل المنتهي؛ عند الفشل، تُرجع -1.

waitpid(): عند النجاح، تُرجع معرف العملية للطفل الذي تغيرت حالته؛ إذا تم تحديد WNOHANG وكان طفل أو أكثر محدد بواسطة pid موجودًا، لكنه لم يغير حالته بعد، فتُرجع 0. عند الفشل، تُرجع -1.

waitid(): تُرجع 0 عند النجاح أو إذا تم تحديد WNOHANG ولم يغير أي طفل محدد بواسطة id حالته بعد؛ عند الفشل، تُرجع -1.

عند الفشل، يضبط كل من هذه الاستدعاءات errno للإشارة إلى الخطأ.

الأخطاء

EAGAIN
    وصف ملف PID المحدد في id غير محظور والعملية التي يشير إليها لم تنته.

ECHILD
    (لـ wait()) لا تحتوي العملية المستدعية على أي أطفال غير منتظرين.

ECHILD
    (لـ waitpid() أو waitid()) العملية المحددة بواسطة pid (waitpid()) أو idtype و id (waitid()) غير موجودة أو ليست ابنة للعملية المستدعية. (يمكن أن يحدث هذا لابنة المرء إذا تم تعيين الإجراء لـ SIGCHLD إلى SIG_IGN. انظر أيضًا قسم ملاحظات لينكس حول الخيوط.)

EINTR
    لم يُضبط WNOHANG والتقطت إشارة غير محظورة أو SIGCHLD؛ انظر signal(7).

EINVAL
    وسيطة options غير صالحة.

ESRCH
    (لـ wait() أو waitpid()) pid يساوي INT_MIN.

الإصدارات

  الاختلافات بين مكتبة C والنواة
wait() هي في الواقع دالة مكتبة (في glibc) تُنفذ كاستدعاء لـ wait4(2).

في بعض البنى، لا يوجد استدعاء نظام waitpid()؛ بدلاً من ذلك، تُنفذ هذه الواجهة عبر دالة غلاف لمكتبة C تستدعي wait4(2).

يأخذ استدعاء النظام الخام waitid() وسيطة خامسة، من النوع struct rusage *. إذا كانت هذه الوسيطة غير NULL، فتُستخدم لإرجاع معلومات استخدام الموارد عن الولد، بنفس طريقة wait4(2). انظر getrusage(2) للتفاصيل.

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

التاريخ
SVr4، 4.3BSD، POSIX.1-2001.

ملاحظات
الولد الذي ينتهي، ولكن لم يُنتظر له، يصبح "زومبي". تحتفظ النواة بمجموعة دنيا من المعلومات حول عملية الزومبي (PID، حالة الإنهاء، معلومات استخدام الموارد) للسماح للأب لاحقًا بإجراء انتظار للحصول على معلومات عن الولد. طالما لم يُزل الزومبي من النظام عبر انتظار، فسيستهلك خانة في جدول عمليات النواة، وإذا امتلأ هذا الجدول، فلن يكون من الممكن إنشاء عمليات أخرى. إذا انتهت عملية أب، فإن أولادها "الزومبي" (إن وجدوا) يُتبنون بواسطة init(1)، (أو بواسطة أقرب عملية "subreaper" كما هو محدد باستخدام عملية prctl(2) PR_SET_CHILD_SUBREAPER)؛ ينفذ init(1) آليًا انتظارًا لإزالة الزومبي.

تحدد POSIX.1-2001 أنه إذا ضُبط تصرف SIGCHLD على SIG_IGN أو ضُبط العلم SA_NOCLDWAIT لـ SIGCHLD (انظر sigaction(2))، فإن الأولاد الذين ينتهون لا يصبحون زومبي وسيحظر استدعاء wait() أو waitpid() حتى ينتهي جميع الأولاد، ثم يفشل مع ضبط errno على ECHILD. (ترك معيار POSIX الأصلي سلوك ضبط SIGCHLD على SIG_IGN غير محدد. لاحظ أنه على الرغم من أن التصرف المبدئي لـ SIGCHLD هو "تجاهل"، فإن ضبط التصرف صراحةً على SIG_IGN يؤدي إلى معالجة مختلفة لأولاد عملية الزومبي.)

يتوافق لينكس 2.6 مع متطلبات POSIX. ومع ذلك، لا يفعل لينكس 2.4 (والإصدارات الأقدم) ذلك: إذا أُجري استدعاء wait() أو waitpid() أثناء تجاهل SIGCHLD، يتصرف الاستدعاء تمامًا كما لو لم يُتجاهل SIGCHLD، أي أن الاستدعاء يحظر حتى ينتهي الولد التالي ثم يُرجع معرف العملية وحالة ذلك الولد.

  ملاحظات لينكس
في نواة لينكس، الخيط المجدول بواسطة النواة ليس بناءً متميزًا عن العملية. بدلاً من ذلك، الخيط هو ببساطة عملية تُنشأ باستخدام استدعاء النظام الفريد للينكس clone(2)؛ تُنفذ الإجراءات الأخرى مثل الاستدعاء المحمول pthread_create(3) باستخدام clone(2). قبل لينكس 2.4، كان الخيط مجرد حالة خاصة من العملية، وكنتيجة لذلك، لم يستطع خيط واحد الانتظار على أولاد خيط آخر، حتى عندما ينتمي الأخير إلى نفس مجموعة الخيوط. ومع ذلك، يصف POSIX هذه الوظيفة، ومنذ لينكس 2.4 يمكن للخيط، وسينتظر مبدئيًا، على أولاد خيوط أخرى في نفس مجموعة الخيوط.

options التالية الخاصة بلينكس مخصصة للاستخدام مع الأولاد المنشئين باستخدام clone(2)؛ يمكن أيضًا، منذ لينكس 4.7، استخدامها مع waitid():

__WCLONE
    انتظر أولاد "clone" فقط. إذا حُذف، فانتظر أولاد "non-clone" فقط. (الولد "clone" هو الذي لا يُسلم إشارة، أو إشارة غير SIGCHLD إلى والده عند الإنهاء.) يُتجاهل هذا الخيار إذا حُدد __WALL أيضًا.

__WALL (منذ لينكس 2.4)
    انتظر جميع العمليات الابنة، بغض النظر عن النوع (مستنسخة أو غير مستنسخة).

__WNOTHREAD (منذ لينكس 2.4)
    لا تنتظر عمليات ابنة لخيوط أخرى في نفس مجموعة الخيوط. كان هذا هو المبدئي قبل لينكس 2.4.

منذ لينكس 4.7، يُفترض آليًا العلم __WALL إذا كانت العملية الابنة قيد التتبع (ptraced).

العلل
وفقًا لـ POSIX.1-2008، يجب على التطبيق الذي يستدعي waitid() التأكد من أن infop يشير إلى بنية siginfo_t (أي أنه مؤشر غير فارغ). على لينكس، إذا كان infop فارغًا (NULL)، فإن waitid() ينجح ويعيد معرف العملية للعملية الابنة المنتظرة. يجب على التطبيقات تجنب الاعتماد على هذه الميزة غير المتسقة وغير القياسية وغير الضرورية.

أمثلة
يوضح البرنامج التالي استخدام fork(2) و waitpid(). ينشئ البرنامج عملية ابنة. إذا لم يُوفر وسيط سطر أوامر للبرنامج، فإن العملية الابنة تعلق تنفيذها باستخدام pause(2)، للسماح للمستخدم بإرسال إشارات إلى الابنة. خلاف ذلك، إذا وُفر وسيط سطر أوامر، فإن العملية الابنة تخرج فورًا، باستخدام العدد الصحيح المقدم في سطر الأوامر كحالة خروج. تنفذ العملية الأم حلقة تراقب الابنة باستخدام waitpid()، وتستخدم وحدات الماكرو W*() الموصوفة أعلاه لتحليل قيمة حالة الانتظار.

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

    $ ./a.out &
    Child PID is 32360
    [1] 32359
    $ kill -STOP 32360
    stopped by signal 19
    $ kill -CONT 32360
    continued
    $ kill -TERM 32360
    killed by signal 15
    [1]+  Done                    ./a.out
    $

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

    int
    main(int argc, char *argv[])
    {
        int    wstatus;
        pid_t  cpid, w;

        cpid = fork();
        if (cpid == -1) {
            perror("fork");
            exit(EXIT_FAILURE);
        }

        if (cpid == 0) {            /* Code executed by child */
            printf("Child PID is %jd\n", (intmax_t) getpid());
            if (argc == 1)
                pause();                    /* Wait for signals */
            _exit(atoi(argv[1]));

        } else {                    /* Code executed by parent */
            do {
                w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
                if (w == -1) {
                    perror("waitpid");
                    exit(EXIT_FAILURE);
                }

                if (WIFEXITED(wstatus)) {
                    printf("exited, status=%d\n", WEXITSTATUS(wstatus));
                } else if (WIFSIGNALED(wstatus)) {
                    printf("killed by signal %d\n", WTERMSIG(wstatus));
                } else if (WIFSTOPPED(wstatus)) {
                    printf("stopped by signal %d\n", WSTOPSIG(wstatus));
                } else if (WIFCONTINUED(wstatus)) {
                    printf("continued\n");
                }
            } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
            exit(EXIT_SUCCESS);
        }
    }

انظر أيضًا
_exit(2), clone(2), fork(2), kill(2), ptrace(2), sigaction(2), signal(2), wait4(2), pthread_create(3), core(5), credentials(7), signal(7)

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

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

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