sched_setaffinity(2)

الاسم
sched_setaffinity, sched_getaffinity - ضبط وجلب قناع التقارب لوحدة المعالجة المركزية لخيط

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

موجز
#define _GNU_SOURCE             /* انظر feature_test_macros(7) */
#include <sched.h>

int sched_setaffinity(pid_t pid, size_t cpusetsize,
                      const cpu_set_t *mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize,
                      cpu_set_t *mask);

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

يُمثل قناع تقارب وحدة المعالجة المركزية بواسطة بنية cpu_set_t، وهي "مجموعة وحدات معالجة مركزية"، يشير إليها mask. تُوصف مجموعة من الكليّات لمعالجة مجموعات وحدات المعالجة المركزية في CPU_SET(3).

تضبط sched_setaffinity() قناع تقارب وحدة المعالجة المركزية للخيط الذي معرفه هو pid إلى القيمة المحددة بواسطة mask. إذا كان pid صفرًا، يُستخدم الخيط المستدعِي. الوسيط cpusetsize هو طول (بالبايت) البيانات المشار إليها بواسطة mask. عادةً ما يُحدد هذا الوسيط كـ sizeof(cpu_set_t).

إذا كان الخيط المحدد بواسطة pid لا يعمل حاليًا على إحدى وحدات المعالجة المركزية المحددة في mask، فيُهجّر ذلك الخيط إلى إحدى وحدات المعالجة المركزية المحددة في mask.

تكتب sched_getaffinity() قناع تقارب الخيط الذي معرفه هو pid في بنية cpu_set_t المشار إليها بواسطة mask. يحدد الوسيط cpusetsize حجم (بالبايت) mask. إذا كان pid صفرًا، يُعاد قناع الخيط المستدعِي.

قيمة الإرجاع
عند النجاح، تُعيد sched_setaffinity() و sched_getaffinity() 0 (لكن انظر "الاختلافات بين مكتبة C والنواة" أدناه، حيث يُلاحظ أن sched_getaffinity() الأساسية تختلف في قيمة إرجاعها). عند الفشل، يُعاد -1، ويُضبط errno للإشارة إلى الخطأ.

الأخطاء

EFAULT
    عنوان الذاكرة المقدم غير صالح.

EINVAL
    لا يحتوي قناع البتات للتقارب mask على أي معالجات موجودة فعليًا حاليًا على النظام ومسموح بها للخيط وفقًا لأي قيود قد تُفرض بواسطة مجموعات تحكم cpuset أو آلية "cpuset" الموصوفة في cpuset(7).

EINVAL
    (sched_getaffinity() وقبل Linux 2.6.9، sched_setaffinity()) cpusetsize أصغر من حجم قناع التقارب المستخدم بواسطة النواة.

EPERM
    (sched_setaffinity()) لا يمتلك الخيط المستدعِي الامتيازات المناسبة. يحتاج المستدعِي إلى معرف مستخدم فعال يساوي معرف المستخدم الحقيقي أو معرف المستخدم الفعال للخيط المحدد بواسطة pid، أو يجب أن يمتلك القدرة CAP_SYS_NICE في فضاء أسماء المستخدم للخيط pid.

ESRCH
    تعذر العثور على الخيط (thread) الذي معرفه pid.

المعايير
لينكس.

التاريخ
Linux 2.5.8، glibc 2.3.

في البداية، تضمنت واجهات glibc وسيط cpusetsize، بنوع unsigned int. في glibc 2.3.3، أُزيل الوسيط cpusetsize، لكنه استُرجع لاحقًا في glibc 2.3.4، بنوع size_t.

ملاحظات
بعد استدعاء sched_setaffinity()، تكون مجموعة وحدات المعالجة المركزية التي سيعمل عليها الخيط فعليًا هي تقاطع المجموعة المحددة في الوسيط mask ومجموعة وحدات المعالجة المركزية الموجودة فعليًا على النظام. قد يُقيّد النظام أيضًا مجموعة وحدات المعالجة المركزية التي يعمل عليها الخيط إذا استُخدمت آلية "cpuset" الموصوفة في cpuset(7). تُفرض هذه القيود على المجموعة الفعلية لوحدات المعالجة المركزية التي سيعمل عليها الخيط بصمت بواسطة النواة.

توجد طرق مختلفة لتحديد عدد وحدات المعالجة المركزية المتاحة على النظام، بما في ذلك: فحص محتويات /proc/cpuinfo؛ استخدام sysconf(3) للحصول على قيم المعاملين _SC_NPROCESSORS_CONF و _SC_NPROCESSORS_ONLN؛ وفحص قائمة أدلة وحدة المعالجة المركزية تحت /sys/devices/system/cpu/.

يحتوي sched(7) على وصف لمخطط جدولة لينكس.

قناع التقارب هو سمة لكل خيط يمكن ضبطها بشكل مستقل لكل خيط في مجموعة خيوط. يمكن تمرير القيمة المُعادة من استدعاء gettid(2) في الوسيط pid. سيؤدي تحديد pid كـ 0 إلى ضبط السمة للخيط المستدعِي، وتمرير القيمة المُعادة من استدعاء getpid(2) سيضبط السمة للخيط الرئيس لمجموعة الخيوط. (إذا كنت تستخدم واجهة برمجة تطبيقات خيوط POSIX، فاستخدم pthread_setaffinity_np(3) بدلاً من sched_setaffinity().)

يمكن استخدام خيار الإقلاع isolcpus لعزل وحدة معالجة مركزية واحدة أو أكثر عند الإقلاع، بحيث لا تُجدول أي عمليات على تلك الوحدات. بعد استخدام خيار الإقلاع هذا، الطريقة الوحيدة لجدولة العمليات على وحدات المعالجة المركزية المعزولة هي عبر sched_setaffinity() أو آلية cpuset(7). لمزيد من المعلومات، انظر ملف مصدر النواة Documentation/admin-guide/kernel-parameters.txt. وكما هو مذكور في ذلك الملف، isolcpus هي الآلية المفضلة لعزل وحدات المعالجة المركزية (مقارنة بالبديل المتمثل في ضبط تقارب وحدة المعالجة المركزية يدويًا لجميع العمليات على النظام).

يرث طفل مُنشأ عبر fork(2) قناع تقارب وحدة المعالجة المركزية لوالده. يُحافظ على قناع التقارب عبر execve(2).

  الاختلافات بين مكتبة C والنواة
تصف صفحة الدليل هذه واجهة glibc لاستدعاءات تقارب وحدة المعالجة المركزية. تختلف واجهة استدعاء النظام الفعلية قليلاً، حيث يُعرّف mask بنوع unsigned long *، مما يعكس حقيقة أن التنفيذ الأساسي لمجموعات وحدات المعالجة المركزية هو قناع بتات بسيط.

عند النجاح، يُعيد استدعاء النظام الخام sched_getaffinity() عدد البايتات المنسوخة في مخزن mask؛ وسيكون هذا هو الحد الأدنى لـ cpusetsize وحجم (بالبايت) نوع البيانات cpumask_t المستخدم داخليًا بواسطة النواة لتمثيل قناع بتات مجموعة وحدة المعالجة المركزية.

  معالجة الأنظمة ذات أقنعة تقارب وحدة المعالجة المركزية الكبيرة
لا تفرض استدعاءات النظام الأساسية (التي تمثل أقنعة وحدة المعالجة المركزية كأقنعة بتات من نوع unsigned long *) أي قيود على حجم قناع وحدة المعالجة المركزية. ومع ذلك، فإن نوع البيانات cpu_set_t المستخدم بواسطة glibc له حجم ثابت يبلغ 128 بايت، مما يعني أن الحد الأقصى لرقم وحدة المعالجة المركزية الذي يمكن تمثيله هو 1023. إذا كان قناع تقارب وحدة المعالجة المركزية للنواة أكبر من 1024، فإن استدعاءات النموذج:

    sched_getaffinity(pid, sizeof(cpu_set_t), &mask);

تفشل بالخطأ EINVAL، وهو الخطأ الناتج عن استدعاء النظام الأساسي في الحالة التي يكون فيها حجم mask المحدد في cpusetsize أصغر من حجم قناع التقارب المستخدم بواسطة النواة. (اعتمادًا على مخطط وحدة المعالجة المركزية للنظام، يمكن أن يكون قناع تقارب النواة أكبر بكثير من عدد وحدات المعالجة المركزية النشطة في النظام).

عند العمل على أنظمة ذات أقنعة تقارب كبيرة لنواة وحدة المعالجة المركزية، يجب تخصيص الوسيط mask ديناميكيًا (انظر CPU_ALLOC(3)). حاليًا، الطريقة الوحيدة للقيام بذلك هي من خلال استقصاء حجم القناع المطلوب باستخدام استدعاءات sched_getaffinity() بأحجام أقنعة متزايدة (حتى لا يفشل الاستدعاء بالخطأ EINVAL).

كن على علم بأن CPU_ALLOC(3) قد يخصص مجموعة وحدات معالجة مركزية أكبر قليلاً مما هو مطلوب (لأن مجموعات وحدات المعالجة المركزية تُنفذ كأقنعة بتات تُخصص بوحدات sizeof(long)). بناءً على ذلك، يمكن لـ sched_getaffinity() ضبط بتات تتجاوز حجم التخصيص المطلوب، لأن النواة ترى بضع بتات إضافية. لذلك، يجب على المستدعِي تكرار البتات في المجموعة المُعادة، وعدّ البتات التي ضُبطت، والتوقف عند الوصول إلى القيمة المُعادة من CPU_COUNT(3) (بدلاً من التكرار على عدد البتات المطلوب تخصيصها).

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

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

نستخدم أولاً lscpu(1) لتحديد أن هذا الحاسوب (x86) يحتوي على نواتين، لكل منهما وحدتا معالجة مركزية:

    $ lscpu | egrep -i 'core.*:|socket';
    Thread(s) per core:    2
    Core(s) per socket:    2
    Socket(s):             1

نقيس بعد ذلك زمن تشغيل البرنامج المثال لثلاث حالات: كلتا العمليتين تعملان على نفس وحدة المعالجة المركزية؛ كلتا العمليتين تعملان على وحدتي معالجة مركزية مختلفتين على نفس النواة؛ وكلتا العمليتين تعملان على وحدتي معالجة مركزية مختلفتين على نواتين مختلفتين.

    $ time -p ./a.out 0 0 100000000;
    real 14.75
    user 3.02
    sys 11.73
    $ time -p ./a.out 0 1 100000000;
    real 11.52
    user 3.98
    sys 19.06
    $ time -p ./a.out 0 3 100000000;
    real 7.89
    user 3.29
    sys 12.07

  مصدر البرنامج
    #define _GNU_SOURCE
    #include <err.h>
    #include <sched.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    #include <unistd.h>

    int
    main(int argc, char *argv[])
    {
        int           parentCPU, childCPU;
        cpu_set_t     set;
        unsigned int  nloops;

        if (argc != 4) {
            fprintf(stderr, "Usage: %s parent-cpu child-cpu num-loops\n",
                    argv[0]);
            exit(EXIT_FAILURE);
        }

        parentCPU = atoi(argv[1]);
        childCPU = atoi(argv[2]);
        nloops = atoi(argv[3]);

        CPU_ZERO(&set);

        switch (fork()) {
        case -1:            /* خطأ */
            err(EXIT_FAILURE, "fork");

        case 0:             /* طفل */
            CPU_SET(childCPU, &set);

            if (sched_setaffinity(0, sizeof(set), &set) == -1)
                err(EXIT_FAILURE, "sched_setaffinity");

            for (unsigned int j = 0; j < nloops; j++)
                getppid();

            exit(EXIT_SUCCESS);

        default:            /* أب */
            CPU_SET(parentCPU, &set);

            if (sched_setaffinity(0, sizeof(set), &set) == -1)
                err(EXIT_FAILURE, "sched_setaffinity");

            for (unsigned int j = 0; j < nloops; j++)
                getppid();

            wait(NULL);     /* انتظر إنهاء الطفل */
            exit(EXIT_SUCCESS);
        }
    }

انظر أيضًا
lscpu(1), nproc(1), taskset(1), clone(2), getcpu(2), getpriority(2), gettid(2), nice(2), sched_get_priority_max(2), sched_get_priority_min(2), sched_getscheduler(2), sched_setscheduler(2), setpriority(2), CPU_SET(3), get_nprocs(3), pthread_setaffinity_np(3), sched_getcpu(3), capabilities(7), cpuset(7), sched(7), numactl(8)

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

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

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