| clone(2) | System Calls Manual | clone(2) |
الاسم¶
clone, __clone2, clone3 - إنشاء عملية ابنة
المكتبة¶
مكتبة سي المعيارية (libc، -lc)
موجز¶
/* النموذج المبدئي لدالة غلاف glibc */
#define _GNU_SOURCE #include <sched.h>
int clone(typeof(int (void *_Nullable)) *fn,
void *stack,
int flags,
void *_Nullable arg, ...
/* pid_t *_Nullable parent_tid,
void *_Nullable tls,
pid_t *_Nullable child_tid */ );
/* للنموذج المبدئي لنداء النظام الخام ()clone، انظر VERSIONS. */
#include <linux/sched.h> /* تعريف struct clone_args */ #include <sched.h> /* تعريف ثوابت CLONE_* */ #include <sys/syscall.h> /* تعريف ثوابت SYS_* */ #include <unistd.h>
long syscall(SYS_clone3, struct clone_args *cl_args, size_t size);
ملاحظة: لا توفر glibc غلافًا لـ clone3()، مما يستلزم استخدام syscall(2).
الوصف¶
تنشئ نداءات النظام هذه عملية جديدة ("ابنة")، بطريقة مشابهة لـ fork(2).
على النقيض من fork(2)، توفر نداءات النظام هذه تحكمًا أكثر دقة في أجزاء سياق التنفيذ التي تُتشارك بين المستدعِي والعملية الابنة. على سبيل المثال، باستخدام نداءات النظام هذه، يمكن لـ المستدعِي التحكم فيما إذا كانت العمليتان تتشاركان مساحة العناوين الافتراضية، وجدول واصفات الملفات، وجدول معالجات الإشارات أم لا. تسمح نداءات النظام هذه أيضًا بوضع العملية الابنة الجديدة في namespaces(7) منفصلة.
لاحظ أنه في صفحة الدليل هذه، تشير "المستدعِي" عادةً إلى "العملية الأب". ولكن انظر أوصاف CLONE_PARENT و CLONE_THREAD أدناه.
تصف هذه الصفحة الواجهات التالية:
- •
- دالة غلاف glibc لـ clone() ونداء النظام الأساسي الذي تستند إليه. يصف النص الرئيس دالة الغلاف؛ وتوصَف الاختلافات في نداء النظام الخام قرب نهاية هذه الصفحة.
- •
- نداء النظام clone3() الأحدث.
فيما تبقى من هذه الصفحة، يُستخدم المصطلح "نداء الاستنساخ" عند الإشارة إلى التفاصيل التي تنطبق على جميع هذه الواجهات.
دالة الغلاف ()clone¶
عند إنشاء العملية الابنة باستخدام دالة غلاف clone()، فإنها تبدأ التنفيذ باستدعاء الدالة التي يشير إليها المعامل fn. (يختلف هذا عن fork(2)، حيث يستمر التنفيذ في الابن من نقطة استدعاء fork(2)). يُمرر المعامل arg كمعامل للدالة fn.
عندما تعود الدالة fn(arg)، تنتهي العملية الابنة. الرقم الصحيح الذي تعيده fn هو حالة الخروج للعملية الابنة. قد تنتهي العملية الابنة أيضًا صراحةً باستدعاء exit(2) أو بعد تلقي إشارة قاتلة.
يحدد المعامل stack موقع المكدس الذي تستخدمه العملية الابنة. وبما أن العملية الابنة والمستدعِي قد يتشاركان الذاكرة، فليس من الممكن للعملية الابنة أن تُنفذ في نفس المكدس الذي يستخدمه المستدعِي. يجب على المستدعِي بالتالي إعداد مساحة ذاكرة لمكدس الابن وتمرير مؤشر إلى هذه المساحة إلى clone(). تنمو المكدسات لأسفل في جميع المعالجات التي تشغل لينكس (باستثناء معالجات HP PA)، لذا يشير stack عادةً إلى أعلى عنوان في مساحة الذاكرة المعدة لمكدس الابن. لاحظ أن clone() لا توفر وسيلة يمكن من خلالها لـ المستدعِي إبلاغ النواة بحجم منطقة المكدس.
تُناقش المعاملات المتبقية لـ clone() أدناه.
clone3()¶
يوفر نداء النظام clone3() مجموعة فائقة من وظائف واجهة clone() الأقدم. كما يوفر عددًا من تحسينات واجهة برمجة التطبيقات (API)، بما في ذلك: مساحة لبتات أعلام إضافية؛ وفصل أنظف في استخدام المعاملات المختلفة؛ والقدرة على تحديد حجم منطقة مكدس الابن.
كما هو الحال مع fork(2)، يعود clone3() في كل من الأب والابن. ويعيد 0 في العملية الابنة ويعيد المعرف PID الخاص بالابن في الأب.
المعامل cl_args لـ clone3() هو هيكل بالشكل التالي:
struct clone_args {
u64 flags; /* قناع بتات الأعلام */
u64 pidfd; /* مكان تخزين واصف ملف PID
(int *) */
u64 child_tid; /* مكان تخزين معرف TID للابن،
في ذاكرة الابن (pid_t *) */
u64 parent_tid; /* مكان تخزين معرف TID للابن،
في ذاكرة الأب (pid_t *) */
u64 exit_signal; /* الإشارة التي تُرسل للأب عند
إنهاء الابن */
u64 stack; /* مؤشر لأدنى بايت في المكدس */
u64 stack_size; /* حجم المكدس */
u64 tls; /* موقع TLS الجديد */
u64 set_tid; /* مؤشر لمصفوفة pid_t
(منذ لينكس 5.5) */
u64 set_tid_size; /* عدد العناصر في set_tid
(منذ لينكس 5.5) */
u64 cgroup; /* واصف ملف لمجموعة التحكم (cgroup) المستهدفة
للابن (منذ لينكس 5.7) */
};
يجب تهيئة المعامل size الذي يُزود لـ clone3() ليساوي حجم هذا الهيكل. (وجود المعامل size يسمح بتوسعات مستقبلية لهيكل clone_args).
يُحدد مكدس العملية الابنة عبر cl_args.stack، الذي يشير إلى أدنى بايت في منطقة المكدس، و cl_args.stack_size، الذي يحدد حجم المكدس بالبايت. في الحالة التي يُحدد فيها علم CLONE_VM (انظر أدناه)، يجب تخصيص مكدس وتحديده صراحةً. خلاف ذلك، يمكن تحديد هذين الحقلين كـ NULL و 0، مما يجعل الابن يستخدم نفس منطقة مكدس الأب (في مساحة العناوين الافتراضية الخاصة بالابن).
تُناقش الحقول المتبقية في المعامل cl_args أدناه.
التكافؤ بين معاملات ()clone و ()clone3¶
على عكس واجهة clone() الأقدم، حيث تُمرر المعاملات بشكل فردي، تُجمع المعاملات في واجهة clone3() الأحدث داخل هيكل clone_args الموضح أعلاه. يسمح هذا الهيكل بمجموعة فائقة من المعلومات الممرة عبر معاملات clone().
يوضح الجدول التالي التكافؤ بين معاملات clone() والحقول في المعامل clone_args المزود لـ clone3():
| clone() | clone3() | ملاحظات |
| حقل cl_args | ||
| flags & ~0xff | flags | لمعظم الأعلام؛ التفاصيل أدناه |
| parent_tid | pidfd | انظر CLONE_PIDFD |
| child_tid | child_tid | انظر CLONE_CHILD_SETTID |
| parent_tid | parent_tid | انظر CLONE_PARENT_SETTID |
| flags & 0xff | exit_signal | |
| stack | stack | |
| --- | stack_size | |
| tls | tls | انظر CLONE_SETTLS |
| --- | set_tid | انظر أدناه للتفاصيل |
| --- | set_tid_size | |
| --- | cgroup | انظر CLONE_INTO_CGROUP |
إشارة إنهاء الابن¶
عند إنهاء العملية الابنة، قد تُرسل إشارة إلى الأب. تُحدد إشارة الإنهاء في البايت المنخفض لـ flags (clone()) أو في cl_args.exit_signal (clone3()). إذا حُددت هذه الإشارة كأي شيء آخر غير SIGCHLD، فيجب على العملية الأب تحديد الخيارات __WALL أو __WCLONE عند انتظار الابن باستخدام wait(2). إذا لم تُحدد أي إشارة (أي صفر)، فلن تُرسل إشارة إلى العملية الأب عند إنهاء الابن.
مصفوفة set_tid¶
بشكل مبدئي، تختار النواة المعرف PID المتسلسل التالي للعملية الجديدة في كل من فضاءات أسماء PID التي توجد فيها. عند إنشاء عملية باستخدام clone3()، يمكن استخدام مصفوفة set_tid (المتوفرة منذ لينكس 5.5) لاختيار معرفات PID محددة للعملية في بعض أو كل فضاءات أسماء PID التي توجد فيها. إذا كان يجب ضبط PID للعملية المنشأة حديثًا فقط لفضاء أسماء PID الحالي أو في فضاء أسماء PID المنشأ حديثًا (إذا كانت flags تحتوي على CLONE_NEWPID) فيجب أن يكون العنصر الأول في مصفوفة set_tid هو الـ PID المطلوب ويجب أن يكون set_tid_size مساوياً لـ 1.
إذا كان يجب أن يكون لـ PID العملية المنشأة حديثًا قيمة معينة في فضاءات أسماء PID متعددة، فيمكن أن تحتوي مصفوفة set_tid على مدخلات متعددة. يحدد المدخل الأول الـ PID في فضاء أسماء PID الأكثر تعشيشًا، ويحتوي كل من المدخلات التالية على الـ PID في فضاء أسماء PID السلف المقابل. يُحدد عدد فضاءات أسماء PID التي يجب ضبط الـ PID فيها بواسطة set_tid_size الذي لا يمكن أن يكون أكبر من عدد فضاءات أسماء PID المتداخلة حاليًا.
لإنشاء عملية بمعرفات PID التالية في تسلسل هرمي لفضاء أسماء PID:
| مستوى فضاء أسماء PID | PID المطلوب | ملاحظات |
| 0 | 31496 | فضاء أسماء PID الأبعد |
| 1 | 42 | |
| 2 | 7 | فضاء أسماء PID الأكثر تعشيشًا |
اضبط المصفوفة على:
set_tid[0] = 7; set_tid[1] = 42; set_tid[2] = 31496; set_tid_size = 3;
إذا كان يلزم فقط تحديد معرفات PID في فضاءي أسماء PID الأكثر تعشيشًا، فاضبط المصفوفة على:
set_tid[0] = 7; set_tid[1] = 42; set_tid_size = 2;
يُختار الـ PID في فضاءات أسماء PID خارج الفضاءين الأكثر تعشيشًا بنفس الطريقة التي يُختار بها أي PID آخر.
تتطلب ميزة set_tid صلاحية CAP_SYS_ADMIN أو (منذ لينكس 5.9) CAP_CHECKPOINT_RESTORE في جميع فضاءات أسماء المستخدمين المالكة لفضاءات أسماء PID المستهدفة.
يمكن لـ المستدعِين فقط اختيار PID أكبر من 1 في فضاء أسماء PID معين إذا كانت هناك عملية init (أي عملية ذات PID 1) موجودة بالفعل في ذلك الفضاء. خلاف ذلك، يجب أن يكون مدخل الـ PID لفضاء أسماء PID هذا هو 1.
قناع الأعلام¶
يسمح كل من clone() و clone3() باستخدام قناع بتات للأعلام يعدل سلوكهما ويسمح لـ المستدعِي بتحديد ما يُتشارك بين المستدعِي والعملية الابنة. يُشار إلى قناع البتات هذا — المعامل flags في clone() أو حقل cl_args.flags الممرر إلى clone3() — باسم قناع flags فيما تبقى من هذه الصفحة.
يُحدد قناع flags كعملية OR بتية لصفر أو أكثر من الثوابت المدرجة أدناه. باستثناء ما هو مذكور أدناه، تتوفر هذه الأعلام (ولها نفس التأثير) في كل من clone() و clone3().
- CLONE_CHILD_CLEARTID (منذ لينكس 2.5.49)
- مسح (تصفير) معرف خيط الابن في الموقع الذي يشير إليه child_tid (clone()) أو cl_args.child_tid (clone3()) في ذاكرة الابن عند خروجه، وإجراء تنبيه (wakeup) على futex في ذلك العنوان. يمكن تغيير العنوان المعني بواسطة نداء النظام set_tid_address(2). يُستخدم هذا بواسطة مكتبات الخيوط.
- CLONE_CHILD_SETTID (منذ لينكس 2.5.49)
- تخزين معرف خيط الابن في الموقع الذي يشير إليه child_tid (clone()) أو cl_args.child_tid (clone3()) في ذاكرة الابن. تكتمل عملية التخزين قبل أن يعيد نداء الاستنساخ التحكم إلى مساحة المستخدم في العملية الابنة. (لاحظ أن عملية التخزين قد لا تكون قد اكتملت قبل عودة نداء الاستنساخ في العملية الأب، وهو أمر ذو صلة إذا استُخدم علم CLONE_VM أيضًا).
- CLONE_CLEAR_SIGHAND (منذ لينكس 5.5)
- بشكل مبدئي، تكون ترتيبات الإشارات في خيط الابن هي نفسها الموجودة في الأب. إذا حُدد هذا العلم، فإن جميع الإشارات التي تُعالج في الأب (ولم تُضبط على SIG_IGN) تُعاد إلى ترتيباتها المبدئية (SIG_DFL) في الابن.
- تحديد هذا العلم مع CLONE_SIGHAND غير منطقي وغير مسموح به.
- CLONE_DETACHED (تاريخي)
- لفترة من الوقت (خلال سلسلة تطوير لينكس 2.5) كان هناك علم CLONE_DETACHED، والذي تسبب في عدم تلقي الأب إشارة عند إنهاء الابن. في النهاية، دُمج تأثير هذا العلم تحت علم CLONE_THREAD وبحلول وقت إصدار لينكس 2.6.0، لم يعد لهذا العلم أي تأثير. منذ لينكس 2.6.2، اختفت الحاجة لتقديم هذا العلم مع CLONE_THREAD.
- لا يزال هذا العلم معرفًا، ولكن عادةً ما يُتجاهل عند استدعاء clone(). ومع ذلك، راجع وصف CLONE_PIDFD لمعرفة بعض الاستثناءات.
- CLONE_FILES (منذ لينكس 2.0)
- إذا ضُبط CLONE_FILES، فإن العملية المستدعِية والعملية الوليدة تتشاركان نفس جدول واصفات الملفات. أي واصف ملف يُنشأ بواسطة العملية المستدعِية أو بواسطة العملية الوليدة يكون صالحًا أيضًا في العملية الأخرى. وبالمثل، إذا أغلقت إحدى العمليات واصف ملف، أو غيرت الأعلام المرتبطة به (باستخدام عملية F_SETFD في fcntl(2))، تتأثر العملية الأخرى أيضًا. إذا استدعت عملية تتشارك جدول واصفات ملفات execve(2)، فإن جدول واصفات ملفاتها يُنسخ (يُفصل التشارك).
- إذا لم يُضبط CLONE_FILES، ترث العملية الوليدة نسخة من جميع واصفات الملفات المفتوحة في العملية المستدعِية وقت استدعاء الاستنساخ. العمليات اللاحقة التي تفتح أو تغلق واصفات الملفات، أو تغير أعلام واصف الملف، والتي تقوم بها إما العملية المستدعِية أو العملية الوليدة لا تؤثر على العملية الأخرى. لاحظ، مع ذلك، أن واصفات الملفات المنسوخة في الوليدة تشير إلى نفس أوصاف الملفات المفتوحة مثل واصفات الملفات المقابلة في العملية المستدعِية، وبالتالي تتشارك إزاحات الملف وأعلام حالة الملف (انظر open(2)).
- CLONE_FS (منذ لينكس 2.0)
- إذا ضُبط CLONE_FS، يتشارك المستدعِي والعملية الوليدة نفس معلومات نظام الملفات. يتضمن ذلك جذر نظام الملفات، ودليل العمل الحالي، وقناع وضع إنشاء الملفات (umask). أي استدعاء لـ chroot(2) أو chdir(2) أو umask(2) تقوم به العملية المستدعِية أو العملية الوليدة يؤثر أيضًا على العملية الأخرى.
- إذا لم يُضبط CLONE_FS، تعمل العملية الوليدة على نسخة من معلومات نظام الملفات للعملية المستدعِية وقت استدعاء الاستنساخ. الاستدعاءات لـ chroot(2) أو chdir(2) أو umask(2) التي تُجرى لاحقًا بواسطة إحدى العمليات لا تؤثر على العملية الأخرى.
- CLONE_INTO_CGROUP (منذ لينكس 5.7)
- بشكل مبدئي، توضع العملية الوليدة في نفس مجموعة التحكم (cgroup) الإصدار 2 التي تتبعها العملية الأب. يسمح علم CLONE_INTO_CGROUP بإنشاء العملية الوليدة في مجموعة تحكم إصدار 2 مختلفة. (لاحظ أن CLONE_INTO_CGROUP له تأثير فقط على مجموعات التحكم من الإصدار 2.)
- لوضع العملية الوليدة في مجموعة تحكم مختلفة، يحدد المستدعِي CLONE_INTO_CGROUP في cl_args.flags ويمرر واصف ملف يشير إلى مجموعة تحكم إصدار 2 في حقل cl_args.cgroup. (يمكن الحصول على واصف الملف هذا عن طريق فتح دليل cgroup v2 باستخدام إما علم O_RDONLY أو علم O_PATH.) لاحظ أن جميع القيود المعتادة (الموضحة في cgroups(7)) بشأن وضع عملية في مجموعة تحكم إصدار 2 تنطبق هنا.
- من بين حالات الاستخدام الممكنة لـ CLONE_INTO_CGROUP ما يلي:
- •
- إن تفريخ عملية في مجموعة تحكم مختلفة عن مجموعة تحكم الأب يجعل من الممكن لمدير الخدمات تفريخ خدمات جديدة مباشرة في مجموعات تحكم مخصصة. هذا يزيل اضطراب المحاسبة الذي قد يحدث إذا أُنشئت العملية الوليدة أولاً في نفس مجموعة تحكم الأب ثم نُقلت إلى مجموعة التحكم المستهدفة. علاوة على ذلك، فإن تفريخ العملية الوليدة مباشرة في مجموعة تحكم مستهدفة أقل تكلفة بكثير من نقل العملية الوليدة إلى مجموعة التحكم المستهدفة بعد إنشائها.
- •
- يسمح علم CLONE_INTO_CGROUP أيضًا بإنشاء عمليات وليدة مجمدة عن طريق تفريخها في مجموعة تحكم مجمدة. (انظر cgroups(7) للحصول على وصف لمتحكم التجميد.)
- •
- بالنسبة للتطبيقات متعددة الخيوط (أو حتى تنفيذات الخيوط التي تستخدم مجموعات التحكم للحد من الخيوط الفردية)، من الممكن إنشاء تخطيط ثابت لمجموعة التحكم قبل تفريخ كل خيط مباشرة في مجموعة التحكم المستهدفة الخاصة به.
- CLONE_IO (منذ لينكس 2.6.25)
- إذا ضُبط CLONE_IO، فإن العملية الجديدة تتشارك سياق إدخال/إخراج مع العملية المستدعِية. إذا لم يُضبط هذا العلم، فإن العملية الجديدة (كما في fork(2)) يكون لها سياق إدخال/إخراج خاص بها.
- سياق الإدخال/الإخراج هو نطاق الإدخال/الإخراج لجدول القرص (أي ما يستخدمه مجدول الإدخال/الإخراج لنمذجة جدولة إدخال/إخراج العملية). إذا تشاركت العمليات نفس سياق الإدخال/الإخراج، فإن مجدول الإدخال/الإخراج يعاملها كعملية واحدة. نتيجة لذلك، فإنها تتشارك وقت القرص. بالنسبة لبعض مجدولي الإدخال/الإخراج، إذا تشاركت عمليتان في سياق إدخال/إخراج واحد، فسيُسمح لهما بتداخل وصولهما إلى القرص. إذا كانت عدة خيوط تقوم بالإدخال/الإخراج نيابة عن نفس العملية (aio_read(3)، على سبيل المثال)، فيجب عليها استخدام CLONE_IO للحصول على أداء إدخال/إخراج أفضل.
- إذا لم تُضبط النواة بخيار CONFIG_BLOCK، فإن هذا العلم لا يفعل شيئًا.
- CLONE_NEWCGROUP (منذ لينكس 4.6)
- إنشاء العملية في فضاء أسماء cgroup جديد. إذا لم يُضبط هذا العلم، فإن العملية (كما في fork(2)) تُنشأ في نفس فضاءات أسماء cgroup الخاصة بالعملية المستدعِية.
- لمزيد من المعلومات حول فضاءات أسماء cgroup، راجع cgroup_namespaces(7).
- فقط العملية ذات الامتيازات (CAP_SYS_ADMIN) يمكنها استخدام CLONE_NEWCGROUP.
- CLONE_NEWIPC (منذ لينكس 2.6.19)
- إذا ضُبط CLONE_NEWIPC، فسيتم إنشاء العملية في فضاء أسماء IPC جديد. إذا لم يُضبط هذا العلم، فإن العملية (كما في fork(2)) تُنشأ في نفس فضاء أسماء IPC الخاص بالعملية المستدعِية.
- لمزيد من المعلومات حول فضاءات أسماء IPC، راجع ipc_namespaces(7).
- فقط العملية ذات الامتيازات (CAP_SYS_ADMIN) يمكنها استخدام CLONE_NEWIPC. لا يمكن تحديد هذا العلم بالتزامن مع CLONE_SYSVSEM.
- CLONE_NEWNET (منذ لينكس 2.6.24)
- (اكتمل تنفيذ هذا العلم فقط في حوالي لينكس 2.6.29.)
- إذا ضُبط CLONE_NEWNET، فسيتم إنشاء العملية في فضاء أسماء شبكة جديد. إذا لم يُضبط هذا العلم، فإن العملية (كما في fork(2)) تُنشأ في نفس فضاء أسماء الشبكة الخاص بالعملية المستدعِية.
- لمزيد من المعلومات حول فضاءات أسماء الشبكة، راجع network_namespaces(7).
- فقط العملية ذات الامتيازات (CAP_SYS_ADMIN) يمكنها استخدام CLONE_NEWNET.
- CLONE_NEWNS (منذ لينكس 2.4.19)
- إذا ضُبط CLONE_NEWNS، تبدأ الوليدة المستنسخة في فضاء أسماء وصل جديد، يُهيأ بنسخة من فضاء أسماء الأب. إذا لم يُضبط CLONE_NEWNS، تعيش الوليدة في نفس فضاء أسماء الوصل الخاص بالأب.
- لمزيد من المعلومات حول فضاءات أسماء الوصل، راجع namespaces(7) و mount_namespaces(7).
- فقط العملية ذات الامتيازات (CAP_SYS_ADMIN) يمكنها استخدام CLONE_NEWNS. لا يُسمح بتحديد كل من CLONE_NEWNS و CLONE_FS في نفس استدعاء الاستنساخ.
- CLONE_NEWPID (منذ لينكس 2.6.24)
- إذا ضُبط CLONE_NEWPID، فسيتم إنشاء العملية في فضاء أسماء PID جديد. إذا لم يُضبط هذا العلم، فإن العملية (كما في fork(2)) تُنشأ في نفس فضاء أسماء PID الخاص بالعملية المستدعِية.
- لمزيد من المعلومات حول فضاءات أسماء PID، راجع namespaces(7) و pid_namespaces(7).
- فقط العملية ذات الامتيازات (CAP_SYS_ADMIN) يمكنها استخدام CLONE_NEWPID. لا يمكن تحديد هذا العلم بالتزامن مع CLONE_THREAD.
- CLONE_NEWUSER
- (أصبح هذا العلم ذا معنى لأول مرة لـ clone() في لينكس 2.6.23، ودُمجت دلالات clone() الحالية في لينكس 3.5، ودُمجت الأجزاء النهائية لجعل فضاءات أسماء المستخدمين قابلة للاستخدام بالكامل في لينكس 3.8.)
- إذا ضُبط CLONE_NEWUSER، فسيتم إنشاء العملية في فضاء أسماء مستخدم جديد. إذا لم يُضبط هذا العلم، فإن العملية (كما في fork(2)) تُنشأ في نفس فضاء أسماء المستخدمين الخاص بالعملية المستدعِية.
- لمزيد من المعلومات حول فضاءات أسماء المستخدمين، راجع namespaces(7) و user_namespaces(7).
- قبل لينكس 3.8، كان استخدام CLONE_NEWUSER يتطلب أن يكون لدى المستدعِي ثلاث قدرات: CAP_SYS_ADMIN و CAP_SETUID و CAP_SETGID. بدءًا من لينكس 3.8، لا حاجة لامتيازات لإنشاء فضاء أسماء مستخدم.
- لا يمكن تحديد هذا العلم بالتزامن مع CLONE_THREAD أو CLONE_PARENT. لأسباب أمنية، لا يمكن تحديد CLONE_NEWUSER بالتزامن مع CLONE_FS.
- CLONE_NEWUTS (منذ لينكس 2.6.19)
- إذا ضُبط CLONE_NEWUTS، فسيتم إنشاء العملية في فضاء أسماء UTS جديد، وتُهيأ معرفاته بنسخ المعرفات من فضاء أسماء UTS للعملية المستدعِية. إذا لم يُضبط هذا العلم، فإن العملية (كما في fork(2)) تُنشأ في نفس فضاء أسماء UTS الخاص بالعملية المستدعِية.
- لمزيد من المعلومات حول فضاءات أسماء UTS، راجع uts_namespaces(7).
- فقط العملية ذات الامتيازات (CAP_SYS_ADMIN) يمكنها استخدام CLONE_NEWUTS.
- CLONE_PARENT (منذ لينكس 2.3.12)
- إذا ضُبط CLONE_PARENT، فإن أب الوليدة الجديدة (كما تعيده getppid(2)) سيكون هو نفسه أب العملية المستدعِية.
- إذا لم يُضبط CLONE_PARENT، فإن أب الوليدة (كما في fork(2)) هو العملية المستدعِية.
- لاحظ أن العملية الأب، كما تعيدها getppid(2)، هي التي تتلقى إشارة عند إنهاء الوليدة، بحيث إذا ضُبط CLONE_PARENT، فإن أب العملية المستدعِية، وليس العملية المستدعِية نفسها، هو من يتلقى الإشارة.
- لا يمكن استخدام علم CLONE_PARENT في استدعاءات الاستنساخ من قبل عملية البدء العامة (PID 1 في فضاء أسماء PID الأولي) وعمليات البدء في فضاءات أسماء PID الأخرى. يمنع هذا القيد إنشاء أشجار عمليات متعددة الجذور بالإضافة إلى منع إنشاء زومبي لا يمكن حصادهم في فضاء أسماء PID الأولي.
- CLONE_PARENT_SETTID (منذ لينكس 2.5.49)
- تخزين معرف خيط الوليدة في الموقع الذي يشير إليه parent_tid (في clone()) أو cl_args.parent_tid (في clone3()) في ذاكرة الأب. (في لينكس 2.5.32-2.5.48 كان هناك علم CLONE_SETTID يقوم بذلك.) تكتمل عملية التخزين قبل أن يعيد استدعاء الاستنساخ التحكم إلى مساحة المستخدم.
- CLONE_PID (لينكس 2.0 إلى لينكس 2.5.15)
- إذا ضُبط CLONE_PID، تُنشأ العملية الوليدة بنفس معرف العملية للمستدعِية. هذا مفيد لاختراق النظام، ولكنه غير مفيد لغير ذلك. من لينكس 2.3.21 فصاعدًا، أصبح بالإمكان تحديد هذا العلم فقط بواسطة عملية تمهيد النظام (PID 0). اختفى العلم تمامًا من مصادر النواة في لينكس 2.5.16. لاحقًا، تجاهلت النواة بصمت هذه البتة إذا حُددت في قناع الأعلام. وبعد فترة طويلة، أُعيد تدوير نفس البتة لاستخدامها كعلم CLONE_PIDFD.
- CLONE_PIDFD (منذ لينكس 5.2)
- إذا حُدد هذا العلم، يُخصص واصف ملف PID يشير إلى العملية الوليدة ويُوضع في موقع محدد في ذاكرة الأب. يُضبط علم الإغلاق عند التنفيذ (close-on-exec) على واصف الملف الجديد هذا. يمكن استخدام واصفات ملفات PID للأغراض الموضحة في pidfd_open(2).
- •
- عند استخدام clone3()، يُوضع واصف ملف PID في الموقع الذي يشير إليه cl_args.pidfd.
- •
- عند استخدام clone()، يُوضع واصف ملف PID في الموقع الذي يشير إليه parent_tid. وبما أن معامل parent_tid يُستخدم لإعادة واصف ملف PID، فلا يمكن استخدام CLONE_PIDFD مع CLONE_PARENT_SETTID عند استدعاء clone().
- إذا حُدد CLONE_PIDFD مع CLONE_THREAD، فإن واصف ملف PID الذي تم الحصول عليه يشير إلى خيط معين، بدلاً من متصدر مجموعة الخيوط إذا لم يُحدد CLONE_THREAD. تتوفر هذه الميزة منذ لينكس 6.9.
- إذا حُدد علم CLONE_DETACHED المهمل بجانب CLONE_PIDFD عند استدعاء clone()، فسيُعاد خطأ. وينتج خطأ أيضًا إذا حُدد CLONE_DETACHED عند استدعاء clone3(). يضمن سلوك الخطأ هذا إمكانية إعادة استخدام البتة المقابلة لـ CLONE_DETACHED لميزات واصف ملف PID الإضافية في المستقبل.
- CLONE_PTRACE (منذ لينكس 2.2)
- إذا حُدد CLONE_PTRACE، وكانت العملية المستدعِية تخضع للتتبع، فسيتم تتبع الوليدة أيضًا (راجع ptrace(2)).
- CLONE_SETTLS (منذ لينكس 2.5.32)
- يُضبط واصف TLS (تخزين الخيط المحلي) على tls.
- تفسير tls والأثر الناتج يعتمد على المعمارية. في x86، يُفسر tls على أنه struct user_desc * (راجع set_thread_area(2)). في x86-64 هي القيمة الجديدة التي ستُضبط لسجل القاعدة %fs (راجع معامل ARCH_SET_FS لـ arch_prctl(2)). في المعماريات التي تحتوي على سجل TLS مخصص، فهي القيمة الجديدة لذلك السجل.
- يتطلب استخدام هذا العلم معرفة مفصلة وعمومًا لا ينبغي استخدامه إلا في المكتبات التي تنفذ تعدد الخيوط.
- CLONE_SIGHAND (منذ لينكس 2.0)
- إذا ضُبط CLONE_SIGHAND، تتشارك العملية المستدعِية والوليدة في نفس جدول معالجي الإشارات. إذا استدعت العملية المستدعِية أو الوليدة sigaction(2) لتغيير السلوك المرتبط بإشارة ما، يتغير السلوك في العملية الأخرى أيضًا. ومع ذلك، لا يزال للعملية المستدعِية والوليدة أقنعة إشارات ومجموعات إشارات معلقة متميزة. لذا، يمكن لإحداهما حجب أو إلغاء حجب الإشارات باستخدام sigprocmask(2) دون التأثير على العملية الأخرى.
- إذا لم يُضبط CLONE_SIGHAND، ترث العملية الوليدة نسخة من معالجي الإشارات للعملية المستدعِية وقت استدعاء الاستنساخ. الاستدعاءات لـ sigaction(2) التي تُجرى لاحقًا بواسطة إحدى العمليات ليس لها أي تأثير على العملية الأخرى.
- منذ لينكس 2.6.0، يجب أن يتضمن قناع flags أيضًا CLONE_VM إذا حُدد CLONE_SIGHAND.
- CLONE_STOPPED (منذ لينكس 2.6.0)
- إذا ضُبط CLONE_STOPPED، فسيتم إيقاف الوليدة في البداية (كما لو أُرسلت إليها إشارة SIGSTOP)، ويجب استئنافها عن طريق إرسال إشارة SIGCONT إليها.
- أُهمل هذا العلم منذ لينكس 2.6.25 فصاعدًا، وأُزيل تمامًا في لينكس 2.6.38. ومنذ ذلك الحين، تتجاهله النواة بصمت دون خطأ. بدءًا من لينكس 4.6، أُعيد تدوير نفس البتة لعلم CLONE_NEWCGROUP.
- CLONE_SYSVSEM (منذ لينكس 2.5.10)
- إذا ضُبط CLONE_SYSVSEM، تتشارك الوليدة والعملية المستدعِية في قائمة واحدة لقيم تعديل سيمافور System V (تُعرف بـ semadj) (راجع semop(2)). في هذه الحالة، تراكم القائمة المشتركة قيم semadj عبر جميع العمليات التي تتشارك القائمة، وتُجرى تعديلات السيمافور فقط عندما تنتهي آخر عملية تشارك القائمة (أو تتوقف عن مشاركتها باستخدام unshare(2)). إذا لم يُضبط هذا العلم، يكون للوليدة قائمة semadj منفصلة تكون فارغة في البداية.
- CLONE_THREAD (منذ لينكس 2.4.0)
- إذا ضُبط CLONE_THREAD، تُوضع الوليدة في نفس مجموعة الخيوط التي تتبعها العملية المستدعِية. ولجعل ما تبقى من مناقشة CLONE_THREAD أكثر قابلية للقراءة، استُخدم مصطلح "خيط" للإشارة إلى العمليات داخل مجموعة الخيوط.
- كانت مجموعات الخيوط ميزة أضيفت في لينكس 2.4 لدعم مفهوم خيوط POSIX لمجموعة من الخيوط التي تتشارك معرف عملية (PID) واحد. داخليًا، هذا المعرف المشترك هو ما يسمى بمعرف مجموعة الخيوط (TGID). منذ لينكس 2.4، تعيد الاستدعاءات لـ getpid(2) معرف TGID للمستدعِي.
- يمكن تمييز الخيوط داخل المجموعة من خلال معرفات الخيوط (TID) الفريدة (على مستوى النظام). يتوفر معرف TID للخيط الجديد كنتيجة دالة تعاد للمستدعِي، ويمكن للخيط الحصول على معرف TID الخاص به باستخدام gettid(2).
- عند إجراء استدعاء استنساخ بدون تحديد CLONE_THREAD، يُوضع الخيط الناتج في مجموعة خيوط جديدة يكون معرف TGID الخاص بها هو نفسه معرف TID للخيط. هذا الخيط هو متصدر مجموعة الخيوط الجديدة.
- الخيط الجديد المنشأ باستخدام CLONE_THREAD له نفس العملية الأب للعملية التي أجرت استدعاء الاستنساخ (أي، مثل CLONE_PARENT)، بحيث تعيد الاستدعاءات لـ getppid(2) نفس القيمة لجميع الخيوط في مجموعة خيوط واحدة. عندما ينتهي خيط CLONE_THREAD، لا تُرسل للخيط الذي أنشأه إشارة SIGCHLD (أو أي إشارة إنهاء أخرى)؛ ولا يمكن الحصول على حالة مثل هذا الخيط باستخدام wait(2). (يُقال عن الخيط إنه منفصل).
- بعد انتهاء جميع الخيوط في مجموعة الخيوط، تُرسل للعملية الأب لمجموعة الخيوط إشارة SIGCHLD (أو إشارة إنهاء أخرى).
- إذا قام أي من الخيوط في مجموعة خيوط بتنفيذ execve(2)، فسيتم إنهاء جميع الخيوط الأخرى باستثناء متصدر مجموعة الخيوط، ويُنفذ البرنامج الجديد في متصدر مجموعة الخيوط.
- إذا أنشأ أحد الخيوط في مجموعة خيوط وليدة باستخدام fork(2)، فيمكن لأي خيط في المجموعة انتظار (wait(2)) تلك الوليدة.
- منذ لينكس 2.5.35، يجب أن يتضمن قناع flags أيضًا CLONE_SIGHAND إذا حُدد CLONE_THREAD (ولاحظ أنه منذ لينكس 2.6.0، يتطلب CLONE_SIGHAND أيضًا تضمين CLONE_VM).
- ترتيبات الإشارات وإجراءاتها تكون على مستوى العملية: إذا سُلّمت إشارة غير معالجة إلى خيط ما، فإنها ستؤثر على (تنهي، توقف، تواصل، تُتجاهل في) جميع أعضاء مجموعة الخيوط.
- لكل خيط قناع إشارات خاص به، كما يُضبط بواسطة sigprocmask(2).
- قد تكون الإشارة موجهة للعملية أو موجهة للخيط. تستهدف الإشارة الموجهة للعملية مجموعة خيوط (أي معرف TGID)، وتُسلّم إلى خيط مختار عشوائيًا من بين تلك التي لا تحجب الإشارة. قد تكون الإشارة موجهة للعملية لأنها نُتجت بواسطة النواة لأسباب أخرى غير استثناء الأجهزة، أو لأنها أُرسلت باستخدام kill(2) أو sigqueue(3). تستهدف الإشارة الموجهة للخيط (أي تُسلّم إلى) خيطًا معينًا. قد تكون الإشارة موجهة للخيط لأنها أُرسلت باستخدام tgkill(2) أو pthread_sigqueue(3)، أو لأن الخيط نفذ تعليمة بلغة الآلة أدت إلى إطلاق استثناء في الأجهزة (على سبيل المثال، وصول غير صالح للذاكرة أطلق SIGSEGV أو استثناء نقطة عائمة أطلق SIGFPE).
- يعيد استدعاء sigpending(2) مجموعة إشارات تمثل اتحاد الإشارات المعلقة الموجهة للعملية والإشارات المعلقة للخيط المستدعِي.
- إذا سُلّمت إشارة موجهة للعملية إلى مجموعة خيوط، وقامت مجموعة الخيوط بتثبيت معالج للإشارة، فسيتم استدعاء المعالج في عضو واحد بالضبط، يُختار عشوائيًا من أعضاء مجموعة الخيوط الذين لم يحجبوا الإشارة. إذا كانت خيوط متعددة في مجموعة ما تنتظر قبول نفس الإشارة باستخدام sigwaitinfo(2)، فستختار النواة عشوائيًا أحد هذه الخيوط لتلقي الإشارة.
- CLONE_UNTRACED (منذ لينكس 2.5.46)
- إذا حُدد CLONE_UNTRACED، فلا يمكن لعملية التتبع فرض CLONE_PTRACE على هذه العملية الوليدة.
- CLONE_VFORK (منذ لينكس 2.2)
- إذا ضُبط CLONE_VFORK، يُعلق تنفيذ العملية المستدعِية حتى تحرر الوليدة موارد ذاكرتها الافتراضية عبر استدعاء execve(2) أو _exit(2) (كما هو الحال مع vfork(2)).
- إذا لم يُضبط CLONE_VFORK، فستكون كل من العملية المستدعِية والوليدة قابلتين للجدولة بعد الاستدعاء، ولا ينبغي للتطبيق الاعتماد على حدوث التنفيذ بأي ترتيب معين.
- CLONE_VM (منذ لينكس 2.0)
- إذا ضُبطت CLONE_VM، فستعمل العملية المستدعية والعملية الوليدة في مساحة الذاكرة ذاتها. وعلى وجه الخصوص، تكون عمليات كتابة الذاكرة التي تجريها العملية المستدعية أو العملية الوليدة مرئية أيضًا في العملية الأخرى. علاوة على ذلك، فإن أي وصل أو فصل لذاكرة يُجرى باستخدام mmap(2) أو munmap(2) بواسطة العملية الوليدة أو المستدعية يؤثر أيضًا في العملية الأخرى.
- إذا لم تُضبط CLONE_VM، فستعمل العملية الوليدة في نسخة منفصلة من مساحة ذاكرة العملية المستدعية وقت استدعاء الاستنساخ. ولا تؤثر عمليات كتابة الذاكرة أو وصل/فصل الملفات التي تجريها إحدى العمليتين في الأخرى، تمامًا كما في fork(2).
- إذا حُددت علامة CLONE_VM ولم تُحدد علامة CLONE_VFORK، فسيُمسح أي كدس إشارات بديل أُنشئ بواسطة sigaltstack(2) في العملية الوليدة.
قيمة الإرجاع¶
عند النجاح، يُعاد معرف الخيط (thread ID) للعملية الوليدة في خيط التنفيذ الخاص بالمستدعِي. وفي حالة الفشل، يُعاد -1 في سياق المستدعِي، ولا تُنشأ أي عملية وليدة، وتُضبط errno للإشارة إلى الخطأ.
الأخطاء¶
- E2BIG (في clone3() فقط)
- النواة لا تدعم بعض الوظائف المطلوبة في استدعاء clone3() هذا: معطى الحجم أكبر مما تدعمه النواة، وتوجد قيم غير صفرية في البنية (struct) بعد الحجم الذي تدعمه النواة.
- E2BIG (في clone3() فقط)
- معطى الحجم أكبر من حجم الصفحة.
- EACCES (في clone3() فقط)
- حُددت CLONE_INTO_CGROUP في cl_args.flags، ولكن القيود (الموضحة في cgroups(7)) المفروضة على وضع العملية الوليدة في الإصدار 2 من مجموعات التحكم (cgroup) المشار إليها بواسطة cl_args.cgroup لم تُستوفَ.
- EAGAIN
- هناك عدد كبير جدًا من العمليات قيد التشغيل بالفعل؛ انظر fork(2).
- EBUSY (في clone3() فقط)
- حُددت CLONE_INTO_CGROUP في cl_args.flags، لكن واصف الملف المحدد في cl_args.cgroup يشير إلى مجموعة تحكم (cgroup) من الإصدار 2 تملك متحكم نطاق (domain controller) مُفعّلًا.
- EEXIST (في clone3() فقط)
- واحد (أو أكثر) من معرفات العمليات (PIDs) المحددة في set_tid موجود بالفعل في مساحة أسماء PID المقابلة.
- EINVAL
- كلا CLONE_SIGHAND و CLONE_CLEAR_SIGHAND حُددا في قناع flags.
- EINVAL
- حُددت CLONE_SIGHAND في قناع flags، لكن CLONE_VM لم تُحدد. (منذ لينكس 2.6.0.)
- EINVAL
- حُددت CLONE_THREAD في قناع flags، لكن CLONE_SIGHAND لم تُحدد. (منذ لينكس 2.5.35.)
- EINVAL
- حُددت CLONE_THREAD في قناع flags، لكن العملية الحالية استدعت سابقًا unshare(2) مع علامة CLONE_NEWPID أو استخدمت setns(2) لإعادة ربط نفسها بمساحة أسماء PID.
- EINVAL
- كلا CLONE_FS و CLONE_NEWNS حُددا في قناع flags.
- EINVAL (منذ لينكس 3.9)
- كلا CLONE_NEWUSER و CLONE_FS حُددا في قناع flags.
- EINVAL
- كلا CLONE_NEWIPC و CLONE_SYSVSEM حُددا في قناع flags.
- EINVAL
- حُددت CLONE_NEWPID مع واحدة (أو كلتا) CLONE_THREAD أو CLONE_PARENT في قناع flags.
- EINVAL
- كلا CLONE_NEWUSER و CLONE_THREAD حُددا في قناع flags.
- EINVAL (منذ لينكس 2.6.32)
- حُددت CLONE_PARENT، وكان المستدعِي هو عملية init.
- EINVAL
- تُعيدها دالة غلاف clone() في glibc عندما يُحدد fn أو stack كقيمة NULL.
- EINVAL
- حُددت CLONE_NEWIPC في قناع flags، لكن النواة لم تُضبط مع خيارات CONFIG_SYSVIPC و CONFIG_IPC_NS.
- EINVAL
- حُددت CLONE_NEWNET في قناع flags، لكن النواة لم تُضبط مع خيار CONFIG_NET_NS.
- EINVAL
- حُددت CLONE_NEWPID في قناع flags، لكن النواة لم تُضبط مع خيار CONFIG_PID_NS.
- EINVAL
- حُددت CLONE_NEWUSER في قناع flags، لكن النواة لم تُضبط مع خيار CONFIG_USER_NS.
- EINVAL
- حُددت CLONE_NEWUTS في قناع flags، لكن النواة لم تُضبط مع خيار CONFIG_UTS_NS.
- EINVAL
- الكدس stack غير محاذٍ لحدود مناسبة لهذه المعمارية. على سبيل المثال، في معمارية aarch64، يجب أن يكون stack من مضاعفات 16.
- EINVAL (في clone3() فقط)
- حُددت CLONE_DETACHED في قناع flags.
- EINVAL (في clone() فقط)
- حُددت CLONE_PIDFD مع CLONE_DETACHED في قناع flags.
- EINVAL (قبل لينكس 6.9)
- حُددت CLONE_PIDFD مع CLONE_THREAD في قناع flags.
- EINVAL (في clone() فقط)
- حُددت CLONE_PIDFD مع CLONE_PARENT_SETTID في قناع flags.
- EINVAL (في clone3() فقط)
- حجم set_tid_size أكبر من عدد مساحات أسماء PID المتداخلة.
- EINVAL (في clone3() فقط)
- أحد معرفات العمليات (PIDs) المحددة في set_tid غير صالح.
- EINVAL (في clone3() فقط)
- حُددت CLONE_THREAD أو CLONE_PARENT في قناع flags، ولكن حُددت إشارة في exit_signal.
- EINVAL (في AArch64 فقط، لينكس 4.6 وما قبله)
- الكدس stack لم يكن محاذيًا لحدود 128 بت.
- ENOMEM
- تعذر تخصيص ذاكرة كافية لتخصيص بنية مهمة للعملية الوليدة، أو لنسخ أجزاء سياق المستدعِي التي يجب نسخها.
- ENOSPC (منذ لينكس 3.7)
- حُددت CLONE_NEWPID في قناع flags، ولكن كان سيتم تجاوز الحد الأقصى لعمق تداخل مساحات أسماء PID؛ انظر pid_namespaces(7).
- ENOSPC (منذ لينكس 4.9؛ قبله EUSERS)
- حُددت CLONE_NEWUSER في قناع flags، وسيتسبب الاستدعاء في تجاوز الحد الأقصى لعدد مساحات أسماء المستخدمين المتداخلة. انظر user_namespaces(7).
- من لينكس 3.11 إلى لينكس 4.8، كان الخطأ الذي يُشخص في هذه الحالة هو EUSERS.
- ENOSPC (منذ لينكس 4.9)
- إحدى القيم في قناع flags حددت إنشاء مساحة أسماء مستخدم جديدة، ولكن القيام بذلك كان سيؤدي إلى تجاوز الحد المحدد بواسطة الملف المقابل في /proc/sys/user. لمزيد من التفاصيل، انظر namespaces(7).
- EOPNOTSUPP (في clone3() فقط)
- حُددت CLONE_INTO_CGROUP في cl_args.flags، لكن واصف الملف المحدد في cl_args.cgroup يشير إلى مجموعة تحكم (cgroup) من الإصدار 2 في حالة domain invalid.
- EPERM
- حُددت CLONE_NEWCGROUP أو CLONE_NEWIPC أو CLONE_NEWNET أو CLONE_NEWNS أو CLONE_NEWPID أو CLONE_NEWUTS بواسطة عملية غير مميزة (عملية بدون CAP_SYS_ADMIN).
- EPERM
- حُددت CLONE_PID بواسطة عملية غير العملية 0. (يحدث هذا الخطأ فقط في لينكس 2.5.15 وما قبله).
- EPERM
- حُددت CLONE_NEWUSER في قناع flags، ولكن إما معرف المستخدم الفعلي أو معرف المجموعة الفعلي للمستدعِي ليس له خريطة مقابلة (mapping) في مساحة أسماء الأب (انظر user_namespaces(7)).
- EPERM (منذ لينكس 3.9)
- حُددت CLONE_NEWUSER في قناع flags وكان المستدعِي في بيئة chroot (أي أن دليل الجذر للمستدعِي لا يطابق دليل الجذر لمساحة أسماء الوصل التي يقيم فيها).
- EPERM (في clone3() فقط)
- كان set_tid_size أكبر من صفر، ويفتقر المستدعِي إلى قدرة CAP_SYS_ADMIN في واحدة أو أكثر من مساحات أسماء المستخدمين التي تمتلك مساحات أسماء PID المقابلة.
- ERESTARTNOINTR (منذ لينكس 2.6.17)
- قُطِعَ استدعاء النظام بواسطة إشارة وسيُعاد تشغيله. (لا يمكن رؤية هذا إلا أثناء التتبع trace.)
- EUSERS (لينكس 3.11 إلى لينكس 4.8)
- حُددت CLONE_NEWUSER في قناع flags، وسيتم تجاوز الحد الأقصى لعدد مساحات أسماء المستخدمين المتداخلة. انظر مناقشة خطأ ENOSPC أعلاه.
الإصدارات¶
تجري دالة غلاف clone() في glibc بعض التغييرات في الذاكرة التي يشير إليها stack (التغييرات المطلوبة لإعداد الكدس بشكل صحيح للوليد) قبل استدعاء نظام clone(). لذا، في الحالات التي يُستخدم فيها clone() لإنشاء عمليات وليدة بشكل تكراري، لا تستخدم المخزن المؤقت المستخدم لكدس الأب ككدس للعملية الوليدة.
في i386، لا ينبغي استدعاء clone() من خلال vsyscall، بل مباشرة من خلال int $0x80.
الاختلافات بين مكتبة C والنواة¶
استدعاء نظام clone() الخام يتوافق بشكل أوثق مع fork(2) من حيث أن التنفيذ في العملية الوليدة يستمر من نقطة الاستدعاء. وعلى هذا النحو، تُحذف المعطيات fn و arg الخاصة بدالة غلاف clone().
على عكس غلاف glibc، يقبل استدعاء نظام clone() الخام NULL كمعطى لـ stack (وبالمثل يسمح clone3() لـ cl_args.stack بأن يكون NULL). وفي هذه الحالة، تستخدم العملية الوليدة نسخة مكررة من كدس الأب. (تضمن دلالات النسخ عند الكتابة Copy-on-write أن تحصل العملية الوليدة على نسخ منفصلة من صفحات الكدس عندما تقوم أي من العمليتين بتعديل الكدس). وفي هذه الحالة، لضمان التشغيل الصحيح، لا ينبغي تحديد خيار CLONE_VM. (إذا كانت العملية الوليدة تتشارك ذاكرة الأب بسبب استخدام علامة CLONE_VM، فلن يحدث تكرار بالنسخ عند الكتابة ومن المرجح أن يؤدي ذلك إلى حدوث فوضى).
يختلف ترتيب المعطيات أيضًا في استدعاء النظام الخام، وهناك اختلافات في المعطيات عبر المعماريات المختلفة، كما هو مفصل في الفقرات التالية.
واجهة استدعاء النظام الخام في x86-64 وبعض المعماريات الأخرى (بما في ذلك sh و tile و alpha) هي:
long clone(unsigned long flags, void *stack,
int *parent_tid, int *child_tid,
unsigned long tls);
في x86-32 وعدة معماريات شائعة أخرى (بما في ذلك score و ARM و ARM 64 و PA-RISC و arc و Power PC و xtensa و MIPS)، ينعكس ترتيب آخر معطيين:
long clone(unsigned long flags, void *stack,
int *parent_tid, unsigned long tls,
int *child_tid);
في معماريتي cris و s390، ينعكس ترتيب أول معطيين:
long clone(void *stack, unsigned long flags,
int *parent_tid, int *child_tid,
unsigned long tls);
في معمارية microblaze، يُزوّد معطى إضافي:
long clone(unsigned long flags, void *stack,
int stack_size, /* حجم الكدس */
int *parent_tid, int *child_tid,
unsigned long tls);
blackfin و m68k و sparc¶
اتفاقيات تمرير المعطيات في blackfin و m68k و sparc تختلف عن الأوصاف أعلاه. لمزيد من التفاصيل، انظر مصدر النواة (و glibc).
المعايير¶
لينكس.
التاريخ¶
- clone3()
- لينكس 5.3.
لينكس 2.4 وما قبله¶
في سلسلة لينكس 2.4.x، لا تجعل CLONE_THREAD بشكل عام أب الخيط الجديد هو نفس أب العملية المستدعية. ومع ذلك، من لينكس 2.4.7 إلى لينكس 2.4.18، كانت علامة CLONE_THREAD تتضمن علامة CLONE_PARENT (كما هو الحال في لينكس 2.6.0 وما بعده).
في لينكس 2.4 وما قبله، لا يأخذ clone() المعطيات parent_tid و tls و child_tid.
ia64¶
في ia64، تُستخدم واجهة مختلفة:
int __clone2(typeof(int (void *)) *fn,
void *stack_base, size_t stack_size,
int flags, void *arg, ...
/* pid_t *parent_tid, struct user_desc *tls,
pid_t *child_tid */ );
النموذج المبدئي الموضح أعلاه هو لدالة غلاف glibc؛ أما بالنسبة لنداء النظام نفسه، فيمكن وصف النموذج المبدئي على النحو التالي (وهو مطابق لنموذج clone() المبدئي على microblaze):
long clone2(unsigned long flags, void *stack_base,
int stack_size, /* حجم المكدس */
int *parent_tid, int *child_tid,
unsigned long tls);
تعمل __clone2() بنفس طريقة clone()، باستثناء أن stack_base يشير إلى أدنى عنوان في منطقة مكدس الابن، ويحدد stack_size حجم المكدس الذي يشير إليه stack_base.
ملاحظات¶
أحد استخدامات نداءات النظام هذه هو تنفيذ الخيوط: تدفقات متعددة للتحكم في برنامج يعمل بالتزامن في مساحة عناوين مشتركة.
يمكن استخدام نداء النظام kcmp(2) لاختبار ما إذا كانت عمليتان تتشاركان موارد مختلفة مثل جدول واصفات الملفات، أو عمليات تراجع سيمافور System V، أو مساحة عناوين افتراضية.
المناولات المسجلة باستخدام pthread_atfork(3) لا تُنفذ أثناء نداء clone.
العلل¶
احتوت إصدارات مكتبة GNU C من 2.3.4 وحتى 2.24 (بما في ذلك هذا الإصدار) على دالة غلاف لـ getpid(2) تؤدي عملية تخزين في خبيئة لمعرفات العمليات (PIDs). اعتمد هذا التخزين في الخبيئة على دعم في غلاف glibc للدالة clone()، ولكن القيود في التنفيذ جعلت الخبيئة غير محدثة في بعض الظروف. وعلى وجه الخصوص، إذا وُصل إشعار (إشارة) إلى الابن فور استدعاء clone()، فإن استدعاء getpid(2) في معالج ذلك الإشعار قد يعيد معرف العملية للمستدعِي («الأب»)، إذا لم يتح لغلاف الاستنساخ فرصة لتحديث خبيئة معرف العملية في الابن بعد. (يتجاهل هذا النقاش الحالة التي يُنشأ فيها الابن باستخدام CLONE_THREAD، حيث ينبغي أن تعيد getpid(2) القيمة نفسها في الابن وفي العملية التي استدعت clone()، بما أن المستدعِي والابن في مجموعة الخيوط نفسها. كما لا تحدث مشكلة الخبيئة القديمة إذا تضمنت معطاة flags الوسم CLONE_VM.) وللحصول على الحقيقة، كان من الضروري أحيانًا استخدام كود مثل الآتي:
#include <syscall.h> pid_t mypid; mypid = syscall(SYS_getpid);
بسبب مشكلة الخبيئة القديمة، بالإضافة إلى مشكلات أخرى لوحظت في getpid(2)، أُزيلت ميزة تخزين معرف العملية في الخبيئة في الإصدار 2.25 من glibc.
أمثلة¶
يوضح البرنامج التالي استخدام clone() لإنشاء عملية ابن تعمل في مساحة أسماء UTS منفصلة. يغير الابن اسم المضيف في مساحة أسماء UTS الخاصة به. ثم يعرض كل من الأب والابن اسم مضيف النظام، مما يسمح برؤية اختلاف اسم المضيف في مساحات أسماء UTS للأب والابن. لمثال على استخدام هذا البرنامج، انظر setns(2).
داخل البرنامج النموذجي، نخصص الذاكرة التي ستُستخدم مكدسًا للابن باستخدام mmap(2) بدلاً من malloc(3) للأسباب الرئيسة التالية:
- •
- تخصص mmap(2) كتلة من الذاكرة تبدأ عند حدود الصفحة وتكون من مضاعفات حجم الصفحة. وهذا مفيد إذا أردنا إنشاء صفحة حماية (صفحة ذات حماية PROT_NONE) في نهاية المكدس باستخدام mprotect(2).
- •
- يمكننا تحديد العلم MAP_STACK لطلب تخطيط مناسب للمكدس. في الوقت الحالي، هذا العلم لا يقوم بأي عملية (no-op) على لينكس، ولكنه موجود وله تأثير على بعض الأنظمة الأخرى، لذا ينبغي لنا تضمينه من أجل قابلية النقل.
مصدر البرنامج¶
#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>
static int /* دالة البدء للابن المنسوخ */
childFunc(void *arg)
{
struct utsname uts;
/* غيّر اسم المضيف في نطاق أسماء UTS للابن. */
if (sethostname(arg, strlen(arg)) == -1)
err(EXIT_FAILURE, "sethostname");
/* استرجع واعرض اسم المضيف. */
if (uname(&uts) == -1)
err(EXIT_FAILURE, "uname");
printf("uts.nodename in child: %s\n", uts.nodename);
/* أبقِ نطاق الأسماء مفتوحًا لفترة عبر النوم.
يسمح هذا ببعض التجارب--على سبيل المثال، قد
تنضم عملية أخرى إلى نطاق الأسماء. */
sleep(200);
return 0; /* ينتهي الابن الآن */
}
#define STACK_SIZE (1024 * 1024) /* حجم المكدس للابن المنسوخ */
int
main(int argc, char *argv[])
{
char *stack; /* بداية مخزن المكدس */
char *stackTop; /* نهاية مخزن المكدس */
pid_t pid;
struct utsname uts;
if (argc < 2) {
fprintf(stderr, "Usage: %s <child-hostname>\n", argv[0]);
exit(EXIT_SUCCESS);
}
/* خصص ذاكرة لتستخدم كمكدس للابن. */
stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
if (stack == MAP_FAILED)
err(EXIT_FAILURE, "mmap");
stackTop = stack + STACK_SIZE; /* نفترض أن المكدس ينمو لأسفل */
/* أنشئ ابنًا له نطاق أسماء UTS الخاص به؛
يبدأ الابن التنفيذ في childFunc(). */
pid = clone(childFunc, stackTop, CLONE_NEWUTS | SIGCHLD, argv[1]);
if (pid == -1)
err(EXIT_FAILURE, "clone");
if (munmap(stack, STACK_SIZE))
err(EXIT_FAILURE, "munmap");
printf("clone() returned %jd\n", (intmax_t) pid);
/* يسقط الأب إلى هنا */
sleep(1); /* أعطِ الابن وقتًا لتغيير اسم مضيفه */
/* اعرض اسم المضيف في نطاق أسماء UTS للأب. سيكون هذا
مختلفًا عن اسم المضيف في نطاق أسماء UTS للابن. */
if (uname(&uts) == -1)
err(EXIT_FAILURE, "uname");
printf("uts.nodename in parent: %s\n", uts.nodename);
if (waitpid(pid, NULL, 0) == -1) /* انتظر الابن */
err(EXIT_FAILURE, "waitpid");
printf("child has terminated\n");
exit(EXIT_SUCCESS);
}
انظر أيضًا¶
fork(2), futex(2), getpid(2), gettid(2), kcmp(2), mmap(2), pidfd_open(2), set_thread_area(2), set_tid_address(2), setns(2), tkill(2), unshare(2), wait(2), capabilities(7), namespaces(7), pthreads(7)
ترجمة¶
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>
هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.
إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.
| 8 فبراير 2026 | صفحات دليل لينكس 6.18 |