2009-03-19 31 views
50

Puedo entender cómo se puede escribir un programa que usa múltiples procesos o hilos: fork() un nuevo proceso y usa IPC, o crea múltiples hilos y usa ese tipo de mecanismos de comunicación.¿Cómo controlar qué núcleo se ejecuta un proceso?

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

Y ahora que tenemos procesadores multi-core (o computadoras multiprocesador), podríamos tener dos procesos ejecutándose simultáneamente en dos núcleos separados.

Mi pregunta es sobre el último escenario: ¿cómo controla el kernel 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 estoy preguntando: Estoy trabajando en un proyecto para la escuela donde vamos a explorar un tema reciente en informática, y elegí arquitecturas multi-core. Parece que hay mucho material sobre cómo programar en ese tipo de entorno (cómo observar el estancamiento o las condiciones de carrera) pero no mucho sobre el control de los núcleos individuales. Me encantaría poder escribir algunos programas de demostración y presentar algunas instrucciones de ensamblaje o código C para el efecto de "Ver, estoy ejecutando un bucle infinito en el segundo núcleo, mire el pico en la utilización de CPU para ese núcleo específico ".

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

editar: Para aclarar: 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 entender) es qué hace realmente el sistema operativo para hacer esto. No es el algoritmo de programación, sino más "una vez que se elige un núcleo, ¿qué instrucciones se deben ejecutar para que ese núcleo comience a buscar las instrucciones?"

+1

posible respuesta aquí: http://stackoverflow.com/questions/980999/what-does-multicore-assembly-language-look-like, una de las respuestas describe la interrupción del interprocesador de inicio, que se envía desde un núcleo de CPU al APIC del chipset, y se puede utilizar para inicializar otra CPU y ejecutar código en ese CPU en una dirección específica –

+0

Ejemplo mínimo de iniciar SMP en nuestro propio sistema operativo: http://stackoverflow.com/a/33651438/895245 Jugar con esa + memoria de sincronización debería ser el camino. –

Respuesta

31

Como han mencionado otros, la afinidad del procesador es Sistema operativo específico. Si quieres hacer esto fuera de los confines del sistema operativo, te divertirás mucho, y con eso me refiero al dolor.

Dicho esto, otros han mencionado SetProcessAffinityMask para Win32. Nadie ha mencionado la forma del kernel de Linux para establecer la afinidad del procesador, y así lo haré. Necesita usar la función sched_set_affinity. Aquí está a nice tutorial sobre cómo.

+1

Escribí un artículo sobre este tema hace un tiempo, pero está escrito en eslovaco, así que supongo que eso no ayudaría a la persona que pregunta :) De todos modos, tu respuesta va en la dirección correcta, por lo que estoy definitivamente de darte un voto up :-) –

+0

Ese tutorial establece la máscara de afinidad en "created_thread", que, AFAICT, no es estrictamente la CPU en la que se está ejecutando actualmente el subproceso. Es solo un entero que se incrementa y se utiliza de tal manera para indexar en la máscara de bits, pero parece que en el ejemplo no hay una determinación real de qué procesador está actualmente en uso, solo limitando los procesos secundarios para ejecutarse en el número de CPU que corresponde al orden en que se crean los procesos hijos. – Jotorious

1

El sistema operativo sabe cómo hacerlo, no es necesario. Podría encontrarse con todo tipo de problemas si especificara en qué núcleo ejecutar, algunos de los cuales podrían ralentizar el proceso. Deje que el SO lo resuelva, solo necesita iniciar el nuevo hilo.

Por ejemplo, si le indicó a un proceso que debe comenzar en core x, pero core x ya estaba bajo una carga pesada, estaría peor que si hubiera dejado que el SO lo manejara.

+0

sí, he votado a favor, pero tal vez tiene el proceso A en ejecución y desea iniciar el proceso B, C y D en cualquier núcleo, excepto el que ejecuta A. parece totalmente razonable. –

30

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

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

ETA:

Si usted está interesado en la forma en que el sistema operativo realmente hace la programación, es posible que desee echa un vistazo a estos enlaces:

Wikipedia article on context switching

Wikipedia article on scheduling

Scheduling in the linux kernel

Con la mayoría de los sistemas operativos modernos, el sistema operativo programa un hilo para ejecutar en un núcleo para un corto segmento de tiempo. Cuando el segmento de tiempo expira, o el subproceso realiza una operación IO que hace que ceda voluntariamente el núcleo, el SO 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 exactamente se produce el cambio de contexto son CPU & dependiente del sistema operativo. Por lo general, implicará un cambio al modo kernel, el sistema operativo guardará el estado del hilo anterior, cargará el estado del nuevo hilo, luego volverá al modo de usuario y reanudará el hilo recién cargado. El artículo de cambio de contexto vinculado al artículo anterior tiene un poco más de detalle al respecto.

+0

+1 por proporcionar la manera más fácil de hacerlo. –

+0

Tenga en cuenta que la máscara de afinidad se hereda por procesos secundarios, por lo que si la configura en el Explorador, todas las aplicaciones iniciadas también usarán un subconjunto de los procesadores disponibles. – Richard

1

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

2

Como han mencionado otros, es 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 inactivos, va a programar los 3 procesos activos restantes en núcleos separados.

Es probable que solo vea un aumento de velocidad al jugar con las afinidades del núcleo en aplicaciones multiproceso 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 fuertemente en el mismo conjunto de datos, mientras que el tercer subproceso usa un conjunto diferente de datos. En este caso, se beneficiaría al máximo al tener los dos hilos que interactúan en el mismo procesador y el tercer hilo en el otro procesador, ya que entonces pueden compartir un caché. El sistema operativo no tiene idea de a qué memoria necesita acceder cada subproceso, por lo que no puede asignar hilos a los núcleos de forma adecuada.

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

3

El proyecto OpenMPI tiene un library to set the processor affinity en Linux de forma portátil.

Hace algún tiempo, lo he usado en un proyecto y funcionó bien.

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

Un vistazo a cat /proc/cpuinfo podría ayudar. En la caja que usé, es bastante extraño. La producción reducida se encuentra al final.

Evidentemente, los núcleos con número impar están en la primera CPU y los núcleos con número impar están en la segunda CPU. Sin embargo, si mal no recuerdo, hubo un problema con los cachés. En estos procesadores Intel Xeon, dos núcleos en cada CPU comparten sus memorias caché 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 esta 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 pocos tipos de máquinas, entonces puede valer la pena hacer este tipo de ajuste. Otra aplicación sería en algún lenguaje específico de dominio como StreamIt donde el compilador podría hacer este trabajo sucio y calcular un cronograma 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 
+0

También SLERT intenta esto y tiene mecanismos bastante sofisticados para seleccionar un procesador o grupo de procesadores. –

5

Nada dice al núcleo "ahora comience 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, en aras de la simplicidad, solo un núcleo/procesador está activo y realmente ejecuta cualquier código. Entonces, si el sistema operativo tiene capacidad de multiprocesador, activará otros núcleos con alguna instrucción específica del sistema, otros núcleos probablemente tomarán exactamente del mismo lugar que otro núcleo y se ejecutarán desde allí.

Entonces, lo que el programador hace es mirar a través de las estructuras internas del sistema operativo (tarea/proceso/cola de hilos) y selecciona una y la marca como ejecutada en su núcleo. Luego, otras instancias del planificador que se ejecutan en otros núcleos no lo tocarán hasta que la tarea esté nuevamente en estado de espera (y no se marque como anclada a un núcleo específico). Después de que la tarea se marque como ejecutada, el planificador ejecuta el cambio a un dominio de usuario con reanudación de la tarea en el punto en que se suspendió previamente.

Técnicamente no hay nada que impida que los núcleos ejecuten exactamente el mismo código al mismo tiempo (y muchas funciones desbloqueadas), pero a menos que el código esté escrito para esperar eso, probablemente se enfade por completo.

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

1

Para averiguar el número de procesadores en lugar de utilizar/proc/cpuinfo basta con ejecutar:

nproc 

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

taskset --cpu-list 1,2 my_command 

dirán que mi el comando solo puede ejecutarse en la CPU 1 o 2.

Para ejecutar un programa en 4 procesadores que realizan 4 cosas diferentes, use la parametrización. El argumento para el programa 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 funcionamiento en una matriz de modo que 0 a (2mil-1) va al procesador de 1, 2mil a (4mil-1) al procesador 2 y así sucesivamente.

Usted puede mirar en el proceso de carga en cada instalando htop usando apt-get/yum y funcionando a la línea de comandos:

htop 
Cuestiones relacionadas