cpuset(7)

الاسم
cpuset - حصر العمليات في مجموعات فرعية من المعالجات وعقد الذاكرة

الوصف
واجهة نظام ملفات cpuset هي واجهة نظام ملفات وهمي لآلية cpuset في النواة، والتي تُستخدم للتحكم في موضع المعالج وموضع الذاكرة للعمليات. تُوصَل عادةً عند /dev/cpuset.

في الأنظمة ذات النوى المجمعة بدعم مدمج لـ cpusets، تُربط جميع العمليات بـ cpuset، وتكون cpusets موجودة دائمًا. إذا كان النظام يدعم cpusets، فسيكون لديه المدخل nodev cpuset في الملف /proc/filesystems. من خلال وصل نظام ملفات cpuset (انظر قسم EXAMPLES أدناه)، يمكن للمسؤول ضبط cpusets على النظام للتحكم في موضع المعالج والذاكرة للعمليات على ذلك النظام. مبدئيًا، إذا لم يُعدل ضبط cpuset على النظام أو إذا لم يُوصل نظام ملفات cpuset أصلاً، فإن آلية cpuset، رغم وجودها، ليس لها أي تأثير على سلوك النظام.

تُعرف cpuset قائمة من وحدات المعالجة المركزية (CPUs) وعقد الذاكرة.

تشمل وحدات المعالجة المركزية للنظام جميع وحدات المعالجة المنطقية التي يمكن للعملية التنفيذ عليها، بما في ذلك، إذا وجدت، نوى معالجة متعددة داخل حزمة واحدة وخيوط فرائقة (Hyper-Threads) داخل نواة المعالج. تشمل عقد الذاكرة جميع بنوك الذاكرة الرئيسة المتمايزة؛ عادةً ما تحتوي الأنظمة الصغيرة وأنظمة SMP على عقدة ذاكرة واحدة فقط تحتوي على كامل الذاكرة الرئيسة للنظام، بينما تحتوي أنظمة NUMA (الوصول غير الموحد للذاكرة) على عقد ذاكرة متعددة.

تُمثل cpusets كأدلة في نظام ملفات وهمي هرمي، حيث يمثل الدليل الرئيس في الهرم (/dev/cpuset) النظام بأكمله (جميع وحدات المعالجة المركزية وعقد الذاكرة المتصلة) وأي cpuset تكون ابنة (سليلة) لـ cpuset أب أخرى تحتوي على مجموعة فرعية من وحدات المعالجة والذاكرة الخاصة بذلك الأب. للأدلة والملفات التي تمثل cpusets أذونات نظام ملفات عادية.

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

عندما يبدأ النظام بالإقلاع، تُعرف cpuset واحدة تشمل جميع وحدات المعالجة وعقد الذاكرة في النظام، وتكون جميع العمليات في تلك cpuset. خلال عملية الإقلاع، أو لاحقًا خلال تشغيل النظام العادي، يمكن إنشاء cpusets أخرى، كأدلة فرعية من هذه الـ cpuset الرئيسة، تحت تحكم مسؤول النظام، ويمكن وضع العمليات في هذه الـ cpusets الأخرى.

تُدمج cpusets مع آلية تقارب الجدولة sched_setaffinity(2) وآليات موضع الذاكرة mbind(2) و set_mempolicy(2) في النواة. لا تسمح أي من هذه الآليات للعملية باستخدام وحدة معالجة أو عقدة ذاكرة غير مسموح بها بواسطة cpuset الخاصة بتلك العملية. إذا تعارضت التغييرات في موضع cpuset لعملية ما مع هذه الآليات الأخرى، فسيُفرض موضع cpuset حتى لو كان ذلك يعني تجاوز هذه الآليات الأخرى. تحقق النواة هذا التجاوز من خلال تقييد وحدات المعالجة وعقد الذاكرة المطلوبة بواسطة هذه الآليات الأخرى بصمت لتقتصر على تلك المسموح بها بواسطة cpuset العملية المستدعية. يمكن أن يؤدي هذا إلى إرجاع هذه الاستدعاءات الأخرى لخطأ، إذا انتهى مثل هذا الاستدعاء بطلب مجموعة فارغة من وحدات المعالجة أو عقد الذاكرة، بعد تقييد الطلب بـ cpuset العملية المستدعية.

عادةً، تُستخدم cpuset لإدارة حصر وحدة المعالجة وعقدة الذاكرة لمجموعة من العمليات المتعاونة، مثل وظيفة مجدول دفعات، وتُستخدم هذه الآليات الأخرى لإدارة موضع العمليات الفردية أو مناطق الذاكرة داخل تلك المجموعة أو الوظيفة.

الملفات
يمثل كل دليل تحت /dev/cpuset مجموعة cpuset ويحتوي على مجموعة ثابتة من الملفات الوهمية التي تصف حالة تلك الـ cpuset.

تُنشأ مجموعات cpuset جديدة باستخدام استدعاء النظام mkdir(2) أو الأمر mkdir(1). تُستعلم خصائص cpuset وتُعدل، مثل أعلامها، ووحدات المعالجة وعقد الذاكرة المسموح بها، والعمليات المرتبطة بها، من خلال القراءة أو الكتابة في الملف المناسب في دليل cpuset تلك، كما هو موضح أدناه.

تُنشأ الملفات الوهمية في كل دليل cpuset آليًا عند إنشاء الـ cpuset، كنتيجة لاستدعاء mkdir(2). لا يمكن إضافة أو إزالة هذه الملفات الوهمية مباشرة.

يمكن إزالة دليل cpuset الذي لا يحتوي على أدلة cpuset ابنة، وليس له عمليات مرتبطة به، باستخدام rmdir(2) أو rmdir(1). ليس من الضروري، أو الممكن، إزالة الملفات الوهمية داخل الدليل قبل إزالته.

الملفات الوهمية في كل دليل cpuset هي ملفات نصية صغيرة يمكن قراءتها وكتابتها باستخدام أدوات الصدفة التقليدية مثل cat(1) و echo(1)، أو من برنامج باستخدام وظائف مكتبة إدخال/إخراج الملفات أو استدعاءات النظام، مثل open(2) و read(2) و write(2) و close(2).

تمثل الملفات الوهمية في دليل cpuset حالة النواة الداخلية وليس لها أي صورة مستمرة على القرص. كل من هذه الملفات الخاصة بكل cpuset مدرج وموصوف أدناه.

tasks
    قائمة بمعرفات العمليات (PIDs) للعمليات في تلك الـ cpuset. تُنسق القائمة كسلسلة من الأرقام العشرية بترميز ASCII، يتبع كل منها سطر جديد. يمكن إضافة عملية إلى cpuset (وإزالتها آليًا من الـ cpuset التي كانت تحتوي عليها سابقًا) عن طريق كتابة معرفها (PID) في ملف tasks الخاص بتلك الـ cpuset (مع أو بدون سطر جديد في النهاية).

    تحذير: يمكن كتابة معرف عملية (PID) واحد فقط في ملف tasks في المرة الواحدة. إذا كُتبت سلسلة تحتوي على أكثر من معرف واحد، فسيُستخدم الأول فقط.

notify_on_release
    علم (0 أو 1). إذا ضُبط على (1)، فستتلقى cpuset تلك معاملة خاصة بعد إطلاقها، أي بعد أن تكف جميع العمليات عن استخدامها (أي تنتهي أو تُنقل إلى cpuset مختلفة) وتُزال جميع أدلة cpuset الابنة. انظر قسم Notify On Release أدناه.

cpuset.cpus
    قائمة بالأرقام المادية لوحدات المعالجة المركزية التي يُسمح للعمليات في تلك الـ cpuset بالتنفيذ عليها. انظر List Format أدناه للحصول على وصف لتنسيق cpus.

    يمكن تغيير وحدات المعالجة المسموح بها لـ cpuset عن طريق كتابة قائمة جديدة في ملف cpus الخاص بها.

cpuset.cpu_exclusive
    علم (0 أو 1). إذا ضُبط على (1)، يكون لـ cpuset استخدام حصري لوحدات المعالجة الخاصة بها (لا يجوز لأي cpuset شقيقة أو ابنة عم تداخل وحدات المعالجة). مبدئيًا، يكون هذا متوقفًا (0). كما أن cpusets المنشأة حديثًا تضبط هذا مبدئيًا على الإيقاف (0).

    تكون مجموعتا cpuset شقيقتين إذا تشاركتا نفس الـ cpuset الأب في هيكل /dev/cpuset. وتكون مجموعتا cpuset أبناء عمومة إذا لم تكن إحداهما سلفًا للأخرى. بغض النظر عن ضبط cpu_exclusive، إذا كانت إحدى الـ cpuset سلفًا للأخرى، وإذا كان لكلتا الـ cpusets قيم cpus غير فارغة، فيجب أن تتداخل cpus الخاصة بهما، لأن cpus لأي cpuset هي دائمًا مجموعة فرعية من cpus الخاصة بالـ cpuset الأب.

cpuset.mems
    قائمة بعقد الذاكرة التي يُسمح للعمليات في هذه الـ cpuset بتخصيص الذاكرة عليها. انظر List Format أدناه للحصول على وصف لتنسيق mems.

cpuset.mem_exclusive
    علم (0 أو 1). إذا ضُبط على (1)، يكون لـ cpuset استخدام حصري لعقد الذاكرة الخاصة بها (لا يجوز لأي شقيق أو ابن عم التداخل). وأيضًا إذا ضُبط على (1)، تكون الـ cpuset هي cpuset من نوع Hardwall (انظر أدناه). مبدئيًا، يكون هذا متوقفًا (0). كما أن cpusets المنشأة حديثًا تضبط هذا مبدئيًا على الإيقاف (0).

    بغض النظر عن ضبط mem_exclusive، إذا كانت إحدى الـ cpuset سلفًا للأخرى، فيجب أن تتداخل عقد الذاكرة الخاصة بهما، لأن عقد الذاكرة لأي cpuset هي دائمًا مجموعة فرعية من عقد الذاكرة للـ cpuset الأب.

cpuset.mem_hardwall (منذ لينكس 2.6.26)
    علم (0 أو 1). إذا ضُبط على (1)، تكون الـ cpuset هي cpuset من نوع Hardwall (انظر أدناه). على عكس mem_exclusive، لا يوجد قيود على ما إذا كان بإمكان cpusets الملحوظة بـ mem_hardwall الحصول على عقد ذاكرة متداخلة مع cpusets الشقيقة أو أبناء العمومة. مبدئيًا، يكون هذا متوقفًا (0). كما أن cpusets المنشأة حديثًا تضبط هذا مبدئيًا على الإيقاف (0).

cpuset.memory_migrate (منذ لينكس 2.6.16)
    علم (0 أو 1). إذا ضُبط على (1)، يُمكّن نقل الذاكرة. مبدئيًا، يكون هذا متوقفًا (0). انظر قسم Memory Migration أدناه.

cpuset.memory_pressure (منذ لينكس 2.6.16)
    مقياس لمقدار ضغط الذاكرة الذي تسببه العمليات في هذه الـ cpuset. انظر قسم Memory Pressure أدناه. ما لم يُفعل memory_pressure_enabled، تكون القيمة دائمًا صفرًا (0). هذا الملف للقراءة فقط. انظر قسم WARNINGS أدناه.

cpuset.memory_pressure_enabled (منذ لينكس 2.6.16)
    علم (0 أو 1). يتوفر هذا الملف في الـ cpuset الرئيسة فقط، وعادةً ما يكون /dev/cpuset. إذا ضُبط على (1)، تُفعل حسابات memory_pressure لجميع cpusets في النظام. مبدئيًا، يكون هذا متوقفًا (0). انظر قسم Memory Pressure أدناه.

cpuset.memory_spread_page (منذ لينكس 2.6.17)
    علم (0 أو 1). إذا ضُبط على (1)، تُوزع الصفحات في خبيئة صفحات النواة (درئات نظام الملفات) بشكل موحد عبر الـ cpuset. مبدئيًا، يكون هذا متوقفًا (0) في الـ cpuset الرئيسة، ويُورث من الـ cpuset الأب في الـ cpusets المنشأة حديثًا. انظر قسم Memory Spread أدناه.

cpuset.memory_spread_slab (منذ لينكس 2.6.17)
    علم (0 أو 1). إذا ضُبط على (1)، تُوزع خبيئات الشرائح (slab caches) في النواة لمدخلات/مخرجات الملفات (بنى الأدلة والـ inodes) بشكل موحد عبر الـ cpuset. مبدئيًا، يكون هذا متوقفًا (0) في الـ cpuset الرئيسة، ويُورث من الـ cpuset الأب في الـ cpusets المنشأة حديثًا. انظر قسم Memory Spread أدناه.

cpuset.sched_load_balance (منذ لينكس 2.6.24)
    علم (0 أو 1). إذا ضُبط على (1، وهو المبدئي) ستقوم النواة آليًا بموازنة حمل العمليات في تلك الـ cpuset عبر وحدات المعالجة المسموح بها فيها. إذا مُسح (0)، ستتجنب النواة موازنة حمل العمليات في هذه الـ cpuset، ما لم تكن هناك cpuset أخرى بوحدات معالجة متداخلة ضُبط فيها علم sched_load_balance. انظر Scheduler Load Balancing أدناه لمزيد من التفاصيل.

cpuset.sched_relax_domain_level (منذ لينكس 2.6.26)
    عدد صحيح، بين -1 وقيمة موجبة صغيرة. يتحكم sched_relax_domain_level في عرض نطاق وحدات المعالجة التي يقوم مجدول النواة عبرها بإعادة توازن فوري للمهام القابلة للتنفيذ. إذا عُطل sched_load_balance، فإن ضبط sched_relax_domain_level لا يهم، حيث لا يتم إجراء موازنة حمل. أما إذا فُعل sched_load_balance، فكلما زادت قيمة sched_relax_domain_level، اتسع نطاق وحدات المعالجة التي تُحاول موازنة الحمل الفورية عبرها. انظر Scheduler Relax Domain Level أدناه لمزيد من التفاصيل.

بالإضافة إلى الملفات الوهمية المذكورة أعلاه في كل دليل تحت /dev/cpuset، لكل عملية ملف وهمي، /proc/pid/cpuset، يعرض مسار دليل cpuset الخاص بالعملية بالنسبة إلى جذر نظام ملفات cpuset.

أيضًا يحتوي ملف /proc/pid/status لكل عملية على أربعة أسطر مضافة، تعرض Cpus_allowed للعملية (وحدات المعالجة التي يمكن جدولتها عليها) و Mems_allowed (عقد الذاكرة التي يمكنها الحصول على ذاكرة منها)، بالتنسيقين Mask Format و List Format (انظر أدناه) كما هو موضح في المثال التالي:

    Cpus_allowed:   ffffffff,ffffffff,ffffffff,ffffffff
    Cpus_allowed_list:     0-127
    Mems_allowed:   ffffffff,ffffffff
    Mems_allowed_list:     0-63

أُضيفت حقول "allowed" في لينكس 2.6.24؛ وأُضيفت حقول "allowed_list" في لينكس 2.6.26.

القدرات الممتدة
بالإضافة إلى التحكم في أي cpus و mems يُسمح للعملية باستخدامها، توفر cpusets القدرات الممتدة التالية.

  مجموعات cpuset الحصرية
إذا لُحظت cpuset بـ cpu_exclusive أو mem_exclusive، فلا يجوز لأي cpuset أخرى، باستثناء السلف المباشر أو السليل، مشاركة أي من وحدات المعالجة أو عقد الذاكرة نفسها.

تقيد cpuset التي تكون mem_exclusive تخصيصات النواة لصفحات خبيئة الدرئ وصفحات بيانات النواة الداخلية الأخرى التي تتشاركها النواة عادةً عبر مستخدمين متعددين. تقيد جميع الـ cpusets، سواء كانت mem_exclusive أم لا، تخصيصات الذاكرة لمساحة المستخدم. وهذا يتيح ضبط النظام بحيث يمكن لعدة وظائف مستقلة مشاركة بيانات النواة المشتركة، مع عزل تخصيص المستخدم لكل وظيفة في الـ cpuset الخاصة بها. للقيام بذلك، أنشئ cpuset كبيرة من نوع mem_exclusive لتحتوي على جميع الوظائف، وأنشئ cpusets ابنة غير mem_exclusive لكل وظيفة فردية. يُسمح فقط بوضع كمية صغيرة من ذاكرة النواة، مثل طلبات معالجات المقاطعة، على عقد ذاكرة خارج حتى cpuset من نوع mem_exclusive.

  Hardwall
تعد الـ cpuset التي ضُبط فيها mem_exclusive أو mem_hardwall هي cpuset من نوع hardwall. تقيد cpuset الـ hardwall تخصيصات النواة للصفحات، والدرئ، والبيانات الأخرى التي تتشاركها النواة عادةً عبر مستخدمين متعددين. تقيد جميع الـ cpusets، سواء كانت hardwall أم لا، تخصيصات الذاكرة لمساحة المستخدم.

وهذا يتيح ضبط النظام بحيث يمكن لعدة وظائف مستقلة مشاركة بيانات النواة المشتركة، مثل صفحات نظام الملفات، مع عزل تخصيص المستخدم لكل وظيفة في الـ cpuset الخاصة بها. للقيام بذلك، أنشئ cpuset كبيرة من نوع hardwall لتحتوي على جميع الوظائف، وأنشئ cpusets ابنة لكل وظيفة فردية ليست من نوع hardwall.

يُسمح فقط بأخذ كمية صغيرة من ذاكرة النواة، مثل طلبات معالجات المقاطعة، خارج حتى الـ cpuset من نوع hardwall.

  الإشعار عند الإطلاق
إذا فُعل علم notify_on_release (1) في cpuset، فعندما تغادر آخر عملية في الـ cpuset (تخرج أو ترتبط بـ cpuset أخرى) وتُزال آخر cpuset ابنة لها، فستقوم النواة بتشغيل الأمر /sbin/cpuset_release_agent، مع تزويده باسم المسار (بالنسبة لنقطة وصل نظام ملفات cpuset) للـ cpuset المتروكة. وهذا يتيح الإزالة الآلية لـ cpusets المتروكة.

القيمة المبدئية لـ notify_on_release في الـ cpuset الرئيسة عند إقلاع النظام هي التعطيل (0). والقيمة المبدئية للـ cpusets الأخرى عند الإنشاء هي القيمة الحالية لضبط notify_on_release الخاص بالأب.

يُستدعى الأمر /sbin/cpuset_release_agent، مع اسم الـ cpuset المراد إطلاقها (المسار النسبي لـ /dev/cpuset) في argv[1].

المحتويات المعتادة للأمر /sbin/cpuset_release_agent هي ببساطة نص صدفة برمجى:

    #!/bin/sh
    rmdir /dev/cpuset/$1

كما هو الحال مع قيم الأعلام الأخرى أدناه، يمكن تغيير هذا العلم عن طريق كتابة رقم ASCII‏ 0 أو 1 (مع سطر جديد اختياري في النهاية) في الملف، لمسح أو ضبط العلم، على التوالي.

  ضغط الذاكرة
يوفر memory_pressure للـ cpuset متوسطًا متحركًا بسيطًا لكل cpuset لمعدل محاولة العمليات فيها لتحرير الذاكرة المستخدمة على عقد الذاكرة الخاصة بها لتلبية طلبات ذاكرة إضافية.

وهذا يتيح لمديري الدفعات الذين يراقبون الوظائف التي تعمل في cpusets مخصصة اكتشاف مستوى ضغط الذاكرة الذي تسببه تلك الوظيفة بكفاءة.

هذا مفيد في الأنظمة المدارة بدقة التي تشغل مزيجًا واسعًا من الوظائف المقدمة، والتي قد تختار إنهاء أو إعادة ترتيب أولويات الوظائف التي تحاول استخدام ذاكرة أكثر مما هو مسموح به في العقد المخصصة لها، ومع وظائف الحوسبة العلمية المتوازية بشكل كبير، والطويلة الأمد، والمترابطة بإحكام والتي ستفشل بشكل كبير في تحقيق أهداف الأداء المطلوبة إذا بدأت في استخدام ذاكرة أكثر مما هو مسموح لها.

توفر هذه الآلية طريقة اقتصادية للغاية لمدير الدفعات لمراقبة cpuset بحثًا عن علامات ضغط الذاكرة. والأمر متروك لمدير الدفعات أو كود المستخدم الآخر لقرر الإجراء الذي يجب اتخاذه إذا اكتشف علامات ضغط الذاكرة.

ما لم يُفعل حساب ضغط الذاكرة عن طريق ضبط الملف الوهمي /dev/cpuset/cpuset.memory_pressure_enabled، فلن يُحسب لأي cpuset، وستعيد القراءات من أي memory_pressure دائمًا صفرًا، كما هو متمثل بالسلسلة "0\n" بترميز ASCII. انظر قسم WARNINGS أدناه.

يُستخدم المتوسط المتحرك لكل cpuset للأسباب التالية:

•   نظرًا لأن هذا المقياس هو لكل cpuset بدلاً من أن يكون لكل عملية أو لكل منطقة ذاكرة وهمية، فإن حمل النظام الذي يفرضه مجدول الدفعات الذي يراقب هذا المقياس ينخفض بشكل كبير في الأنظمة الكبيرة، لأنه يمكن تجنب فحص قائمة المهام عند كل مجموعة من الاستعلامات.

•   نظرًا لأن هذا المقياس هو متوسط متحرك وليس عدادًا تراكميًا، يمكن لمجدول الدفعات اكتشاف ضغط الذاكرة بقراءة واحدة، بدلاً من الاضطرار إلى القراءة وتراكم النتائج لفترة من الزمن.

•   نظرًا لأن هذا المقياس هو لكل cpuset بدلاً من أن يكون لكل عملية، يمكن لمجدول الدفعات الحصول على المعلومات الرئيسية - ضغط الذاكرة في cpuset - بقراءة واحدة، بدلاً من الاضطرار إلى الاستعلام وتراكم النتائج عبر جميع مجموعة العمليات (المتغيرة ديناميكيًا) في الـ cpuset.

يُحسب memory_pressure لـ cpuset باستخدام مرشح رقمي بسيط لكل cpuset يُحتفظ به داخل النواة. لكل cpuset، يتتبع هذا المرشح المعدل الأخير الذي تدخل فيه العمليات المرتبطة بتلك الـ cpuset في كود استرداد النواة المباشر.

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

يوفر ملف cpuset.memory_pressure عددًا صحيحًا يمثل المعدل الأخير (عمر نصف قدره 10 ثوانٍ) لمرات الدخول إلى كود الاسترداد المباشر الذي تسببت فيه أي عملية في الـ cpuset، بوحدات محاولات الاسترداد في الثانية، مضروبة في 1000.

  نشر الذاكرة
يوجد ملفا أعلام منطقية لكل cpuset يتحكمان في مكان تخصيص النواة للصفحات لدرئات نظام الملفات وبنى بيانات النواة ذات الصلة. يُطلق عليهما cpuset.memory_spread_page و cpuset.memory_spread_slab.

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

إذا ضُبط ملف العلم المنطقي cpuset.memory_spread_slab لكل cpuset، فستقوم النواة بنشر بعض خبيئات الشرائح (slab caches) المتعلقة بنظام الملفات، مثل تلك الخاصة بالـ inodes ومدخلات الأدلة، بالتساوي عبر جميع العقد التي يُسمح للعملية المسببة للخطأ باستخدامها، بدلاً من تفضيل وضع تلك الصفحات في العقدة التي تعمل فيها العملية.

لا يؤثر ضبط هذه الأعلام على مقطع البيانات (انظر brk(2)) أو صفحات مقطع المكدس للعملية.

مبدئيًا، كلا النوعين من نشر الذاكرة متوقفان وتفضل النواة تخصيص صفحات الذاكرة في العقدة المحلية لمكان تشغيل العملية الطالبة. إذا لم تكن تلك العقدة مسموحًا بها بموجب سياسة ذاكرة NUMA للعملية أو ضبط cpuset أو إذا لم تكن هناك صفحات ذاكرة خالية كافية في تلك العقدة، فستبحث النواة عن أقرب عقدة مسموح بها وبها ذاكرة خالية كافية.

عند إنشاء cpusets جديدة، فإنها ترث إعدادات نشر الذاكرة من أبيها.

يؤدي ضبط نشر الذاكرة إلى جعل تخصيصات صفحات الذاكرة أو خبيئات الشرائح المتأثرة تتجاهل سياسة ذاكرة NUMA للعملية وتُنشر بدلاً من ذلك. ومع ذلك، فإن تأثير هذه التغييرات في موضع الذاكرة الناتج عن نشر الذاكرة المحدد بواسطة cpuset يكون مخفيًا عن استدعاءات mbind(2) أو set_mempolicy(2). تظهر هاتان الاستدعاءتان لسياسة ذاكرة NUMA دائمًا وكأنه لا يوجد نشر ذاكرة محدد بواسطة cpuset، حتى لو كان مفعلًا. وإذا أُوقف نشر ذاكرة cpuset لاحقًا، تُطبق سياسة ذاكرة NUMA المحددة مؤخرًا بواسطة هذه الاستدعاءات آليًا.

كلا من cpuset.memory_spread_page و cpuset.memory_spread_slab هما ملفا أعلام منطقية. مبدئيًا، يحتويان على "0"، مما يعني أن الميزة معطلة لتلك الـ cpuset. إذا كُتب "1" في هذا الملف، فسيتم تفعيل الميزة المذكورة.

يتصرف نشر الذاكرة المحدد بواسطة cpuset بشكل مشابه لما يُعرف (في سياقات أخرى) باسم موضع الذاكرة الدوري (round-robin) أو المتشابك (interleave).

يمكن أن يوفر نشر الذاكرة المحدد بواسطة cpuset تحسينات جوهرية في الأداء للوظائف التي:

•   الحاجة إلى وضع البيانات المحلية للخيوط (thread-local) على عقد ذاكرة قريبة من وحدات المعالجة المركزية التي تشغل الخيوط الأكثر وصولاً لهذه البيانات؛ ولكن أيضًا

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

بدون هذه السياسة، قد يصبح تخصيص الذاكرة عبر العقد في مجموعة معالجات المهمة غير متساوٍ للغاية، لا سيما في المهام التي قد تحتوي على خيط واحد فقط يهيئ أو يقرأ مجموعة البيانات.

  هجرة الذاكرة
عادةً، في ظل الإعداد المبدئي (المعطل) لـ cpuset.memory_migrate، بمجرد تخصيص صفحة (إعطائها صفحة فيزيائية من الذاكرة الرئيسية)، تظل تلك الصفحة في أي عقدة خُصصت فيها طالما بقيت مخصصة، حتى لو تغيرت سياسة وضع الذاكرة mems لمجموعة المعالجات لاحقًا.

عند تفعيل هجرة الذاكرة في مجموعة معالجات، إذا تغير إعداد mems للمجموعة، فإن أي صفحة ذاكرة مستخدمة من قبل أي عملية في المجموعة موجودة على عقدة ذاكرة لم تعد مسموحة ستُنقل إلى عقدة ذاكرة مسموحة.

علاوة على ذلك، إذا نُقلت عملية إلى مجموعة معالجات مفعّل فيها memory_migrate، فإن أي صفحات ذاكرة تستخدمها كانت على عقد ذاكرة مسموحة في مجموعتها السابقة، ولكنها غير مسموحة في مجموعتها الجديدة، ستُنقل إلى عقدة ذاكرة مسموحة في المجموعة الجديدة.

يُحفظ الموضع النسبي للصفحة المنقولة داخل مجموعة المعالجات أثناء عمليات الهجرة هذه قدر الإمكان. على سبيل المثال، إذا كانت الصفحة في العقدة الصالحة الثانية لمجموعة المعالجات السابقة، فستوضع الصفحة في العقدة الصالحة الثانية للمجموعة الجديدة، إن أمكن.

  موازنة حمل المجدول
يقوم مجدول النواة بموازنة حمل العمليات آليًا. إذا كانت إحدى وحدات المعالجة المركزية غير مستغلة كفاية، فستبحث النواة عن عمليات في وحدات معالجة أخرى أكثر حملاً وتنقل تلك العمليات إلى الوحدة غير المستغلة، ضمن قيود آليات الموضع مثل مجموعات المعالجات و sched_setaffinity(2).

تزداد التكلفة الخوارزمية لموازنة الحمل وتأثيرها على هياكل بيانات النواة المشتركة الرئيسة (مثل قائمة العمليات) بشكل أكبر من الخطي مع زيادة عدد وحدات المعالجة المركزية التي تجري موازنتها. على سبيل المثال، تكلف موازنة الحمل عبر مجموعة واحدة كبيرة من المعالجات أكثر مما تكلفه الموازنة عبر مجموعتين أصغر، حجم كل منهما نصف المجموعة الكبيرة. (تعتمد العلاقة الدقيقة بين عدد المعالجات وتكلفة موازنة الحمل على تفاصيل تنفيذ مجدول عمليات النواة، والتي تخضع للتغيير بمرور الوقت مع تنفيذ خوارزميات مجدول محسنة).

يوفر علم sched_load_balance الخاص بكل مجموعة معالجات آلية لكبح موازنة حمل المجدول الآلية هذه في الحالات التي لا تكون فيها ضرورية وحيث يحقق كبحها فوائد أداء قيمة.

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

موازنة الحمل المبدئية هذه عبر جميع المعالجات لا تناسب الحالتين التاليتين:

•   في الأنظمة الكبيرة، تكون موازنة الحمل عبر العديد من المعالجات مكلفة. إذا كان النظام يُدار باستخدام مجموعات معالجات لوضع مهام مستقلة على مجموعات منفصلة من المعالجات، فإن موازنة الحمل الكاملة تصبح غير ضرورية.

•   الأنظمة التي تدعم الزمن الحقيقي (real-time) على بعض المعالجات تحتاج إلى تقليل الأعباء الإضافية للنظام على تلك المعالجات، بما في ذلك تجنب موازنة حمل العمليات إذا لم تكن هناك حاجة إليها.

عند تفعيل علم sched_load_balance (الإعداد المبدئي)، فإنه يطلب موازنة الحمل عبر جميع وحدات المعالجة المركزية المسموح بها في تلك المجموعة، مما يضمن أن موازنة الحمل يمكنها نقل عملية (غير مقيدة بطريقة أخرى، كما في sched_setaffinity(2)) من أي معالج في تلك المجموعة إلى أي معالج آخر فيها.

عند تعطيل علم sched_load_balance، سيتجنب المجدول موازنة الحمل عبر المعالجات في تلك المجموعة، إلا بالقدر الضروري بسبب وجود مجموعة معالجات متداخلة مفعّل فيها sched_load_balance.

لذا، على سبيل المثال، إذا كان لمجموعة المعالجات العليا علم sched_load_balance مفعلاً، فسيقوم المجدول بموازنة الحمل عبر جميع المعالجات، ولن يكون لإعداد علم sched_load_balance في مجموعات المعالجات الأخرى أي تأثير، لأننا نقوم بالفعل بموازنة حمل كاملة.

وبناءً عليه، في الحالتين المذكورتين أعلاه، ينبغي تعطيل علم sched_load_balance في المجموعة العليا، وتفعيله فقط في بعض المجموعات الفرعية الأصغر.

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

بالطبع، يمكن ترك العمليات المقيدة بمعالج معين في مجموعة معالجات تعطل sched_load_balance لأن تلك العمليات لن تذهب إلى أي مكان آخر على أي حال.

  مستوى استرخاء نطاق المجدول
يقوم مجدول النواة بموازنة حمل فورية كلما أصبح المعالج متاحاً أو أصبحت مهمة أخرى قابلة للتشغيل. تعمل موازنة الحمل هذه لضمان توظيف أكبر عدد ممكن من المعالجات بشكل مفيد في تشغيل المهام. كما تقوم النواة بموازنة حمل دورية بناءً على ساعة البرمجيات الموصوفة في time(7). ينطبق إعداد sched_relax_domain_level فقط على موازنة الحمل الفورية. وبغض النظر عن إعداد sched_relax_domain_level، تُحاول موازنة الحمل الدورية عبر جميع المعالجات (ما لم تُعطل عن طريق إيقاف sched_load_balance). وفي كل الأحوال، ستُجدول المهام للعمل فقط على المعالجات المسموح بها من قبل مجموعة معالجاتها، كما تُعدلها نداءات النظام sched_setaffinity(2).

في الأنظمة الصغيرة، مثل تلك التي تحتوي على عدد قليل من المعالجات، تكون موازنة الحمل الفورية مفيدة لتحسين تفاعلية النظام وتقليل دورات المعالج الضائعة في وضع الخمول. ولكن في الأنظمة الكبيرة، قد يكون محاولة موازنة الحمل الفورية عبر عدد كبير من المعالجات أكثر تكلفة مما تستحق، اعتماداً على خصائص الأداء الخاصة بمزيج المهام والعتاد.

سيعتمد المعنى الدقيق لقيم الأعداد الصحيحة الصغيرة لـ sched_relax_domain_level على تفاصيل التنفيذ الداخلية لكود مجدول النواة وعلى بنية العتاد غير الموحدة. وكلاهما سيتطور بمرور الوقت ويختلف باختلاف بنية النظام وإصدار النواة.

حتى وقت كتابة هذا، عندما قُدمت هذه الميزة في لينكس 2.6.26، كان للقيم الموجبة لـ sched_relax_domain_level في بعض البنى الشهيرة المعاني التالية.

1   إجراء موازنة حمل فورية عبر أشقاء خيوط المعالجة الفائقة (Hyper-Thread) في نفس النواة.
2   إجراء موازنة حمل فورية عبر النوى الأخرى في نفس الحزمة (package).
3   إجراء موازنة حمل فورية عبر المعالجات الأخرى في نفس العقدة أو النصلة (blade).
4   إجراء موازنة حمل فورية عبر عدة عقد (تفصيل تنفيذي) [في أنظمة NUMA].
5   إجراء موازنة حمل فورية عبر جميع المعالجات في النظام [في أنظمة NUMA].

تعني قيمة الصفر (0) لـ sched_relax_domain_level دائماً عدم إجراء موازنة حمل فورية، وبالتالي تُجرى موازنة الحمل بشكل دوري فقط، وليس فوراً عندما يصبح المعالج متاحاً أو تصبح مهمة أخرى قابلة للتشغيل.

تعني قيمة ناقص واحد (-1) لـ sched_relax_domain_level دائماً استخدام القيمة المبدئية للنظام. يمكن أن تختلف القيمة المبدئية للنظام باختلاف البنية وإصدار النواة. يمكن تغيير هذه القيمة المبدئية عن طريق معطى وقت إقلاع النواة "relax_domain_level=".

في حالة وجود مجموعات معالجات متعددة متداخلة لها قيم sched_relax_domain_level متعارضة، فإن القيمة الأعلى هي التي تُطبق على جميع المعالجات في أي من المجموعات المتداخلة. وفي مثل هذه الحالات، تُعتبر -1 هي القيمة الأدنى، وتُلغى بأي قيمة أخرى، و 0 هي القيمة الأدنى التالية.

التنسيقات
تُستخدم التنسيقات التالية لتمثيل مجموعات وحدات المعالجة المركزية وعقد الذاكرة.

  تنسيق القناع
يُستخدم تنسيق القناع لتمثيل أقنعة البتات لمعالجات وعقد الذاكرة في ملف /proc/pid/status.

يعرض هذا التنسيق كل كلمة بطول 32 بت بالنظام الست عشري (باستخدام محارف ASCII "0" - "9" و "a" - "f")؛ وتُملأ الكلمات بأصفار بادئة عند الحاجة. وبالنسبة للأقنعة التي تزيد عن كلمة واحدة، تُستخدم الفاصلة للفصل بين الكلمات. تُعرض الكلمات بترتيب الـ big-endian، حيث تظهر البتات الأكثر أهمية أولاً. والأرقام الست عشرية داخل الكلمة تكون أيضاً بترتيب big-endian.

عدد الكلمات ذات الـ 32 بت المعروضة هو الحد الأدنى اللازم لعرض جميع بتات قناع البتات، بناءً على حجم القناع.

Examples of the Mask Format:

    00000001                        # تعيين البت 0 فقط
    40000000,00000000,00000000      # تعيين البت 94 فقط
    00000001,00000000,00000000      # تعيين البت 64 فقط
    000000ff,00000000               # تعيين البتات 32-39
    00000000,000e3862               # تعيين 1، 5، 6، 11-13، 17-19

قناع بتات بتعيين البتات 0، 1، 2، 4، 8، 16، 32، و 64 يظهر كالتالي:

    00000001,00000001,00010117

الرقم "1" الأول للبت 64، والثاني للبت 32، والثالث للبت 16، والرابع للبت 8، والخامس للبت 4، والرقم "7" للبتات 2 و 1 و 0.

  تنسيق القائمة
تنسيق القائمة لـ cpus و mems هو قائمة مفصولة بفواصل لأرقام ونطاقات أرقام المعالجات أو عقد الذاكرة، بنظام ASCII العشري.

Examples of the List Format:

    0-4,9           # تعيين البتات 0، 1، 2، 3، 4، و 9
    0-2,7,12-14     # تعيين البتات 0، 1، 2، 7، 12، 13، و 14

قواعد
تنطبق القواعد التالية على كل مجموعة معالجات:

•   يجب أن تكون معالجاتها وعقد ذاكرتها مجموعة فرعية (قد تكون مساوية) من تلك الخاصة بوالدتها.

•   لا يمكن تعليمها كـ cpu_exclusive إلا إذا كانت والدتها معلمة بذلك.

•   لا يمكن تعليمها كـ mem_exclusive إلا إذا كانت والدتها معلمة بذلك.

•   إذا كانت cpu_exclusive، فلا يجوز لمعالجاتها أن تتداخل مع أي مجموعة شقيقة.

•   إذا كانت mem_exclusive، فلا يجوز لعقد ذاكرتها أن تتداخل مع أي مجموعة شقيقة.

الأذونات
تُحدد أذونات مجموعة المعالجات من خلال أذونات الأدلة والملفات الوهمية في نظام ملفات مجموعات المعالجات، والذي يُوصل عادةً عند /dev/cpuset.

على سبيل المثال، يمكن لعملية أن تضع نفسها في مجموعة معالجات أخرى (غير مجموعتها الحالية) إذا كان بإمكانها الكتابة في ملف tasks الخاص بتلك المجموعة. يتطلب هذا إذن التنفيذ على الأدلة المحيطة وإذن الكتابة على ملف tasks.

يُطبق قيد إضافي على طلبات وضع عملية أخرى في مجموعة معالجات. لا يجوز لعملية أن تربط أخرى بمجموعة معالجات إلا إذا كان لديها إذن بإرسال إشارة لتلك العملية (انظر kill(2)).

يجوز لعملية إنشاء مجموعة معالجات فرعية إذا كان بإمكانها الوصول إلى دليل مجموعة المعالجات الوالدة والكتابة فيه. ويمكنها تعديل المعالجات أو عقد الذاكرة في مجموعة ما إذا كان بإمكانها الوصول إلى دليل تلك المجموعة (أذونات التنفيذ على كل من الأدلة الأبوية) والكتابة في ملف cpus أو mems المقابل.

هناك فرق بسيط واحد بين الطريقة التي تُقيم بها هذه الأذونات والطريقة التي تُقيم بها أذونات عمليات نظام الملفات العادية. تفسر النواة أسماء المسارات النسبية بدءاً من دليل العمل الحالي للعملية. حتى لو كان المرء يعمل على ملف مجموعة معالجات، فإن المسارات النسبية تُفسر بالنسبة لدليل عمل العملية الحالي، وليس بالنسبة لمجموعة معالجات العملية الحالية. الطرق الوحيدة التي يمكن من خلالها استخدام مسارات مجموعة معالجات نسبية لمجموعة العملية الحالية هي إما أن يكون دليل عمل العملية الحالي هو نفسه دليل مجموعتها (بأن تكون قد أجرت cd أو chdir(2) إلى دليل مجموعتها تحت /dev/cpuset، وهو أمر غير معتاد) أو إذا قام كود المستخدم بتحويل مسار مجموعة المعالجات النسبي إلى مسار نظام ملفات كامل.

نظرياً، يعني هذا أن كود المستخدم يجب أن يحدد مجموعات المعالجات باستخدام مسارات مطلقة، مما يتطلب معرفة نقطة وصل نظام ملفات مجموعات المعالجات (عادةً، ولكن ليس بالضرورة، /dev/cpuset). وعملياً، فإن كل كود مستوى المستخدم الذي يعلمه المؤلف يفترض ببساطة أنه إذا وُصل نظام ملفات مجموعات المعالجات، فإنه موصول عند /dev/cpuset. علاوة على ذلك، من الممارسات الشائعة للكود المكتوب بعناية التحقق من وجود الملف الوهمي /dev/cpuset/tasks للتأكد من أن نظام الملفات الوهمي لمجموعات المعالجات موصول حالياً.

تحذيرات

  تفعيل memory_pressure
بشكل مبدئي، يحتوي ملف cpuset.memory_pressure الخاص بكل مجموعة دائماً على الصفر (0). وما لم تُفعل هذه الميزة بكتابة "1" في الملف الوهمي /dev/cpuset/cpuset.memory_pressure_enabled، فإن النواة لن تحسب memory_pressure لكل مجموعة معالجات.

  استخدام أمر echo
عند استخدام أمر echo في محث الصدفة لتغيير قيم ملفات مجموعة المعالجات، انتبه إلى أن أمر echo المدمج في بعض الصدفات لا يعرض رسالة خطأ إذا فشل نداء النظام write(2). على سبيل المثال، إذا فشل الأمر:

    echo 19 > cpuset.mems

بسبب عدم السماح بعقدة الذاكرة 19 (ربما لا يحتوي النظام الحالي على عقدة ذاكرة بهذا الرقم)، فقد لا يعرض أمر echo أي خطأ. من الأفضل استخدام الأمر الخارجي /bin/echo لتغيير إعدادات ملف مجموعة المعالجات، حيث سيعرض هذا الأمر أخطاء write(2)، كما في المثال:

    /bin/echo 19 > cpuset.mems
    /bin/echo: write error: Invalid argument

استثناءات

  موضع الذاكرة
ليست كل تخصيصات ذاكرة النظام مقيدة بمجموعات المعالجات، وذلك للأسباب التالية.

إذا استخدمت ميزة التوصيل السريع (hot-plug) لإزالة جميع المعالجات المخصصة حالياً لمجموعة ما، فستقوم النواة آلياً بتحديث cpus_allowed لجميع العمليات المرتبطة بالمعالجات في تلك المجموعة للسماح بجميع المعالجات. وعندما تتوفر ميزة التوصيل السريع للذاكرة لإزالة عقد الذاكرة، يُتوقع تطبيق استثناء مماثل هناك أيضاً. وبشكل عام، تفضل النواة خرق قيود موضع مجموعة المعالجات بدلاً من تجويع عملية أُخرجت جميع معالجاتها أو عقد ذاكرتها المسموحة عن الخدمة (offline). يجب على كود المستخدم إعادة ضبط مجموعات المعالجات للإشارة فقط إلى المعالجات وعقد الذاكرة المتصلة عند استخدام التوصيل السريع لإضافة أو إزالة مثل هذه الموارد.

هناك بضع طلبات تخصيص ذاكرة داخلية حرجة للنواة، معلمة بـ GFP_ATOMIC، يجب توفيرها فوراً. قد تُسقط النواة بعض الطلبات أو تتعطل إذا فشل أحد هذه التخصيصات. وإذا تعذر توفير مثل هذا الطلب داخل مجموعة معالجات العملية الحالية، فإننا نرخي قيود المجموعة ونبحث عن الذاكرة في أي مكان يمكننا العثور فيه عليها. من الأفضل خرق قيود المجموعة بدلاً من إجهاد النواة.

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

  إعادة تسمية مجموعات المعالجات
يمكنك استخدام نداء النظام rename(2) لإعادة تسمية مجموعات المعالجات. يُدعم فقط إعادة التسمية البسيطة؛ أي أن تغيير اسم دليل المجموعة مسموح به، لكن نقل دليل إلى دليل مختلف غير مسموح به.

الأخطاء
يقوم تنفيذ النواة لينكس لمجموعات المعالجات بتعيين errno لتحديد سبب فشل نداء النظام الذي يؤثر على مجموعات المعالجات.

إعدادات errno المحتملة ومعانيها عند تعيينها في نداء فاشل لمجموعة معالجات هي كما هو مدرج أدناه.

E2BIG
    محاولة إجراء write(2) على ملف مجموعة معالجات خاص بطول أكبر من حد أقصى تحدده النواة لطول مثل هذه الكتابات.

EACCES
    محاولة write(2) لمعرف العملية (PID) لعملية ما في ملف tasks لمجموعة معالجات مع الافتقار للإذن بنقل تلك العملية.

EACCES
    محاولة إضافة معالج أو عقدة ذاكرة إلى مجموعة باستخدام write(2)، بينما لم يكن هذا المعالج أو تلك العقدة موجودين بالفعل في والدتها.

EACCES
    محاولة ضبط cpuset.cpu_exclusive أو cpuset.mem_exclusive باستخدام write(2) على مجموعة تفتقر والدتها لنفس الإعداد.

EACCES
    محاولة إجراء write(2) لملف cpuset.memory_pressure.

EACCES
    محاولة إنشاء ملف في دليل مجموعة معالجات.

EBUSY
    محاولة إزالة مجموعة معالجات مرتبطة بها عمليات باستخدام rmdir(2).

EBUSY
    محاولة إزالة مجموعة معالجات لها مجموعات فرعية باستخدام rmdir(2).

EBUSY
    محاولة إزالة معالج أو عقدة ذاكرة من مجموعة وهي موجودة أيضاً في مجموعة فرعية لتلك المجموعة.

EEXIST
    حُووِل إنشاء مجموعة معالجات (cpuset)، باستخدام mkdir(2)، موجودة بالفعل.

EEXIST
    حُووِل تغيير اسم rename(2) مجموعة معالجات إلى اسم موجود بالفعل.

EFAULT
    حُووِل قراءة read(2) أو كتابة write(2) ملف مجموعة معالجات باستخدام مخزن مؤقت خارج مساحة العناوين التي يمكن لعمليات الكتابة الوصول إليها.

EINVAL
    حُووِل تغيير مجموعة معالجات، باستخدام write(2)، بطريقة تنتهك سمة cpu_exclusive أو mem_exclusive لتلك المجموعة أو أي من أشقائها.

EINVAL
    حُووِل كتابة write(2) قائمة cpuset.cpus أو cpuset.mems فارغة إلى مجموعة معالجات مرتبطة بعمليات أو مجموعات معالجات فرعية.

EINVAL
    حُووِل كتابة write(2) قائمة cpuset.cpus أو cpuset.mems تتضمن نطاقًا يكون فيه الرقم الثاني أصغر من الرقم الأول.

EINVAL
    حُووِل كتابة write(2) قائمة cpuset.cpus أو cpuset.mems تتضمن محرفًا غير صالح في السلسلة النصية.

EINVAL
    حُووِل كتابة write(2) قائمة إلى ملف cpuset.cpus لا تتضمن أي معالجات متصلة (online).

EINVAL
    حُووِل كتابة write(2) قائمة إلى ملف cpuset.mems لا تتضمن أي عقد ذاكرة متصلة.

EINVAL
    حُووِل كتابة write(2) قائمة إلى ملف cpuset.mems تتضمن عقدة لا تحتوي على ذاكرة.

EIO حُووِل كتابة write(2) سلسلة نصية إلى ملف tasks الخاص بمجموعة معالجات لا تبدأ بعدد صحيح عشري بترميز ASCII.

EIO حُووِل تغيير اسم rename(2) مجموعة معالجات إلى دليل مختلف.

ENAMETOOLONG
    حُووِل قراءة read(2) ملف /proc/pid/cpuset لمسار مجموعة معالجات أطول من حجم صفحة النواة.

ENAMETOOLONG
    حُووِل إنشاء مجموعة معالجات، باستخدام mkdir(2)، يكون اسم دليلها الأساس أطول من 255 محرفًا.

ENAMETOOLONG
    حُووِل إنشاء مجموعة معالجات، باستخدام mkdir(2)، يكون مسارها الكامل، بما في ذلك بادئة نقطة الوصل (عادةً "/dev/cpuset/")، أطول من 4095 محرفًا.

ENODEV
    أُزيلت مجموعة المعالجات بواسطة عملية أخرى في نفس الوقت الذي حُووِل فيه إجراء كتابة write(2) على أحد الملفات الوهمية في دليل المجموعة.

ENOENT
    حُووِل إنشاء مجموعة معالجات، باستخدام mkdir(2)، داخل مجموعة معالجات أب غير موجودة.

ENOENT
    حُووِل الوصول access(2) أو فتح open(2) ملف غير موجود في دليل مجموعة معالجات.

ENOMEM
    الذاكرة المتاحة داخل النواة غير كافية؛ يمكن أن يحدث هذا في مجموعة متنوعة من نداءات النظام التي تؤثر على مجموعات المعالجات، ولكن فقط إذا كان النظام يعاني من نقص حاد في الذاكرة.

ENOSPC
    حُووِل كتابة write(2) معرف العملية (PID) لعملية ما إلى ملف tasks الخاص بمجموعة معالجات عندما كان إعداد cpuset.cpus أو cpuset.mems الخاص بالمجموعة فارغًا.

ENOSPC
    حُووِل كتابة write(2) إعداد cpuset.cpus أو cpuset.mems فارغ إلى مجموعة معالجات مرتبطة بمهام.

ENOTDIR
    حُووِل تغيير اسم rename(2) مجموعة معالجات غير موجودة.

EPERM
    حُووِل إزالة ملف من دليل مجموعة معالجات.

ERANGE
    حُدّدت قائمة cpuset.cpus أو cpuset.mems للنواة تتضمن رقمًا كبيرًا جدًا بحيث لا تستطيع النواة ضبطه في أقنعة البتات الخاصة بها.

ESRCH
    حُووِل كتابة write(2) معرف العملية (PID) لعملية غير موجودة في ملف tasks الخاص بمجموعة معالجات.

الإصدارات
ظهرت مجموعات المعالجات في لينكس 2.6.12.

ملاحظات
على الرغم من اسمه، فإن معلمة pid هي في الواقع معرف خيط (thread ID)، وكل خيط في مجموعة خيوط يمكن ربطه بمجموعة معالجات مختلفة. القيمة المعادة من استدعاء gettid(2) يمكن تمريرها في المعطى pid.

العلل
يمكن فتح ملفات cpuset.memory_pressure للكتابة أو الإنشاء أو الاقتطاع، ولكن بعدها ستفشل عملية الكتابة write(2) مع ضبط errno على EACCES، ولن يكون لخيارات الإنشاء والاقتطاع في open(2) أي تأثير.

أمثلة
توضح الأمثلة التالية الاستعلام عن خيارات مجموعة المعالجات وضبطها باستخدام أوامر الصدفة.

  إنشاء مجموعة معالجات والارتباط بها.
لإنشاء مجموعة معالجات جديدة وربط صدفة الأوامر الحالية بها، اتبع الخطوات التالية:

(1) mkdir /dev/cpuset (إذا لم يكن قد أُنشئ بالفعل)
(2) mount -t cpuset none /dev/cpuset (إذا لم يكن قد وُصل بالفعل)
(3) أنشئ مجموعة المعالجات الجديدة باستخدام mkdir(1).
(4) خصّص المعالجات وعقد الذاكرة لمجموعة المعالجات الجديدة.
(5) اربط الصدفة بمجموعة المعالجات الجديدة.

على سبيل المثال، سيقوم تسلسل الأوامر التالي بإعداد مجموعة معالجات تسمى "Charlie"، تحتوي فقط على المعالجين 2 و 3، وعقدة الذاكرة 1، ثم يربط الصدفة الحالية بتلك المجموعة.

    $ mkdir /dev/cpuset
    $ mount -t cpuset cpuset /dev/cpuset
    $ cd /dev/cpuset
    $ mkdir Charlie
    $ cd Charlie
    $ /bin/echo 2-3 > cpuset.cpus
    $ /bin/echo 1 > cpuset.mems
    $ /bin/echo $$ > tasks
    # الصدفة الحالية تعمل الآن في مجموعة المعالجات Charlie
    # السطر التالي يجب أن يعرض '/Charlie'
    $ cat /proc/self/cpuset

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

(1) لنفترض أننا نريد نقل المهمة في مجموعة alpha (المعالجات 4–7 و عقد الذاكرة 2–3) إلى مجموعة جديدة beta (المعالجات 16–19 و عقد الذاكرة 8–9).
(2) أنشئ أولاً مجموعة المعالجات الجديدة beta.
(3) ثم اسمح بالمعالجات 16–19 وعقد الذاكرة 8–9 في beta.
(4) ثم مكن memory_migration في beta.
(5) ثم انقل كل عملية من alpha إلى beta.

يحقق تسلسل الأوامر التالي هذا الغرض.

    $ cd /dev/cpuset
    $ mkdir beta
    $ cd beta
    $ /bin/echo 16-19 > cpuset.cpus
    $ /bin/echo 8-9 > cpuset.mems
    $ /bin/echo 1 > cpuset.memory_migrate
    $ while read i; do /bin/echo $i; done < ../alpha/tasks > tasks

من المفترض أن ينقل ما سبق أي عمليات في alpha إلى beta، وأي ذاكرة تحتفظ بها هذه العمليات في عقد الذاكرة 2–3 إلى عقد الذاكرة 8–9، على التوالي.

لاحظ أن الخطوة الأخيرة من التسلسل أعلاه لم تنفذ:

    $ cp ../alpha/tasks tasks

كانت حلقة while ضرورية، بدلاً من الاستخدام الذي يبدو أسهل لأمر cp(1)، لأنه لا يمكن كتابة سوى معرف عملية (PID) واحد فقط في كل مرة إلى ملف tasks.

يمكن تحقيق نفس التأثير (كتابة معرف عملية واحد في كل مرة) مثل حلقة while بشكل أكثر كفاءة، وبعدد أقل من ضربات المفاتيح وبصيغة تعمل على أي صدفة، ولكن للأسف بشكل أكثر غموضًا، باستخدام الخيار -u (غير مخزن مؤقتًا) لأمر sed(1):

    $ sed -un p < ../alpha/tasks > tasks

انظر أيضًا
taskset(1), get_mempolicy(2), getcpu(2), mbind(2), sched_getaffinity(2), sched_setaffinity(2), sched_setscheduler(2), set_mempolicy(2), CPU_SET(3), proc(5), cgroups(7), numa(7), sched(7), migratepages(8), numactl(8)

Documentation/admin-guide/cgroup-v1/cpusets.rst في شجرة مصدر نواة لينكس

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

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

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