فرع(2)

الاسم
fork - إنشاء عملية تابعة

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

موجز
#include <unistd.h>

pid_t fork(void);

الوصف
تنشئ fork() عملية جديدة بنسخ العملية المستدعية. يُشار إلى العملية الجديدة باسم العملية التابعة. يُشار إلى العملية المستدعية باسم العملية الأم.

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

العملية التابعة هي نسخة طبق الأصل من العملية الأم باستثناء النقاط التالية:

•   للعُملية التابعة معرف عملية فريد خاص بها، ولا يتطابق معرف العملية هذا مع معرف أي مجموعة عمليات (setpgid(2)) أو جلسة موجودة.

•   معرف العملية الأم للتابع هو نفس معرف العملية الأم.

•   لا ترث العملية التابعة أقفال الذاكرة الخاصة بعمليتها الأم (mlock(2), mlockall(2)).

•   يتم إعادة تعيين استخدامات موارد العملية (getrusage(2)) وعدادات وقت وحدة المعالجة المركزية (times(2)) إلى الصفر في التابعة.

•   مجموعة الإشارات المعلقة للعملية التابعة فارغة في البداية (sigpending(2)).

•   لا ترث العملية التابعة تعديلات الإشارات من عمليتها الأم (semop(2)).

•   لا ترث العملية التابعة أقفال السجلات المرتبطة بالعملية من عمليتها الأم (fcntl(2)). (من ناحية أخرى، ترث أقفال وصف الملف المفتوح fcntl(2) وأقفال flock(2) من عمليتها الأم.)

•   لا ترث العملية التابعة المؤقتات من عمليتها الأم (setitimer(2), alarm(2), timer_create(2)).

•   لا ترث العملية التابعة عمليات الإدخال/الإخراج غير المتزامنة المعلقة من عمليتها الأم (aio_read(3), aio_write(3))، ولا ترث أي سياقات إدخال/إخراج غير متزامنة من عمليتها الأم (انظر io_setup(2)).

جميع سمات العملية في القائمة السابقة محددة في POSIX.1. تختلف العملية الأم والتابعة أيضًا فيما يتعلق بسمات العملية الخاصة بلينكس التالية:

•   لا ترث العملية التابعة إشعارات تغيير الدليل (dnotify) من عمليتها الأم (انظر وصف F_NOTIFY في fcntl(2)).

•   يتم إعادة تعيين إعداد prctl(2) PR_SET_PDEATHSIG بحيث لا تتلقى العملية التابعة إشارة عند إنهاء عمليتها الأم.

•   يتم تعيين قيمة ارتخاء المؤقت المبدئية إلى قيمة ارتخاء المؤقت الحالية للعملية الأم. انظر وصف PR_SET_TIMERSLACK في prctl(2).

•   لا يتم توريث تعيينات الذاكرة التي تم وضع علامة عليها بعلم madvise(2) MADV_DONTFORK عبر fork().

•   يتم تصفير الذاكرة في نطاقات العناوين التي تم وضع علامة عليها بعلم madvise(2) MADV_WIPEONFORK في التابعة بعد fork(). (يبقى إعداد MADV_WIPEONFORK في مكانه لنطاقات العناوين تلك في التابعة.)

•   إشارة إنهاء العملية التابعة هي دائمًا SIGCHLD (انظر clone(2)).

•   لا ترث العملية التابعة بتات إذن الوصول إلى المنفذ التي تم تعيينها بواسطة ioperm(2)؛ يجب على التابعة تشغيل أي بتات تحتاجها باستخدام ioperm(2).

لاحظ النقاط الإضافية التالية:

•   يتم إنشاء العملية التابعة بخيط واحد—الخيط الذي استدعى fork(). يتم نسخ مساحة العنوان الافتراضية الكاملة للأم في التابعة، بما في ذلك حالات كائنات المزامنة ومتغيرات الشرط وكائنات pthreads الأخرى؛ قد يكون استخدام pthread_atfork(3) مفيدًا للتعامل مع المشكلات التي قد يسببها هذا.

•   بعد fork() في برنامج متعدد الخيوط، يمكن للعملية التابعة استدعاء الدوال الآمنة للإشارات غير المتزامنة فقط بأمان (انظر signal-safety(7)) إلى أن تستدعي execve(2).

•   ترث العملية التابعة نسخًا من مجموعة واصفات الملفات المفتوحة للأم. يشير كل واصف ملف في التابعة إلى نفس وصف الملف المفتوح (انظر open(2)) مثل واصف الملف المقابل في الأم. يعني هذا أن واصفي الملف يشتركان في أعلام حالة الملف المفتوح، وإزاحة الملف، وسمات الإدخال/الإخراج المدفوعة بالإشارة (انظر وصف F_SETOWN و F_SETSIG في fcntl(2)).

•   ترث العملية التابعة نسخًا من مجموعة واصفات قوائم انتظار الرسائل المفتوحة للأم (انظر mq_overview(7)). يشير كل واصف ملف في التابعة إلى نفس وصف قائمة انتظار الرسائل المفتوحة مثل واصف الملف المقابل في الأم. يعني هذا أن واصفي الملف يشتركان في نفس الأعلام (mq_flags).

•   ترث العملية التابعة نسخًا من مجموعة تيارات الدليل المفتوحة للأم (انظر opendir(3)). ينص POSIX.1 على أن تيارات الدليل المقابلة في الأم والتابعة قد تشترك في تحديد موضع تيار الدليل؛ على لينكس/glibc لا تفعل ذلك.

قيمة الإرجاع
عند النجاح، يتم إرجاع معرف العملية (PID) للعملية التابعة في الأم، ويتم إرجاع 0 في التابعة. عند الفشل، يتم إرجاع -1 في الأم، ولا يتم إنشاء عملية تابعة، ويتم تعيين errno للإشارة إلى الخطأ.

الأخطاء

EAGAIN
    تمت مصادفة حد يفرضه النظام على عدد الخيوط. هناك عدد من الحدود التي قد تؤدي إلى هذا الخطأ:

    •   تم الوصول إلى حد الموارد الناعم RLIMIT_NPROC (المعيّن عبر setrlimit(2))، والذي يحد من عدد العمليات والخيوط لمعرف مستخدم حقيقي؛

    •   الحد النظامي للنواة على عدد العمليات والخيوط، /proc/sys/kernel/threads-max، قد وُصِل إليه (انظر proc(5))؛

    •   الحد الأقصى لعدد معرفات العمليات (PIDs)، /proc/sys/kernel/pid_max، قد وُصِل إليه (انظر proc(5))؛ أو

    •   حد PID (pids.max) الذي تفرضه وحدة تحكم cgroup "عدد العمليات" (PIDs) قد وُصِل إليه.

EAGAIN
    المستدعي يعمل تحت سياسة الجدولة SCHED_DEADLINE وليس لديه علم إعادة التعيين عند fork مضبوطًا. انظر sched(7).

ENOMEM
    فشلت fork() في تخصيص هياكل النواة الضرورية لأن الذاكرة ضيقة.

ENOMEM
    جرت محاولة لإنشاء عملية طفل في نطاق PID الذي أنهت عمليته "init" عملها. انظر pid_namespaces(7).

ENOSYS
    fork() غير مدعومة على هذه المنصة (على سبيل المثال، عتاد بدون وحدة إدارة ذاكرة).

ERESTARTNOINTR (منذ لينكس 2.6.17)
    قُطِعَ استدعاء النظام بواسطة إشارة وسيُعاد تشغيله. (لا يمكن رؤية هذا إلا أثناء التتبع trace.)

الإصدارات

  الاختلافات بين مكتبة C والنواة
منذ glibc 2.3.3، بدلاً من استدعاء استدعاء النظام fork() للنواة، فإن غلاف glibc fork() المقدم كجزء من تنفيذ خيوط NPTL يستدعي clone(2) بأعلام توفر نفس تأثير استدعاء النظام التقليدي. (استدعاء fork() يعادل استدعاء clone(2) مع تحديد flags كـ SIGCHLD فقط.) يستدعي غلاف glibc أي معالجات fork التي أُنشئت باستخدام pthread_atfork(3).

  أمان الإشارة غير المتزامنة
_Fork(3) هي متغير آمن للإشارات غير المتزامنة من fork(2).

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

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

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

أمثلة
انظر pipe(2) و wait(2) لمزيد من الأمثلة.

    #include <signal.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>

    int
    main(void)
    {
        pid_t pid;

        if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
            perror("signal");
            exit(EXIT_FAILURE);
        }
        pid = fork();
        switch (pid) {
        case -1:
            perror("fork");
            exit(EXIT_FAILURE);
        case 0:
            puts("Child exiting.");
            fflush(stdout);
            _exit(EXIT_SUCCESS);
        default:
            printf("Child is PID %jd\n", (intmax_t) pid);
            puts("Parent exiting.");
            exit(EXIT_SUCCESS);
        }
    }

انظر أيضًا
clone(2), execve(2), exit(2), _exit(2), setrlimit(2), unshare(2), vfork(2), wait(2), daemon(3), _Fork(3), pthread_atfork(3), capabilities(7), credentials(7)

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

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

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