¿Cómo es el lenguaje ensamblador multinúcleo?


Érase una vez, para escribir el ensamblador x86, por ejemplo, tendría instrucciones que indicaban "cargar el registro EDX con el valor 5", "incrementar el registro EDX", etc.

Con las CPU modernas que tienen 4 núcleos (o incluso más), a nivel de código máquina ¿parece que hay 4 CPU separadas (es decir, hay solo 4 registros "EDX" distintos) ? Si es así, cuando dices "incrementar el registro EDX", ¿qué determina qué registro EDX de la CPU se incrementa? Hay un "contexto de CPU" ¿o el concepto de "hilo" en el ensamblador x86 ahora?

¿Cómo funciona la comunicación/sincronización entre los núcleos?

Si estuviera escribiendo un sistema operativo, ¿qué mecanismo está expuesto a través del hardware para permitirle programar la ejecución en diferentes núcleos? ¿Es alguna instrucción(s) especial (es) privilegiada (s)?

Si estuviera escribiendo un compilador de optimización / VM de bytecode para una CPU multinúcleo, ¿qué necesitaría saber específicamente sobre, por ejemplo, x86 para que genere código que se ejecute eficientemente a través de todos los núcleos?

¿Qué cambios se han realizado en el código de máquina x86 para admitir la funcionalidad de varios núcleos?

10 answers

Esta no es una respuesta directa a la pregunta, pero es una respuesta a una pregunta que aparece en los comentarios. Esencialmente, la pregunta es qué soporte da el hardware a la operación multihilo.

Nicholas Flynt tenía razón, al menos con respecto a x86. En un entorno multihilo (Hyper-threading, multi-core o multi-processor), el subproceso Bootstrap (normalmente el subproceso 0 en core 0 en processor 0) inicia la búsqueda de código desde la dirección 0xfffffff0. Todos los demás los hilos se inician en un estado de suspensión especial llamado Wait-for-SIPI. Como parte de su inicialización, el subproceso primario envía una interrupción especial entre procesadores (IPI) sobre el APIC llamada SIPI (IPI de inicio) a cada subproceso que está en WFS. El SIPI contiene la dirección desde la que ese hilo debe comenzar a obtener el código.

Este mecanismo permite que cada hilo ejecute código desde una dirección diferente. Todo lo que se necesita es soporte de software para cada hilo para configurar sus propias tablas y colas de mensajería. El sistema operativo usa esos para hacer la programación multihilo real.

En lo que respecta al ensamblaje real, como escribió Nicholas, no hay diferencia entre los ensamblajes para una sola aplicación roscada o multi roscada. Cada hilo lógico tiene su propio conjunto de registros, por lo que escribe:

mov edx, 0

Solo actualizará EDX para el subproceso actualmente en ejecución. No hay forma de modificar EDX en otro procesador usando un solo ensamblado instrucción. Necesita algún tipo de llamada al sistema para pedirle al sistema operativo que le diga a otro subproceso que ejecute código que actualizará su propio EDX.

 114
Author: Nathan Fellman,
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-05-23 12:18:21

Según lo entiendo, cada "núcleo" es un procesador completo, con su propio conjunto de registros. Básicamente, la BIOS comienza con un núcleo en ejecución, y luego el sistema operativo puede "iniciar" otros núcleos inicializándolos y apuntándolos al código a ejecutar, etc.

La sincronización la realiza el sistema operativo. En general, cada procesador está ejecutando un proceso diferente para el sistema operativo, por lo que la funcionalidad de subprocesos múltiples del sistema operativo se encarga de decidir qué proceso se toca qué memoria, y qué hacer en caso de una colisión de memoria.

 42
Author: Nicholas Flynt,
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-06-11 13:21:22

Ejemplo de metal desnudo Intel x86 mínimo ejecutable

Ejemplo de metal desnudo ejecutable con todo el boilerplate requerido . Todas las partes principales se tratan a continuación.

Probado en Ubuntu 15.10 QEMU 2.3.0 y Lenovo ThinkPad T400.

El Manual Intel Volumen 3 System Programming Guide - 325384-056US Septiembre 2015 cubre SMP en los capítulos 8, 9 y 10.

Cuadro 8-1. "Broadcast INIT-SIPI-SIPI Sequence and Choice of Timeouts" contiene un ejemplo que básicamente solo funciona:

MOV ESI, ICR_LOW    ; Load address of ICR low dword into ESI.
MOV EAX, 000C4500H  ; Load ICR encoding for broadcast INIT IPI
                    ; to all APs into EAX.
MOV [ESI], EAX      ; Broadcast INIT IPI to all APs
; 10-millisecond delay loop.
MOV EAX, 000C46XXH  ; Load ICR encoding for broadcast SIPI IP
                    ; to all APs into EAX, where xx is the vector computed in step 10.
MOV [ESI], EAX      ; Broadcast SIPI IPI to all APs
; 200-microsecond delay loop
MOV [ESI], EAX      ; Broadcast second SIPI IPI to all APs
                    ; Waits for the timer interrupt until the timer expires

En ese código:

  1. La mayoría de los sistemas operativos harán que la mayoría de esas operaciones sean imposibles desde ring 3 (programas de usuario).

    Por lo tanto, necesita escribir su propio núcleo para jugar libremente con él: un programa Linux para usuarios no funcionará.

  2. Al principio, se ejecuta un solo procesador, llamado procesador bootstrap (BSP).

    Debe despertar a los otros (llamados Procesadores de aplicaciones (AP)) a través de especial interrupciones llamadas Interrupciones entre procesadores (IPI).

    Esas interrupciones se pueden hacer programando Advanced Programmable Interrupt Controller (APIC) a través del registro de comandos de interrupción (ICR)

    El formato del ICR está documentado en: 10.6"ISSUING INTERPROCESSOR INTERRUPTS"

    El IPI sucede tan pronto como escribimos al ICR.

  3. ICR_LOW se define en 8.4.4 "Ejemplo de inicialización MP" como:

    ICR_LOW EQU 0FEE00300H
    

    El valor mágico 0FEE00300 es la dirección de memoria del ICR, como se documenta en la Tabla 10-1"Mapa de Direcciones del Registro APIC Local"

  4. El método más simple posible se utiliza en el ejemplo: configura el ICR para enviar IPIs de difusión que se entregan a todos los demás procesadores excepto al actual.

    Pero también es posible, y recomendado por algunos , obtener información sobre los procesadores a través de estructuras de datos especiales configuradas por la BIOS como tablas ACPI o MP de Intel configuration table y solo activa las que necesites una por una.

  5. XX en 000C46XXH codifica la dirección de la primera instrucción que el procesador ejecutará como:

    CS = XX * 0x100
    IP = 0
    

    Recuerde que CS múltiples direcciones por 0x10, así que la dirección de memoria real de la primera instrucción es:

    XX * 0x1000
    

    Así que si por ejemplo XX == 1, el procesador comenzará en 0x1000.

    Entonces debemos asegurarnos de que hay código de modo real de 16 bits para ejecutarse en esa ubicación de memoria, por ejemplo, con:

    cld
    mov $init_len, %ecx
    mov $init, %esi
    mov 0x1000, %edi
    rep movsb
    
    .code16
    init:
        xor %ax, %ax
        mov %ax, %ds
        /* Do stuff. */
        hlt
    .equ init_len, . - init
    

    Usar un script enlazador es otra posibilidad.

  6. Los bucles de retardo son una parte molesta para ponerse a trabajar: no hay una manera súper simple de hacer tal duerme precisamente.

    Los métodos posibles incluyen:

    • PIT (usado en mi ejemplo)
    • HPET
    • calibrar el tiempo de un bucle ocupado con lo anterior, y usarlo en su lugar

    Relacionado: Cómo mostrar un número en la pantalla y y ¿dormir un segundo con el ensamblaje DOS x86?

  7. Creo que el procesador inicial necesita estar en modo protegido para que esto funcione mientras escribimos a address 0FEE00300H que es demasiado alto para 16 bits

  8. Para comunicarse entre procesadores, podemos usar un spinlock en el proceso principal, y modificar el bloqueo desde el segundo núcleo.

    Debemos asegurarnos de que la escritura de memoria se realiza, por ejemplo, a través de wbinvd.

Estado Compartido entre procesadores

8.7.1 "Estado de los Procesadores Lógicos", dice:

Las siguientes características son parte del estado arquitectónico de los procesadores lógicos dentro de los procesadores Intel 64 o IA-32 compatible con la tecnología Intel Hyper-Threading. Las características se pueden subdividir en tres grupos:

  • Duplicado para cada procesador lógico
  • Compartido por procesadores lógicos en un procesador físico
  • Compartido o duplicado, dependiendo de la aplicación

Las siguientes características se duplican para cada procesador lógico:

  • Registros de propósito general (EAX, EBX, ECX, EDX, ESI, EDI, ESP y EBP)
  • Registros de segmento (CS, DS, SS, ES, FS y GS)
  • EFLAGS y registros EIP. Tenga en cuenta que los registros CS y EIP/RIP para cada procesador lógico apuntan a la secuencia de instrucciones para el subproceso que está siendo ejecutado por el procesador lógico.
  • x87 FPU registros (ST0 a través de ST7, palabra de estado, palabra de control, palabra de etiqueta, puntero de operando de datos e instrucción pointer)
  • Registros MMX (MM0 a MM7)
  • Registros XMM (XMM0 a XMM7) y el registro MXCSR
  • Registros de control y registros de puntero de tabla del sistema (GDTR, LDTR, IDTR, registro de tareas)
  • Registros de depuración (DR0, DR1, DR2, DR3, DR6, DR7) y el control de depuración MSRs
  • Estado global de comprobación de máquina (IA32_MCG_STATUS) y capacidad de comprobación de máquina (IA32_MCG_CAP) MSRs
  • Modulación de reloj térmico y control de gestión de energía ACPI MSRs
  • Contador de marcas de tiempo MSRs
  • La mayoría de los otros registros MSR, incluyendo la tabla de atributos de página (PAT). Vea las excepciones a continuación.
  • Registros APIC locales.
  • Registros adicionales de propósito general (R8-R15), registros XMM (XMM8-XMM15), registro de control, IA32_EFER on Procesadores Intel 64.

Las siguientes características son compartidas por logical procesadores:

  • Registros de rango de tipo de memoria (MTRR)

Si las siguientes características son compartidas o duplicadas es específica de la implementación:

  • IA32_MISC_ENABLE MSR (dirección MSR 1A0H)
  • MSRs de arquitectura de comprobación de máquina (MCA) (excepto para los MSRs IA32_MCG_STATUS e IA32_MCG_CAP)
  • Control de monitoreo de rendimiento y contador MSRs

Se discute el uso compartido de caché at:

Los Intel hyperthreads tienen mayor uso compartido de caché y canalización que los núcleos separados: https://superuser.com/questions/133082/hyper-threading-and-dual-core-whats-the-difference/995858#995858

Linux kernel 4.2

La acción principal de inicialización parece estar en arch/x86/kernel/smpboot.c.

Ejemplos de BRAZO

ARM parece ser un poco más fácil de configurar que x86 ya que tiene menos sobrecarga histórica, aquí hay dos ejemplos mínimos ejecutables:

TODO: revisión esos ejemplos, y explicarlos mejor aquí.

Este documento proporciona algunas pautas sobre el uso de primitivas de sincronización ARM que luego puede usar para hacer cosas divertidas con múltiples núcleos: http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf

 40
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-07-11 06:50:41

Las preguntas frecuentes no oficiales sobre SMPlogotipo de desbordamiento de pila


Érase una vez, para escribir el ensamblador x86, por ejemplo, tendría instrucciones que indicaban "cargar el registro EDX con el valor 5", "incrementar el registro EDX", etc. Con las CPU modernas que tienen 4 núcleos (o incluso más), a nivel de código máquina ¿parece que hay 4 CPU separadas (es decir, hay solo 4 registros "EDX" distintos) ?

Exacto. Hay 4 conjuntos de registros, incluyendo 4 punteros de instrucción.

Si es así, cuando dices "incrementar el registro EDX", ¿qué determina qué registro EDX de la CPU se incrementa?

La CPU que ejecutó esa instrucción, naturalmente. Piense en ello como 4 microprocesadores completamente diferentes que simplemente comparten la misma memoria.

¿Hay un concepto de" contexto de CPU "o" hilo " en el ensamblador x86 ahora?

No. El ensamblador simplemente traduce instrucciones como siempre lo hizo. No allí cambia.

¿Cómo funciona la comunicación/sincronización entre los núcleos?

Dado que comparten la misma memoria, es principalmente una cuestión de lógica de programa. Aunque ahora hay un mecanismo de interrupción entre procesadores, no es necesario y no estaba originalmente presente en los primeros sistemas x86 de doble CPU.

Si estaba escribiendo un sistema operativo, qué mecanismo está expuesto a través del hardware para permitirle programar la ejecución en diferentes núcleos?

El planificador en realidad no cambia, excepto que es un poco más cuidadoso sobre las secciones críticas y los tipos de bloqueos utilizados. Antes de SMP, el código del kernel eventualmente llamaría al scheduler, que miraría la cola de ejecución y elegiría un proceso para ejecutarse como el siguiente hilo. (Los procesos del núcleo se parecen mucho a los hilos.) El kernel SMP ejecuta exactamente el mismo código, un hilo a la vez, es solo que ahora el bloqueo de secciones críticas debe ser seguro para SMP asegúrese de que dos núcleos no puedan recoger accidentalmente el mismo PID.

¿Es alguna instrucción especial privilegiada?

No. Los núcleos se ejecutan en la misma memoria con las mismas instrucciones antiguas.

Si estuviera escribiendo un compilador de optimización/VM de bytecode para una CPU multinúcleo, ¿qué necesitaría saber específicamente sobre, por ejemplo, x86 para que genere código que se ejecute de manera eficiente en todos los núcleos?

Tú corres igual. código como antes. Es el kernel de Unix o Windows lo que necesitaba cambiar.

Podría resumir mi pregunta como " ¿Qué cambios se han realizado en el código de máquina x86 para admitir la funcionalidad de varios núcleos?"

Nada era necesario. Los primeros sistemas SMP usaban exactamente el mismo conjunto de instrucciones que los uniprocesadores. Ahora, ha habido una gran cantidad de evolución de la arquitectura x86 y millones de nuevas instrucciones para hacer que las cosas vayan más rápido, pero ninguna era necesaria para SMP.

Para obtener más información, consulte la Especificación Intel Multiprocesador .


Actualización: todas las preguntas de seguimiento se pueden responder simplemente aceptando completamente que una CPU multinúcleo n-way es casi1 exactamente lo mismo que n procesadores separados que comparten la misma memoria.2 Hubo una pregunta importante que no se hizo: ¿cómo se escribe un programa para ejecutarse en más de un núcleo para obtener más rendimiento? Y la respuesta es: se escribe usando una biblioteca de hilos como Pthreads. Algunas bibliotecas de subprocesos usan "subprocesos verdes" que no son visibles para el sistema operativo, y no obtendrán núcleos separados, pero mientras la biblioteca de subprocesos use características de subprocesos del núcleo, su programa subproceso será automáticamente multinúcleo.
1. Para la compatibilidad con versiones anteriores, solo el primer núcleo se inicia en el reinicio, y se deben hacer algunas cosas del tipo de controlador para encender los restantes.
2. También comparten todos los periféricos, naturalmente.
 33
Author: DigitalRoss,
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
2016-09-16 23:00:08

Cada núcleo se ejecuta desde un área de memoria diferente. Su sistema operativo apuntará un núcleo a su programa y el núcleo ejecutará su programa. Su programa no será consciente de que hay más de un núcleo o en qué núcleo se está ejecutando.

Tampoco hay instrucciones adicionales disponibles solo para el Sistema Operativo. Estos núcleos son idénticos a los chips de un solo núcleo. Cada núcleo ejecuta una parte del Sistema operativo que se encargará de la comunicación a las áreas de memoria comunes utilizadas para intercambio de información para encontrar el siguiente área de memoria a ejecutar.

Esta es una simplificación, pero le da la idea básica de cómo se hace. Más sobre multicores y multiprocesadores en Embedded.com tiene mucha información sobre este tema ... Este tema se complica muy rápidamente!

 9
Author: Gerhard,
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-06-11 13:49:35

Si estuviera escribiendo una optimización compilador / bytecode VM para un multinúcleo CPU, ¿qué necesitas saber específicamente sobre, digamos, x86 para hacer genera código que se ejecuta de manera eficiente a través de todos los núcleos?

Como alguien que escribe VM de compilador de optimización/bytecode, puedo ayudarlo aquí.

No necesita saber nada específicamente sobre x86 para que genere código que se ejecute de manera eficiente en todos los núcleos.

Sin embargo, usted puede necesita saber sobre cmpxchg y amigos para escribir código que se ejecute correctamente en todos los núcleos. La programación multinúcleo requiere el uso de sincronización y comunicación entre hilos de ejecución.

Es posible que necesite saber algo sobre x86 para que genere código que se ejecute eficientemente en x86 en general.

Hay otras cosas que sería útil que aprendieras:

Usted debe aprender acerca de las instalaciones del sistema operativo (Linux o Windows o OSX) proporciona para permitirle ejecutar múltiples subprocesos. Debe aprender sobre las API de paralelización, como OpenMP y Threading Building Blocks, o el próximo "Grand Central"de OSX 10.6 "Snow Leopard".

Debe considerar si su compilador debe ser auto-paralelizado, o si el autor de las aplicaciones compiladas por su compilador necesita agregar una sintaxis especial o llamadas API en su programa para aprovechar los múltiples núcleos.

 9
Author: Alex Brown,
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-06-11 16:42:19

El código ensamblador se traducirá en código máquina que se ejecutará en un núcleo. Si quieres que sea multiproceso tendrás que usar primitivas del sistema operativo para iniciar este código en diferentes procesadores varias veces o diferentes piezas de código en diferentes núcleos - cada núcleo ejecutará un subproceso separado. Cada hilo solo verá un núcleo en el que se está ejecutando actualmente.

 5
Author: sharptooth,
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-06-11 13:21:19

No se hace en las instrucciones de la máquina en absoluto; los núcleos pretenden ser CPU distintas y no tienen ninguna capacidad especial para hablar entre sí. Hay dos maneras de comunicarse:

  • Comparten el espacio de direcciones físicas. El hardware maneja la coherencia de la caché, por lo que una CPU escribe en una dirección de memoria que otra lee.

  • Comparten un APIC (programmable interrupt controller). Esta es la memoria mapeada en el espacio de direcciones físicas, y puede ser utilizado por un procesador para controlar los otros, encenderlos o apagarlos, enviar interrupciones, etc.

Http://www.cheesecake.org/sac/smp.html es una buena referencia con una url tonta.

 2
Author: pjc50,
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-10-27 13:56:55

La principal diferencia entre una aplicación single - y una multi-thread es que la primera tiene una pila y la segunda tiene una para cada hilo. El código se genera de manera algo diferente ya que el compilador asumirá que los registros de datos y segmentos de pila (ds y ss) no son iguales. Esto significa que la indirección a través de los registros ebp y esp que por defecto al registro ss no también por defecto a ds (porque ds!= ss). Por el contrario, indirección a través de los otros registros que por defecto a ds no por defecto a ss.

Los subprocesos comparten todo lo demás, incluidas las áreas de datos y código. También comparten rutinas lib, así que asegúrate de que sean seguras para los hilos. Un procedimiento que ordena un área en RAM puede ser multihilo para acelerar las cosas. Los subprocesos accederán, compararán y ordenarán los datos en la misma área de memoria física y ejecutarán el mismo código pero usarán diferentes variables locales para controlar su respectiva parte de la ordenación. Esto, por supuesto, es porque los hilos tienen diferentes pilas donde están contenidas las variables locales. Este tipo de programación requiere un ajuste cuidadoso del código para que las colisiones de datos entre núcleos (en cachés y RAM) se reduzcan, lo que a su vez resulta en un código que es más rápido con dos o más subprocesos que con solo uno. Por supuesto, un código sin afinar a menudo será más rápido con un procesador que con dos o más. Depurar es más difícil porque el punto de interrupción estándar "int 3" no será aplicable ya que desea interrumpir un hilo específico y no todos ellos. Los puntos de interrupción del registro de depuración tampoco resuelven este problema a menos que pueda configurarlos en el procesador específico que ejecuta el subproceso específico que desea interrumpir.

Otro código multihilo puede involucrar diferentes subprocesos que se ejecutan en diferentes partes del programa. Este tipo de programación no requiere el mismo tipo de ajuste y, por lo tanto, es mucho más fácil de aprender.

 1
Author: Olof Forshell,
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
2011-02-22 12:25:43

Lo que se ha agregado en cada arquitectura compatible con multiprocesamiento en comparación con las variantes de un solo procesador que vinieron antes de ellas son instrucciones para sincronizar entre núcleos. Además, tiene instrucciones para lidiar con la coherencia de la caché, los búferes de lavado y operaciones similares de bajo nivel con las que tiene que lidiar un sistema operativo. En el caso de arquitecturas multihilo simultáneas como IBM POWER6, IBM Cell, Sun Niagara e Intel "Hyperthreading", también tiende a ver nuevas instrucciones para priorizar entre subprocesos (como establecer prioridades y ceder explícitamente al procesador cuando no hay nada que hacer).

Pero la semántica básica de un solo hilo es la misma, solo agrega facilidades adicionales para manejar la sincronización y la comunicación con otros núcleos.

 0
Author: jakobengblom2,
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-08-18 18:20:46