¿Cómo se generan, asignan y usan las frecuencias IRQ en una computadora?

tldr; esto se maneja a través del enrutamiento de interrupción, con la ayuda de un controlador de interrupción programable (PIC). Cada dispositivo, como una tarjeta de red, tiene una línea de interrupción que debe asignarse a un número limitado de números de solicitud de interrupción (IRQ) en la CPU. Dado que el sistema operativo debe funcionar en una variedad de placas base y dispositivos, el PIC, el BIOS y el sistema operativo trabajan juntos para asignar la línea de interrupción de cada dispositivo a un número IRQ, evitando conflictos en el proceso.


Una CPU consta de un número limitado de entradas de interrupción. Una CPU moderna x86 generalmente tiene alrededor de 24 líneas de interrupción de hardware. Estas líneas están etiquetadas como “IRQ” más un número, como IRQ0 o IRQ2.

Un dispositivo como una interfaz de red o un teclado solicita la atención de la CPU mediante una interrupción enviada a una de estas muchas líneas. Cuando la CPU recibe una interrupción, ejecuta un controlador de interrupción que sabe exactamente en qué lugar de la memoria buscar cualquier dato asociado con dicha interrupción (como la tecla presionada cuando el teclado envía una interrupción a la CPU).

Ahora, por supuesto, si varios dispositivos se comunicaran a través de la misma línea IRQ, existe el riesgo de que la CPU ejecute el controlador de interrupciones para el dispositivo incorrecto, lo que puede ocasionar todo tipo de problemas (como conflictos IRQ). Por lo tanto, es necesario que el sistema operativo sepa, de antemano, la asignación entre la línea de interrupción de un dispositivo y la entrada de interrupción de la CPU.

Este mapeo se realiza utilizando un controlador de interrupción programable (PIC) . En los sistemas x86 modernos, esto corresponde a un Controlador de interrupción programable avanzado (APIC), basado en la arquitectura APIC de Intel.

En pocas palabras, cada dispositivo que necesita enviar una interrupción a la CPU, como una interfaz de red PCI, generalmente tiene cuatro interrupciones INTA, INTB … INTD. Cada ranura PCI en la placa base corresponde a un número 0, 1…. . Por lo tanto, una interrupción proveniente de un dispositivo PCI ahora se identifica mediante la ranura PCI y la interrupción del dispositivo. Esto significa que una interrupción desde una interfaz de red en la ranura PCI 3 será, por ejemplo, “3A”.

Las líneas de interrupción de diferentes dispositivos en la placa base y las ranuras PCI están conectadas a enlaces de interrupción (o enlaces de entrada) en un PIC. Esto está cableado y se espera que el BIOS sepa cómo se ve este cableado. Estos enlaces de entrada están etiquetados como LNKA, LNKB, … y así sucesivamente. El PIC realiza el paso final desde la interrupción de la interrupción de un dispositivo hasta la recepción de una IRQ en la CPU, utilizando el enrutamiento de interrupción . Es decir, el PIC tiene una tabla de enrutamiento que asigna un enlace de entrada a un número IRQ para enviarlo a la CPU. Esto, hasta cierto punto, es configurable. Tenga en cuenta que algunos números IRQ están reservados (como IRQ0 para el temporizador del sistema e IRQ1 para el teclado), y el PIC debe trabajar con los números IRQ no reservados para este enrutamiento.

Podemos ver cómo se ve esta tabla de enrutamiento en mi caja de Linux:

~ ❯ dmesg | grep -i interrupt
[0.168194] ACPI: uso de IOAPIC para el enrutamiento de interrupciones
[0.181045] ACPI: Enlace de interrupción PCI [LNKA] (IRQs 3 4 5 6 7 9 10 * 11)
[0.181145] ACPI: Enlace de interrupción PCI [LNKB] (IRQs 3 4 5 6 * 7 9 10 11)
[0.181241] ACPI: Enlace de interrupción PCI [LNKC] (IRQs 3 4 5 6 * 7 9 10 11)
[0.181338] ACPI: Enlace de interrupción PCI [LNKD] (IRQ 3 4 5 6 7 9 10 * 11)
[0.181417] ACPI: Enlace de interrupción PCI [LNKE] (IRQs 3 4 5 6 7 9 10 11) * 0, deshabilitado.
[0.181514] ACPI: Enlace de interrupción PCI [LNKF] (IRQs 3 4 5 6 7 9 10 * 11)
[0.181610] ACPI: Enlace de interrupción PCI [LNKG] (IRQs 3 4 5 6 7 9 * 10 11)
[0.181706] ACPI: Enlace de interrupción PCI [LNKH] (IRQs 3 4 5 6 7 9 * 10 11)

Aquí, vemos ocho enlaces de interrupción diferentes, etiquetados como LNKA a LNKH. El número IRQ al que se asigna un enlace de interrupción está marcado con un asterisco * en la lista de números.

Tenga en cuenta, sin embargo, que se están asignando múltiples enlaces de interrupción al mismo número IRQ. ¿No es esto realmente un conflicto IRQ? No necesariamente.

Los buses PCI modernos admiten el intercambio de IRQ, en el que varios dispositivos pueden terminar usando el mismo número de IRQ. Cuando una CPU recibe una IRQ compartida, la CPU ejecuta todas las rutinas de servicio (llamadas una cadena) correspondientes a ese número de IRQ, una después de la otra. Cada rutina de servicio mira los registros del dispositivo correspondiente para ver si realmente emitió una interrupción, y si no, sale y permite que la siguiente rutina de servicio realice una verificación similar, hasta que uno de ellos tenga éxito.

Otra dimensión del problema de enrutamiento de interrupción es tener múltiples núcleos de CPU. ¿Qué núcleo de CPU realmente recibe una IRQ de un dispositivo? Esto se puede observar en Linux a través de / proc / interrupts. Nuevamente, aquí hay una muestra de mi computadora portátil:

~ ❯ sudo cat / proc / interrupciones

CPU0 CPU1 CPU2 CPU3
0: 18 0 0 0 Temporizador de borde IR-IO-APIC
1: 21559 8449 1329 1433 IR-IO-APIC-edge i8042
8: 0 0 0 1 IR-IO-APIC-edge rtc0
9: 1863310 154172 50582 42139 IR-IO-APIC-fasteoi acpi
12: 1960938 133267 38141 43995 IR-IO-APIC-edge i8042
16: 272302 280520 2832 12080 IR-IO-APIC-fasteoi ehci_hcd: usb1, mmc0
19: 7 2 3 1 IR-IO-APIC-fasteoi
23: 127 35 15 72 IR-IO-APIC-fasteoi ehci_hcd: usb2
40: 0 0 0 0 DMAR_MSI-edge dmar0
41: 0 0 0 0 DMAR_MSI-edge dmar1
42: 10980 2397 249806 262777 IR-PCI-MSI-edge xhci_hcd
43: 45212 56782 72595 143901 IR-PCI-MSI-edge ahci
44: 6 20 0 0 IR-PCI-MSI-edge mei_me
45: 521394 1838604 50802 51272 IR-PCI-MSI-edge i915
46: 4440024159 44 73 IR-PCI-MSI-edge iwlwifi
47: 101150 622530 0 0 IR-PCI-MSI-edge snd_hda_intel
NMI: 135 827 771 740 Interrupciones no enmascarables
LOC: 11668631 8405525 11512330 8035213 Interrupciones de temporizador local
SPU: 0 0 0 0 interrupciones espurias
PMI: 135 827 771 740 Interrupciones de monitoreo de rendimiento
IWI: 349936 316196 341605 364736 IRQ interrupciones de trabajo
RTR: 0 0 0 0 APIC ICR reintentos de lectura
RES: 1767309 1681185 1974605 1766898 Reprogramación de interrupciones
CAL: 11839 55379 59806 59143 Interrupciones de llamadas de función
TLB: 783909 733032 763796 762143 derribos TLB
TRM: 0 0 0 0 interrupciones de eventos térmicos
THR: 0 0 0 0 Umbral de interrupciones APIC
MCE: 0 0 0 0 Excepciones de verificación de máquina
MCP: 284 279 279 279 encuestas de verificación de máquinas
ERR: 0
SIG: 0

La primera entrada de cada fila es el número / etiqueta IRQ. Los siguientes cuatro son el número de IRQ de ese tipo recibidas por cada núcleo de CPU. Esto es seguido por el tipo de APIC y el tipo de disparador de interrupción (borde o fin de interrupción). ¿Recuerdas que mencioné que IRQ0 se usaba para el temporizador del sistema y que IRQ1 se usaba para un teclado? Eso es lo que ve en las dos primeras líneas de la salida anterior (i8042 es un controlador de teclado para Linux).

Aquí hay un problema de equilibrio de carga, con el enrutamiento de IRQ a núcleos de CPU. Por ejemplo, mire el núcleo 0 de la CPU para IRQ1, está obteniendo muchas más IRQ que los otros núcleos.

Espero que esto le brinde una descripción general de cómo se enrutan las interrupciones de los dispositivos a las CPU.

Fuentes:
http://en.wikipedia.org/wiki/Int…
http://en.wikipedia.org/wiki/Adv…
http://www.tldp.org/HOWTO/Plug-a…
http://www.tldp.org/HOWTO/Unix-a…
http://www.tldp.org/HOWTO/Plug-a…
http://wiki.osdev.org/Interrupts