posix_spawn(3)

الاسم
posix_spawn, posix_spawnp - إنجاب عملية

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

موجز
#include <spawn.h>

int posix_spawn(pid_t *restrict pid, const char *restrict path,
                const posix_spawn_file_actions_t *restrict file_actions,
                const posix_spawnattr_t *restrict attrp,
                char *const argv[restrict],
                char *const envp[restrict]);
int posix_spawnp(pid_t *restrict pid, const char *restrict file,
                const posix_spawn_file_actions_t *restrict file_actions,
                const posix_spawnattr_t *restrict attrp,
                char *const argv[restrict],
                char *const envp[restrict]);

الوصف
تُستخدم الدالتان posix_spawn() وposix_spawnp() لإنشاء عملية تابعة جديدة تنفذ ملفًا محددًا. وُصفت هاتان الدالتان بواسطة POSIX لتوفير طريقة موحدة لإنشاء عمليات جديدة على آلات تفتقر إلى القدرة على دعم استدعاء النظام fork(2). هذه الآلات هي عمومًا أنظمة مضمنة صغيرة تفتقر إلى دعم MMU.

توفر الدالتان posix_spawn() وposix_spawnp() وظائف مدمجة لـ fork(2) وexec(3)، مع بعض خطوات الصيانة الاختيارية في العملية التابعة قبل exec(3). لا يُقصد من هاتين الدالتين استبدال استدعاءات النظام fork(2) وexecve(2). في الواقع، توفران فقط مجموعة فرعية من الوظائف التي يمكن تحقيقها باستخدام استدعاءات النظام.

الفرق الوحيد بين posix_spawn() وposix_spawnp() هو الطريقة التي تحددان بها الملف الذي ستنفذه العملية التابعة. مع posix_spawn()، يُحدد الملف القابل للتنفيذ كاسم مسار (يمكن أن يكون مطلقًا أو نسبيًا). مع posix_spawnp()، يُحدد الملف القابل للتنفيذ كاسم ملف بسيط؛ يبحث النظام عن هذا الملف في قائمة الدلائل المحددة بواسطة PATH (بنفس طريقة execvp(3)). لبقية هذه الصفحة، تُصاغ المناقشة من حيث posix_spawn()، مع فهم أن posix_spawnp() يختلف فقط في النقطة الموصوفة للتو.

الوسائط المتبقية لهاتين الدالتين هي كما يلي:

pid يشير إلى مخزن مؤقت يُستخدم لإرجاع معرف العملية للعملية التابعة الجديدة.

file_actions
    يشير إلى كائن إجراءات ملف الإنجاب الذي يحدد الإجراءات المتعلقة بالملف التي ستنفذ في التابع بين خطوتي fork(2) وexec(3). يُهيأ هذا الكائن ويُملأ قبل استدعاء posix_spawn() باستخدام posix_spawn_file_actions_init(3) ودوال posix_spawn_file_actions_*().

attrp
    يشير إلى كائن سمات يحدد سمات مختلفة للعملية التابعة المنشأة. يُهيأ هذا الكائن ويُملأ قبل استدعاء posix_spawn() باستخدام posix_spawnattr_init(3) ودوال posix_spawnattr_*().

argv
envp
    يحددان قائمة الوسائط والبيئة للبرنامج الذي ينفذ في العملية التابعة، كما في execve(2).

أدناه، تُوصف الدوال من حيث عملية من ثلاث خطوات: خطوة fork()، خطوة ما قبل exec() (تُنفذ في التابع)، وخطوة exec() (تُنفذ في التابع).

  خطوة fork()
منذ glibc 2.24، تبدأ دالة posix_spawn() باستدعاء clone(2) مع علمي CLONE_VM وCLONE_VFORK. تستخدم التطبيقات الأقدم fork(2)، أو ربما vfork(2) (انظر أدناه).

يُوضع PID العملية التابعة الجديدة في *pid. ثم تُعيد دالة posix_spawn() التحكم إلى العملية الأم.

لاحقًا، يمكن للأب استخدام إحدى استدعاءات النظام الموصوفة في wait(2) للتحقق من حالة العملية الابن. إذا فشل الابن في أي من خطوات الصيانة الموصوفة أدناه، أو فشل في تنفيذ الملف المطلوب، فإنه يخرج بحالة 127.

قبل glibc 2.24، تُنشأ العملية الابن باستخدام vfork(2) بدلاً من fork(2) عندما يكون أي مما يلي صحيحًا:

•   عنصر spawn-flags لكائن السمات المشار إليه بواسطة attrp يحتوي على العلم الخاص بـ GNU POSIX_SPAWN_USEVFORK؛ أو

•   file_actions هو NULL وعنصر spawn-flags لكائن السمات المشار إليه بواسطة attrp لا يحتوي على POSIX_SPAWN_SETSIGMASK، POSIX_SPAWN_SETSIGDEF، POSIX_SPAWN_SETSCHEDPARAM، POSIX_SPAWN_SETSCHEDULER، POSIX_SPAWN_SETPGROUP، أو POSIX_SPAWN_RESETIDS.

بعبارة أخرى، يُستخدم vfork(2) إذا طلبه المتصل، أو إذا لم يكن هناك تنظيف متوقع في الابن قبل أن ينفذ exec(3) الملف المطلوب.

  خطوة ما قبل التنفيذ (pre-exec()): الصيانة
بين خطوتي fork() و exec()، قد تحتاج العملية الابن إلى أداء مجموعة من إجراءات الصيانة. تدعم الدالتان posix_spawn() و posix_spawnp() مجموعة صغيرة ومحددة جيدًا من مهام النظام التي يمكن للعملية الابن إنجازها قبل تنفيذ الملف القابل للتنفيذ. تُتحكم هذه العمليات بواسطة كائن السمات المشار إليه بواسطة attrp وكائن إجراءات الملف المشار إليه بواسطة file_actions. في الابن، تُجرى المعالجة بالتسلسل التالي:

(1) إجراءات سمات العملية: تُغير قناع الإشارة، والمعالجات المبدئية للإشارة، وخوارزمية الجدولة ومعاملاتها، ومجموعة العملية، ومعرفات المستخدم والمجموعة الفعلية كما هو محدد بواسطة كائن السمات المشار إليه بواسطة attrp.

(2) تُنفذ إجراءات الملف، كما هو محدد في وسيط file_actions، بالترتيب الذي حُددت به باستخدام استدعاءات دوال posix_spawn_file_actions_add*().

(3) تُغلق واصفات الملفات التي تحتوي على العلم FD_CLOEXEC.

جميع سمات العملية في الابن، بخلاف تلك المتأثرة بالسمات المحددة في الكائن المشار إليه بواسطة attrp وإجراءات الملف في الكائن المشار إليه بواسطة file_actions، ستتأثر كما لو أن الابن أُنشئ باستخدام fork(2) ونفذ البرنامج باستخدام execve(2).

تُعرف إجراءات سمات العملية بواسطة كائن السمات المشار إليه بواسطة attrp. يتحكم سمة spawn-flags (المعينة باستخدام posix_spawnattr_setflags(3)) في الإجراءات العامة التي تحدث، وتحدد السمات الأخرى في الكائن القيم التي ستُستخدم خلال تلك الإجراءات.

تأثيرات الأعلام التي قد تُحدد في spawn-flags هي كما يلي:

POSIX_SPAWN_SETSIGMASK
    يضبط قناع الإشارة على مجموعة الإشارات المحددة في سمة spawn-sigmask للكائن المشار إليه بواسطة attrp. إذا لم يُعيّن العلم POSIX_SPAWN_SETSIGMASK، فإن الابن يرث قناع إشارة الأب.

POSIX_SPAWN_SETSIGDEF
    يعيد ضبط ترتيب جميع الإشارات في المجموعة المحددة في سمة spawn-sigdefault للكائن المشار إليه بواسطة attrp إلى المبدئي. لمعالجة ترتيبات الإشارات غير المحددة في سمة spawn-sigdefault، أو المعالجة عندما لا يُحدد POSIX_SPAWN_SETSIGDEF، انظر execve(2).

POSIX_SPAWN_SETSCHEDPARAM
    إذا عُيّن هذا العلم، ولم يُعيّن العلم POSIX_SPAWN_SETSCHEDULER، فاضبط معاملات الجدولة على المعاملات المحددة في سمة spawn-schedparam للكائن المشار إليه بواسطة attrp.

POSIX_SPAWN_SETSCHEDULER
    اضبط خوارزمية سياسة الجدولة ومعاملات الطفل، كما يلي:

    •   تُضبط سياسة الجدولة إلى القيمة المحددة في السمة spawn-schedpolicy للكائن المشار إليه بواسطة attrp.

    •   تُضبط معاملات الجدولة إلى القيمة المحددة في السمة spawn-schedparam للكائن المشار إليه بواسطة attrp (ولكن انظر الأخطاء).

    إذا لم تُحدد العلمان POSIX_SPAWN_SETSCHEDPARAM و POSIX_SPAWN_SETSCHEDPOLICY، يرث الطفل سمات الجدولة المقابلة من الوالد.

POSIX_SPAWN_RESETIDS
    إذا وُضعت هذه العلامة، أعد ضبط UID و GID الفعليين إلى UID و GID الحقيقيين لعملية الوالد. إذا لم توضع هذه العلامة، يحتفظ الطفل بـ UID و GID الفعليين للوالد. في كلتا الحالتين، إذا كانت بتات صلاحية set-user-ID و set-group-ID مفعلة في الملف القابل للتنفيذ، فسيغلب تأثيرها ضبط UID و GID الفعليين (انظر execve(2)).

POSIX_SPAWN_SETPGROUP
    اضبط مجموعة العملية إلى القيمة المحددة في السمة spawn-pgroup للكائن المشار إليه بواسطة attrp. إذا كانت قيمة السمة spawn-pgroup تساوي 0، يُجعل معرف مجموعة عملية الطفل مساويًا لمعرف عمليته. إذا لم توضع علامة POSIX_SPAWN_SETPGROUP، يرث الطفل معرف مجموعة عملية الوالد.

POSIX_SPAWN_USEVFORK
    منذ glibc 2.24، ليس لهذه العلامة أي تأثير. في التطبيقات الأقدم، يفرض وضع هذه العلامة استخدام خطوة fork() لـ vfork(2) بدلاً من fork(2). يجب تعريف ماكرو اختبار الميزة _GNU_SOURCE للحصول على تعريف هذا الثابت.

POSIX_SPAWN_SETSID (منذ glibc 2.26)
    إذا وُضعت هذه العلامة، يجب على عملية الطفل إنشاء جلسة جديدة وتصبح قائدة الجلسة. يجب أيضًا على عملية الطفل أن تصبح قائدة مجموعة العملية الجديدة في الجلسة (انظر setsid(2)). يجب تعريف ماكرو اختبار الميزة _GNU_SOURCE للحصول على تعريف هذا الثابت.

إذا كان attrp فارغًا (NULL)، فتُطبق السلوكيات المبدئية الموصوفة أعلاه لكل علامة.

تحدد وسيطة file_actions سلسلة من عمليات الملف التي تُنفذ في عملية الطفل بعد المعالجة العامة الموصوفة أعلاه، وقبل تنفيذها لـ exec(3). إذا كان file_actions فارغًا (NULL)، فلا يُتخذ أي إجراء خاص، وتُطبق دلالات exec(3) القياسية—تبقى واصفات الملفات المفتوحة قبل exec مفتوحة في العملية الجديدة، باستثناء تلك التي وُضعت لها علامة FD_CLOEXEC. تظل أقفال الملفات في مكانها.

إذا لم يكن file_actions فارغًا (NULL)، فإنه يحتوي على مجموعة مرتبة من الطلبات لملفات open(2) و close(2) و dup2(2). تُضاف هذه الطلبات إلى file_actions بواسطة posix_spawn_file_actions_addopen(3) و posix_spawn_file_actions_addclose(3) و posix_spawn_file_actions_adddup2(3). تُنفذ العمليات المطلوبة بالترتيب الذي أُضيفت به إلى file_actions.

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

  خطوة exec()
بمجرد أن يتفرع الطفل بنجاح وينفذ جميع خطوات ما قبل exec المطلوبة، يشغل الطفل الملف القابل للتنفيذ المطلوب.

تأخذ عملية الطفل بيئتها من وسيطة envp، التي تُفسر كما لو أنها مُررت إلى execve(2). تأتي وسيطات العملية المنشأة من وسيطة argv، التي تُعالج كما في execve(2).

قيمة الإرجاع
عند الإكمال بنجاح، تضع posix_spawn() و posix_spawnp() معرف عملية الطفل (PID) في pid، وتُرجع 0. إذا حدث خطأ أثناء خطوة fork()، فلا يُنشأ أي طفل، وتكون محتويات *pid غير محددة، وتُرجع هاتان الدالتان رقم خطأ كما هو موصوف أدناه.

حتى عندما تُرجع هذه الدوال حالة نجاح، فقد تَفشَل العملية الابن لأسباب عديدة تتعلق بتهيئتها قبل exec(). بالإضافة إلى ذلك، قد يَفشَل exec(3). في كل هذه الحالات، ستَخْرُج العملية الابن بقيمة خروج 127.

الأخطاء
تَفْشَل الدالتان posix_spawn() وposix_spawnp() فقط في حالة فَشَل استدعاء fork(2) أو vfork(2) أو clone(2) الأساسي؛ في هذه الحالات، تُرجع هاتان الدالتان رقم خطأ، والذي سيكون أحد الأخطاء الموصوفة لـ fork(2) أو vfork(2) أو clone(2).

بالإضافة إلى ذلك، تَفْشَل هذه الدوال إذا:

ENOSYS
    الدالة غير مدعومة على هذا النظام.

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

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

ملاحظات
أنشطة الصيانة في الابن تُتحكم بها الكائنات المشار إليها بواسطة attrp (للإجراءات غير المتعلقة بالملفات) وfile_actions. في اصطلاح POSIX، يُشار إلى نوعي البيانات posix_spawnattr_t وposix_spawn_file_actions_t ككائنات، وعناصرها غير محددة بالاسم. يجب على البرامج المحمولة تهيئة هذه الكائنات باستخدام الدوال المحددة بواسطة POSIX فقط. (بمعنى آخر، على الرغم من أن هذه الكائنات قد تُنفذ كهياكل تحتوي على حقول، يجب على البرامج المحمولة تجنب الاعتماد على تفاصيل التنفيذ هذه.)

وفقًا لـ POSIX، من غير المحدد ما إذا كانت معالجات fork المنشأة بـ pthread_atfork(3) تُستدعى عند استدعاء posix_spawn(). منذ glibc 2.24، لا تُنفذ معالجات fork في أي حالة. في التطبيقات الأقدم، تُستدعى معالجات fork فقط إذا أُنشئ الابن باستخدام fork(2).

لا توجد دالة "posix_fspawn" (أي دالة تكون لـ posix_spawn() كما fexecve(3) لـ execve(2)). ومع ذلك، يمكن الحصول على هذه الوظيفة بتحديد معامل path كأحد الملفات في دليل /proc/self/fd للمستدعي.

العلل
ينص POSIX.1 على أنه عندما يُحدد POSIX_SPAWN_SETSCHEDULER في spawn-flags، فإن POSIX_SPAWN_SETSCHEDPARAM (إن وُجد) يُتجاهل. ومع ذلك، قبل glibc 2.14، فَشِلَت استدعاءات posix_spawn() بخطأ إذا حُدد POSIX_SPAWN_SETSCHEDULER دون تحديد POSIX_SPAWN_SETSCHEDPARAM أيضًا.

أمثلة
يُوضح البرنامج أدناه استخدام دوال متنوعة في واجهة برمجة تطبيقات spawn في POSIX. يقبل البرنامج سمات سطر أوامر يمكن استخدامها لإنشاء كائنات إجراءات ملفات وسمات. تُستخدم وسائط سطر الأوامر المتبقية كاسم الملف التنفيذي ووسائط سطر أوامر البرنامج الذي يُنفذ في الابن.

في التشغيل الأول، يُنفذ أمر date(1) في الابن، ولا يستخدم استدعاء posix_spawn() أي كائنات إجراءات ملفات أو سمات.

    $ ./a.out date;
    PID of child: 7634
    Tue Feb  1 19:47:50 CEST 2011
    Child status: exited, status=0

في التشغيل التالي، يُستخدم خيار سطر الأوامر -c لإنشاء كائن إجراءات ملفات يُغلق الإخراج القياسي في الابن. وبالتالي، يَفشَل date(1) عند محاولة تنفيذ الإخراج ويخرج بحالة 1.

    $ ./a.out -c date;
    PID of child: 7636
    date: write error: Bad file descriptor
    Child status: exited, status=1

في التشغيل التالي، يُستخدم خيار سطر الأوامر -s لإنشاء كائن سمات يُحدد أن جميع الإشارات (القابلة للحظر) في الابن يجب أن تُحظر. وبالتالي، فَشَل محاولة قتل الابن بالإشارة الافتراضية المرسلة بواسطة kill(1) (أي SIGTERM)، لأن تلك الإشارة محظورة. لذلك، لقتل الابن، SIGKILL ضرورية (لا يمكن حظر SIGKILL).

    $ ./a.out -s sleep 60 &
    [1] 7637
    $ PID of child: 7638

$ kill 7638;
$ kill -KILL 7638;
$ Child status: killed by signal 9
[1]+  Done                    ./a.out -s sleep 60

عندما نحاول تنفيذ أمر غير موجود في الابن، يَفشَل exec(3) ويخرج الابن بحالة 127.

    $ ./a.out xxxxx;
    PID of child: 10190
    Child status: exited, status=127

  مصدر البرنامج
    #include <err.h>
    #include <errno.h>
    #include <spawn.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <wait.h>

    char **environ;

    int
    main(int argc, char *argv[])
    {
        pid_t child_pid;
        int s, opt, status;
        sigset_t mask;
        posix_spawnattr_t attr;
        posix_spawnattr_t *attrp;
        posix_spawn_file_actions_t file_actions;
        posix_spawn_file_actions_t *file_actionsp;

        /* Parse command-line options, which can be used to specify an
           attributes object and file actions object for the child.  */

        attrp = NULL;
        file_actionsp = NULL;

        while ((opt = getopt(argc, argv, "sc")) != -1) {
            switch (opt) {
            case 'c':       /* -c: close standard output in child */

                /* Create a file actions object and add a "close"
                   action to it.  */

                s = posix_spawn_file_actions_init(&file_actions);
                if (s != 0)
                    errc(EXIT_FAILURE, s, "posix_spawn_file_actions_init");

                s = posix_spawn_file_actions_addclose(&file_actions,
                                                      STDOUT_FILENO);
                if (s != 0)
                    errc(EXIT_FAILURE, s, "posix_spawn_file_actions_addclose");

                file_actionsp = &file_actions;
                break;

            case 's':       /* -s: block all signals in child */

                /* Create an attributes object and add a "set signal mask"
                   action to it.  */

                s = posix_spawnattr_init(&attr);
                if (s != 0)
                    errc(EXIT_FAILURE, s, "posix_spawnattr_init");
                s = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK);
                if (s != 0)
                    errc(EXIT_FAILURE, s, "posix_spawnattr_setflags");

                sigfillset(&mask);
                s = posix_spawnattr_setsigmask(&attr, &mask);
                if (s != 0)
                    errc(EXIT_FAILURE, s, "posix_spawnattr_setsigmask");

                attrp = &attr;
                break;
            }
        }

        if (argv[optind] == NULL) {
            fprintf(stderr, "Usage: %s [-cs] executable [args]\n", argv[0]);
            exit(EXIT_FAILURE);
        }

        /* Spawn the child.  The name of the program to execute and the
           command-line arguments are taken from the command-line arguments
           of this program.  The environment of the program execed in the
           child is made the same as the parent's environment.  */

        s = posix_spawnp(&child_pid, argv[optind], file_actionsp, attrp,
                         &argv[optind], environ);
        if (s != 0)
            errc(EXIT_FAILURE, s, "posix_spawn");

        /* Destroy any objects that we created earlier.  */

        if (attrp != NULL) {
            s = posix_spawnattr_destroy(attrp);
            if (s != 0)
                errc(EXIT_FAILURE, s, "posix_spawnattr_destroy");
        }

        if (file_actionsp != NULL) {
            s = posix_spawn_file_actions_destroy(file_actionsp);
            if (s != 0)
                errc(EXIT_FAILURE, s, "posix_spawn_file_actions_destroy");
        }

        printf("PID of child: %jd\n", (intmax_t) child_pid);

        /* Monitor status of the child until it terminates.  */

        do {
            s = waitpid(child_pid, &status, WUNTRACED | WCONTINUED);
            if (s == -1)
                err(EXIT_FAILURE, "waitpid");

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

        exit(EXIT_SUCCESS);
    }

انظر أيضًا
close(2), dup2(2), execl(2), execlp(2), fork(2), open(2), sched_setparam(2), sched_setscheduler(2), setpgid(2), setuid(2), sigaction(2), sigprocmask(2), posix_spawn_file_actions_addclose(3), posix_spawn_file_actions_adddup2(3), posix_spawn_file_actions_addopen(3), posix_spawn_file_actions_destroy(3), posix_spawn_file_actions_init(3), posix_spawnattr_destroy(3), posix_spawnattr_getflags(3), posix_spawnattr_getpgroup(3), posix_spawnattr_getschedparam(3), posix_spawnattr_getschedpolicy(3), posix_spawnattr_getsigdefault(3), posix_spawnattr_getsigmask(3), posix_spawnattr_init(3), posix_spawnattr_setflags(3), posix_spawnattr_setpgroup(3), posix_spawnattr_setschedparam(3), posix_spawnattr_setschedpolicy(3), posix_spawnattr_setsigdefault(3), posix_spawnattr_setsigmask(3), pthread_atfork(3), <spawn.h>, Base Definitions volume of POSIX.1-2001, http://www.opengroup.org/unix/online.html

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

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

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