¿Cómo controlar en qué núcleo se ejecuta un proceso?


Puedo entender cómo se puede escribir un programa que usa múltiples procesos o subprocesos: fork() un nuevo proceso y usar IPC, o crear múltiples subprocesos y usar ese tipo de mecanismos de comunicación.

También entiendo el cambio de contexto. Es decir, con solo una CPU, el sistema operativo programa el tiempo para cada proceso (y hay toneladas de algoritmos de programación por ahí) y por lo tanto logramos ejecutar múltiples procesos simultáneamente.

Y ahora que tenemos procesadores multi-core (o equipos multi-procesador), podríamos tener dos procesos que se ejecutan simultáneamente en dos núcleos separados.

Mi pregunta es sobre el último escenario: ¿cómo controla el núcleo en qué núcleo se ejecuta un proceso? ¿Qué llamadas al sistema (en Linux, o incluso en Windows) programan un proceso en un núcleo específico?

La razón por la que pregunto: estoy trabajando en un proyecto para la escuela donde vamos a explorar un tema reciente en computación - y elegí arquitecturas multi-core. Parece que para ser un montón de material sobre cómo programar en ese tipo de entorno (cómo vigilar el punto muerto o las condiciones de carrera), pero no mucho en el control de los propios núcleos individuales. Me encantaría poder escribir algunos programas de demostración y presentar algunas instrucciones de ensamblaje o código C con el efecto de "Mira, estoy ejecutando un bucle infinito en el 2do núcleo, mira el pico en la utilización de la CPU para ese núcleo específico".

¿Algún ejemplo de código? O tutoriales?

Editar: Para aclaración-muchas personas han dicho que este es el propósito del sistema operativo, y que uno debe dejar que el sistema operativo se encargue de esto. Estoy completamente de acuerdo! Pero entonces lo que estoy preguntando (o tratando de tener una idea de) es lo que el sistema operativo realmente hace para hacer esto. No el algoritmo de programación ,sino más bien " una vez que se elige un núcleo, ¿qué instrucciones se deben ejecutar para que ese núcleo comience a buscar instrucciones?"

Author: Bill the Lizard, 2009-03-19

9 answers

Como otros han mencionado, la afinidad del procesador es específica del sistema operativo. Si quieres hacer esto fuera de los confines del sistema operativo, te espera mucha diversión, y con eso me refiero al dolor.

Dicho esto, otros han mencionado SetProcessAffinityMask para Win32. Nadie ha mencionado la forma en que el kernel de Linux establece la afinidad del procesador, y así lo haré. Necesita usar la función sched_setaffinity. Aquí está un buen tutorial sobre cómo.

 34
Author: Randolpho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-07 08:31:17

Normalmente la decisión sobre el núcleo en el que se ejecutará una aplicación la toma el sistema. Sin embargo, puede establecer la "afinidad" para una aplicación a un núcleo específico para decirle al sistema operativo que solo ejecute la aplicación en ese núcleo. Normalmente esto no es una buena idea, pero hay algunos casos raros en los que podría tener sentido.

Para hacer esto en Windows, use el administrador de tareas, haga clic derecho en el proceso y elija "Establecer afinidad". Puede hacerlo mediante programación en Windows utilizando funciones como SetThreadAffinityMask, SetProcessAffinityMask o SetThreadIdealProcessor.

ETA:

Si está interesado en cómo el sistema operativo realmente hace la programación, es posible que desee revisar estos enlaces:

Artículo de Wikipedia sobre cambio de contexto

Artículo de Wikipedia sobre programación

Programación en el núcleo linux

Con la mayoría de los sistemas operativos modernos, el sistema operativo programa un subproceso para ejecutarse en un núcleo durante un corto período de tiempo. Cuando caduque el segmento de tiempo, o el subproceso realiza una operación de E / s que hace que ceda voluntariamente el núcleo, el sistema operativo programará otro subproceso para ejecutarse en el núcleo (si hay algún subproceso listo para ejecutarse). Exactamente qué hilo está programado depende del algoritmo de programación del sistema operativo.

Los detalles de implementación de cómo se produce exactamente el cambio de contexto dependen de la CPU y el sistema operativo. Generalmente implicará un cambio al modo kernel, el sistema operativo guardará el estado del subproceso anterior, cargará el estado del subproceso nuevo, luego volver al modo usuario y reanudar el subproceso recién cargado. El artículo de cambio de contexto al que enlacé anteriormente tiene un poco más de detalle sobre esto.

 31
Author: Eric Petroelje,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-07 15:51:51

Nada le dice a core "ahora comienza a ejecutar este proceso".

El núcleo no ve el proceso, solo conoce el código ejecutable y varios niveles de ejecución y las limitaciones asociadas a las instrucciones que se pueden ejecutar.

Cuando la computadora arranca, por razones de simplicidad solo un núcleo/procesador está activo y realmente ejecuta cualquier código. Luego, si el sistema operativo es capaz de multiprocesador, activa otros núcleos con alguna instrucción específica del sistema, lo más probable es que otros núcleos exactamente el mismo lugar que el otro núcleo y correr desde allí.

Así que lo que scheduler hace es mirar a través de las estructuras internas del sistema operativo (tarea/proceso/cola de subprocesos) y elige una y la marca como ejecutándose en su núcleo. Luego, otras instancias del programador que se ejecutan en otros núcleos no lo tocarán hasta que la tarea esté en estado de espera nuevamente (y no esté marcada como anclada a un núcleo específico). Después de que la tarea se marque como en ejecución, scheduler ejecuta switch to userland con la tarea reanudándose en el punto en el que estaba anteriormente suspender.

Técnicamente no hay nada que impida que los núcleos ejecuten exactamente el mismo código al mismo tiempo (y muchas funciones desbloqueadas lo hacen), pero a menos que el código se escriba para esperar eso, probablemente se orine sobre sí mismo.

El escenario se vuelve más extraño con modelos de memoria más exóticos (arriba asume el espacio de memoria de trabajo lineal "habitual") donde los núcleos no necesariamente ven la misma memoria y puede haber requisitos para obtener código de los embragues de otros núcleos, pero es mucho más fácil manejado simplemente manteniendo la tarea anclada al núcleo(AFAIK Sony PS3 arquitectura con SPU es así).

 5
Author: Pasi Savolainen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-11 10:40:51

El proyecto OpenMPI tiene una biblioteca para establecer la afinidad del procesador en Linux de forma portátil.

Hace algún tiempo, he utilizado esto en un proyecto y funcionó bien.

Advertencia: Apenas recuerdo que hubo algunos problemas para averiguar cómo el sistema operativo numera los núcleos. Usé esto en un sistema de CPU de 2 Xeon con 4 núcleos cada uno.

Un vistazo a cat /proc/cpuinfo podría ayudar. En la caja que usé, es bastante raro. La salida hervida está en final.

Evidentemente, los núcleos uniformemente numerados están en la primera cpu y los núcleos extrañamente numerados están en la segunda cpu. Sin embargo, si no recuerdo mal, hubo un problema con los cachés. En estos procesadores Intel Xeon, dos núcleos en cada CPU comparten sus cachés L2 (no recuerdo si el procesador tiene una caché L3). Creo que los procesadores virtuales 0 y 2 compartieron un caché L2, 1 y 3 compartieron uno, 4 y 6 compartieron uno y 5 y 7 compartieron uno.

Debido a esto rareza (hace 1.5 años no pude encontrar ninguna documentación sobre la numeración de procesos en Linux), tendría cuidado de hacer este tipo de ajuste de bajo nivel. Sin embargo, claramente hay algunos usos. Si su código se ejecuta en algunos tipos de máquinas, entonces podría valer la pena hacer este tipo de ajuste. Otra aplicación estaría en algún lenguaje específico de dominio como StreamIt donde el compilador podría hacer este trabajo sucio y calcular una programación inteligente.

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
 3
Author: Manuel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-09 21:59:10

Para averiguar el número de procesadores en lugar de usar /proc/cpuinfo simplemente ejecute:

nproc

Para ejecutar un proceso en un grupo de procesadores específicos:

taskset --cpu-list 1,2 my_command 

Dirá que mi comando solo puede ejecutarse en cpu 1 o 2.

Para ejecutar un programa en 4 procesadores haciendo 4 cosas diferentes use parametrización. El argumento del programa le dice que haga algo diferente:

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

Un buen ejemplo de esto es tratar con 8 millones de operaciones en una matriz de modo que 0 a (2mil-1) va al procesador 1, 2mil a (4mil-1) al procesador 2 y así sucesivamente.

Puede ver la carga en cada proceso instalando htop usando apt-get/yum y ejecutándolo en la línea de comandos:

 htop
 3
Author: Eamonn Kenny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-28 10:13:03

Como otros han mencionado, está controlado por el sistema operativo. Dependiendo del sistema operativo, puede o no proporcionarle llamadas al sistema que le permitan afectar en qué núcleo se ejecuta un proceso determinado. Sin embargo, normalmente debería dejar que el sistema operativo realice el comportamiento predeterminado. Si tiene un sistema de 4 núcleos con 37 procesos en ejecución, y 34 de esos procesos están durmiendo, va a programar los 3 procesos activos restantes en núcleos separados.

Es probable que solo veas un aumento de velocidad sobre jugar con afinidades centrales en aplicaciones multihilo muy especializadas. Por ejemplo, supongamos que tiene un sistema con 2 procesadores de doble núcleo. Supongamos que tiene una aplicación con 3 subprocesos, y dos de subprocesos operan en gran medida en el mismo conjunto de datos, mientras que el tercer subproceso utiliza un conjunto diferente de datos. En este caso, usted se beneficiaría más al tener los dos subprocesos que interactúan en el mismo procesador y el tercer subproceso en el otro procesador, ya que entonces pueden compartir una caché. El sistema operativo no tiene idea de a qué memoria necesita acceder cada subproceso, por lo que es posible que no asigne subprocesos a los núcleos de manera adecuada.

Si está interesado en cómo el sistema operativo, lea programación. Los detalles esenciales del multiprocesamiento en x86 se pueden encontrar en los Manuales del Desarrollador de Software de Arquitecturas Intel 64 y IA-32 . Volumen 3A, Capítulos 7 y 8 contienen información relevante, pero tenga en cuenta que estos manuales son extremadamente técnicos.

 2
Author: Adam Rosenfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-19 21:14:00

El sistema operativo sabe cómo hacer esto, usted no tiene que hacerlo. Podría encontrarse con todo tipo de problemas si especificara en qué núcleo ejecutar, algunos de los cuales podrían ralentizar el proceso. Deja que el sistema operativo lo averigüe, solo necesitas iniciar el nuevo hilo.

Por ejemplo, si le dijeras a un proceso que se inicie en core x, pero core x ya estaba bajo una carga pesada, estarías peor que si hubieras dejado que el sistema operativo lo manejara.

 1
Author: Ed S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-19 20:39:45

No conozco las instrucciones de montaje. Pero la función de la API de Windows es SetProcessAffinityMask . Puedes ver un ejemplo de algo que improvisé hace un tiempo para ejecutar Picasa en un solo núcleo

 1
Author: Will Rickards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-19 21:00:45

Linux sched_setaffinity C minimal

En este ejemplo, obtenemos la afinidad, la modificamos y verificamos si ha tenido efecto con 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;
}

Compilar y ejecutar con:

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

Salida de muestra:

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

Lo que significa que:

  • inicialmente, todos mis 16 núcleos estaban habilitados, y el proceso se ejecutaba aleatoriamente en el núcleo 9 (el 10º)
  • después de establecer la afinidad solo en el primer núcleo, el proceso se movió necesariamente a core 0 (el primero)

También es divertido ejecutar este programa a través de taskset:

taskset -c 1,3 ./a.out

Que da salida de forma:

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

Y así vemos que limitó la afinidad desde el principio.

Esto funciona porque la afinidad es heredada por procesos hijos, que taskset es bifurcación: ¿Cómo evitar heredar afinidad de CPU por procesos hijos bifurcados?

Probado en Ubuntu 16.04, GitHub upstream.

X86 desnudo metal

Si eres tan hardcore: ¿Cómo es el lenguaje ensamblador multinúcleo?

Cómo Linux lo implementa

¿Cómo funciona sched_setaffinity ()?

 0
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-07 08:28:40