Как контролировать, на каком ядре выполняется процесс?



Я могу понять, как можно написать программу, которая использует несколько процессов или потоков: fork() новый процесс и использовать IPC, или создать несколько потоков и использовать такие механизмы связи.

Я понимаю, переключение контекста. То есть, только с одним процессором, операционная система планирует время для каждого процесса (и есть тонны алгоритмов планирования там), и тем самым мы достигаем запуска нескольких процессов одновременно.

и теперь, когда мы имея многоядерные процессоры (или многопроцессорные компьютеры), мы могли бы иметь два процесса, работающих одновременно на двух отдельных ядрах.

мой вопрос касается последнего сценария: как ядро контролирует, на каком ядре выполняется процесс? Какие системные вызовы (в Linux или даже Windows) планируют процесс на определенном ядре?

причина, по которой я спрашиваю: я работаю над проектом для школы, где мы должны изучить недавнюю тему в области вычислений - и я выбрал многоядерный зодчие. Кажется, есть много материала о том, как программировать в такой среде (как следить за тупиковыми или гоночными условиями), но не так много о контроле самих отдельных ядер. Я хотел бы иметь возможность написать несколько демонстрационных программ и представить некоторые инструкции по сборке или код C для эффекта "Смотрите, я запускаю бесконечный цикл на 2-м ядре, посмотрите на всплеск загрузки процессора для это конкретное ядро".

любой код примеры? Или учебники?

edit: для уточнения-многие люди сказали, что это цель ОС, и что нужно позволить ОС позаботиться об этом. Я полностью согласен! Но тогда я спрашиваю (или пытаюсь почувствовать), что на самом деле делает операционная система для этого. Не алгоритм планирования, а больше "после того, как ядро выбрано, какие инструкции должны быть выполнены, чтобы это ядро начало получать инструкции?"

283   9  

9 ответов:

Как уже упоминалось, сродство процессора операционная система специфических. Если вы хотите сделать это за пределами операционной системы, вам будет очень весело, и под этим я подразумеваю боль.

тем не менее, другие упоминали SetProcessAffinityMask для Win32. Никто не упомянул способ ядра Linux установить сродство процессора, и поэтому я буду. Вы должны использовать

Обычно решение о том, на каком ядре будет работать приложение, принимается системой. Тем не менее, вы можете установить "сродство" для приложения к определенному ядру, чтобы сообщить ОС только запустить приложение на этом ядре. Обычно это не очень хорошая идея, но есть некоторые редкие случаи, где это может иметь смысл.

чтобы сделать это в windows, используйте диспетчер задач, щелкните правой кнопкой мыши на процессе и выберите "Установить сходство". Вы можете сделать это программно в Windows, используя такие функции, как SetThreadAffinityMask, SetProcessAffinityMask или SetThreadIdealProcessor.

ETA:

Если вы заинтересованы в том, как ОС на самом деле выполняет планирование, вы можете проверить эти ссылки:

статья в Википедии о переключении контекста

статья Википедии о планировании

планирование в ядре linux

с большинством современных ОС, ОС планирует поток выполните на ядре за короткий отрезок времени. Когда временной срез истекает, или поток выполняет операцию ввода-вывода, которая заставляет его добровольно уступить ядро, ОС запланирует запуск другого потока на ядре (если есть какие-либо потоки, готовые к запуску). Какой именно поток запланирован зависит от алгоритма планирования ОС.

детали реализации точно, как происходит переключение контекста, зависят от процессора и ОС. Как правило, это будет включать в себя переход в режим ядра, ОС сохранение состояния предыдущего потока, загрузка состояния нового потока, затем переключение обратно в пользовательский режим и возобновление вновь загруженного потока. Переключение контекста статьи я ссылался выше более подробно об этом.

ничто не говорит ядру "теперь начните запускать этот процесс".

базовый не вижу процесс, он знает только о исполняемом коде и различных уровнях выполнения и связанных с ними ограничениях на инструкции, которые могут быть выполнены.

При загрузке компьютера, для простоты только одно ядро/процессор активен и фактически выполняет код. Затем, если ОС поддерживает Многопроцессорность, она активирует другие ядра с некоторой системной инструкцией, другие ядра больше всего скорее всего, забрать из точно такого же места, как и другие ядра и бежать оттуда.

Итак, что делает планировщик, он просматривает внутренние структуры ОС (очередь задач/процессов/потоков) и выбирает один и помечает его как работающий в своем ядре. Затем другие экземпляры планировщика, работающие на других ядрах, не будут касаться его, пока задача снова не будет находиться в состоянии ожидания (и не будет отмечена как прикрепленная к определенному ядру). После того, как задача помечена как запущенная, планировщик Выполняет переключение на userland с возобновлением задачи в точке, где она была ранее отстранен.

технически ничто не мешает ядрам запускать точно такой же код в одно и то же время (и многие разблокированные функции делают это), но если код не написан, чтобы ожидать этого, он, вероятно, будет мочиться на себя.

сценарий становится более странным с более экзотическими моделями памяти (выше предполагается "обычное" линейное единое рабочее пространство памяти), где ядра не обязательно все видят одну и ту же память, и могут быть требования по извлечению кода из других сцепления ядра, но это намного проще, просто удерживая задачу, прикрепленную к ядру (архитектура AFAIK Sony PS3 с SPU-это так).

The OpenMPI есть библиотека для установки сродства процессора on Linux в портативном пути.

некоторое время назад, я использовал это в проекте и все работало нормально.

предостережение: я смутно помню, что были некоторые проблемы в выяснении того, как операционная система нумерует ядра. Я использовал это в 2-х процессорной системе Xeon с 4 ядрами каждый.

посмотреть cat /proc/cpuinfo может помочь. На коробке я используется, это довольно странно. Вываренный выход находится в конце.

из-за этой странности (1,5 года назад я не мог найти никакой документации по нумерации процессов в Linux), я был бы осторожен, чтобы сделать такую настройку низкого уровня. Тем не менее, очевидно, что есть некоторые применения. Если ваш код работает на нескольких типах машин, то это может быть стоит сделать этот вид настройки. Другое приложение будет на каком-то конкретном языке домена, например StreamIt где компилятор может делать эту грязную работу и вычислить умный график.

processor       : 0
physical id     : 0
siblings        : 4
core id         : 0
cpu cores       : 4

processor       : 1
physical id     : 1
siblings        : 4
core id         : 0
cpu cores       : 4

processor       : 2
physical id     : 0
siblings        : 4
core id         : 1
cpu cores       : 4

processor       : 3
physical id     : 1
siblings        : 4
core id         : 1
cpu cores       : 4

processor       : 4
physical id     : 0
siblings        : 4
core id         : 2
cpu cores       : 4

processor       : 5
physical id     : 1
siblings        : 4
core id         : 2
cpu cores       : 4

processor       : 6
physical id     : 0
siblings        : 4
core id         : 3
cpu cores       : 4

processor       : 7
physical id     : 1
siblings        : 4
core id         : 3
cpu cores       : 4

чтобы узнать количество процессоров вместо использования /proc / cpuinfo просто запустите:

nproc

для запуска процесса на группе конкретных процессоров:

taskset --cpu-list 1,2 my_command 

скажет, что моя команда может работать только на cpu 1 или 2.

чтобы запустить программу на 4 процессорах, выполняющих 4 разных действия, используйте параметризацию. Аргумент к программе говорит ему сделать что-то другое:

for i in `seq 0 1 3`;
do 
  taskset --cpu-list $i my_command $i;
done

хороший пример этого имеет дело с 8 миллионов операций в массив таким образом, что от 0 до (2mil-1) идет к процессору 1, от 2mil до (4mil-1) к процессору 2 и так далее.

вы можете посмотреть на нагрузку на каждый процесс, установив htop с помощью apt-get/yum и запустив в командной строке:

 htop

Как уже упоминалось, он управляется операционной системой. В зависимости от ос, он может или не может предоставить вам системные вызовы, которые позволяют вам влиять на то, на каком ядре выполняется данный процесс. Однако обычно вы должны просто позволить ОС выполнять поведение по умолчанию. Если у вас есть 4-ядерная система с 37 запущенными процессами, и 34 из этих процессов спят, она будет планировать оставшиеся 3 активных процесса на отдельные ядра.

вы, вероятно, увидите только повышение скорости при игре с основными сродствами в очень специализированных многопоточных приложениях. Например, предположим, что у вас есть система с 2 двухъядерными процессорами. Предположим, у вас есть приложение с 3 потоками, и два из них работают в основном на одном и том же наборе данных, тогда как третий поток использует другой набор данных. В этом случае вы выиграете больше всего, имея два потока, которые взаимодействуют на одном процессоре и третий поток на другом процессоре, так как тогда они могут общий доступ к кэшу. ОС понятия не имеет, к какой памяти должен обращаться каждый поток, поэтому она может не выделять потоки ядрам соответствующим образом.

Если вы заинтересованы в как операционная система, читайте на планирование. Мелкие детали многопроцессорной обработки на x86 можно найти в руководства для разработчиков программного обеспечения Intel 64 и IA-32 Architectures. Том 3A, Главы 7 и 8 содержат соответствующую информацию, но имейте в виду, что эти руководства являются чрезвычайно технически.

ОС знает, как это сделать, вам не нужно. Вы можете столкнуться со всевозможными проблемами, если вы укажете, на каком ядре работать, некоторые из которых могут фактически замедлить процесс. Пусть ОС разберется, вам просто нужно запустить новый поток.

например, если вы сказали процессу начать на core x, но core x уже был под большой нагрузкой, вам будет хуже, чем если бы вы просто позволили ОС справиться с этим.

Я не знаю инструкции по сборке. Но функция Windows API-это SetProcessAffinityMask. Вы можете видеть пример что - то я собрал вместе некоторое время назад, чтобы запустить Picasa только на одном ядре

Linux sched_setaffinity C минимальный

в этом примере мы получаем сходство, изменяем его и проверяем, вступило ли оно в силу с sched_getcpu().

#define _GNU_SOURCE
#include <assert.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void print_affinity() {
    cpu_set_t mask;
    long nproc, i;

    if (sched_getaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_getaffinity");
        assert(false);
    } else {
        nproc = sysconf(_SC_NPROCESSORS_ONLN);
        printf("sched_getaffinity = ");
        for (i = 0; i < nproc; i++) {
            printf("%d ", CPU_ISSET(i, &mask));
        }
        printf("\n");
    }
}

int main(void) {
    cpu_set_t mask;

    print_affinity();
    printf("sched_getcpu = %d\n", sched_getcpu());
    CPU_ZERO(&mask);
    CPU_SET(0, &mask);
    if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_setaffinity");
        assert(false);
    }
    print_affinity();
    /* TODO is it guaranteed to have taken effect already? Always worked on my tests. */
    printf("sched_getcpu = %d\n", sched_getcpu());
    return EXIT_SUCCESS;
}

скомпилировать и запустить с:

gcc -std=c99 main.c
./a.out

пример вывода:

sched_getaffinity = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 
sched_getcpu = 9
sched_getaffinity = 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 0

это означает, что:

  • первоначально все мои 16 ядер были включены, и процесс был случайным образом запущен на ядре 9 (10-й)
  • после того, как мы установили сродство только к первому ядру, процесс был перемещен обязательно к ядру 0 (первый)

это также интересно запустить эту программу через taskset:

taskset -c 1,3 ./a.out

который дает выход формы:

sched_getaffinity = 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 2
sched_getaffinity = 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 0

и так мы видим, что это ограниченное сродство с самого начала.

это работает, потому что сходство наследуется дочерними процессами, которые taskset рассеивания: как предотвратить наследование сходства ЦП дочерним разветвленным процесс?

протестировано в Ubuntu 16.04,GitHub вверх по течению.

x86 голый металл

если вы такой хардкор:как выглядит многоядерный язык ассемблера?

как Linux реализует его

как sched_setaffinity() работает?

    Ничего не найдено.

Добавить ответ:
Отменить.