"Diseño e implementación del kernel de Linux" lectura de notas: interrupción y manejo de interrupciones

Interrumpir

El mecanismo por el cual el hardware envía señales al kernel cuando es necesario se llama mecanismo de interrupción .

El mecanismo de interrupción se utiliza para la comunicación entre el dispositivo y el kernel.

La interrupción es esencialmente una señal eléctrica especial enviada al procesador por el dispositivo de hardware.

Una vez que el procesador recibe la interrupción, reflejará inmediatamente la llegada de esta señal al sistema operativo, y luego el sistema operativo es responsable de procesar estos datos recién llegados.

Las interrupciones se pueden generar en cualquier momento, por lo que el kernel puede interrumpirse en cualquier momento debido a una nueva interrupción de llegada, por lo que es necesario asegurarse de que el manejador de interrupciones se pueda ejecutar rápidamente.

Una interrupción es una señal eléctrica, generada por un dispositivo de hardware y enviada directamente al pin de entrada del controlador de interrupciones.

El controlador de interrupciones es un simple chip electrónico cuya función es utilizar múltiples tuberías de interrupción para comunicarse con el procesador a través de una sola tubería conectada al procesador.

Después de recibir la señal, el controlador de interrupciones envía una señal eléctrica al procesador.

Cuando el procesador detecta esta señal, interrumpe su trabajo actual y procesa la interrupción.

Los diferentes dispositivos corresponden a diferentes interrupciones y cada interrupción se identifica con un número único.

Este número se denomina número de solicitud de interrupción (IRQ) .

El número de solicitud de interrupción está fijo en una PC clásica. Por ejemplo, IRQ0 es una interrupción de reloj e IRQ1 es una interrupción de teclado; también se puede asignar dinámicamente, como una interrupción de un dispositivo en el bus PCI.

 

anormal

Las excepciones también se denominan interrupciones síncronas. (Por el contrario, las interrupciones antes mencionadas se denominan interrupciones asincrónicas).

Lo genera el propio procesador.

El manejo de las interrupciones es básicamente el mismo que el de las excepciones. (Como se mencionó en el capítulo anterior de llamadas al sistema, las llamadas al sistema se pueden activar mediante interrupciones)

 

Controlador de interrupciones

El kernel ejecuta controladores de interrupciones o rutinas de servicio de interrupciones para responder a interrupciones específicas.

El manejador de interrupciones es para la interrupción, no para el dispositivo que generó la interrupción.

Si un dispositivo genera múltiples interrupciones, existen múltiples manejadores de interrupciones.

El controlador de interrupciones del dispositivo lo proporciona su controlador de dispositivo .

En Linux, los controladores de interrupciones son funciones de C ordinarias, pero con declaraciones de tipo específicas.

 

Mitad superior e inferior

El manejador de interrupciones debe ejecutarse rápidamente, pero el manejador de interrupciones puede necesitar lidiar con más carga de trabajo.

Para resolver este problema, Linux proporciona la mitad superior e inferior del controlador de interrupciones:

Parte superior : ejecutar inmediatamente después de recibir la interrupción, pero solo trabajar con un límite de tiempo estricto;

Mitad inferior : La ejecución se interrumpirá en el momento adecuado.

 

Controlador de interrupciones de registro

El manejador de interrupciones es parte del controlador.

El controlador llamará a request_irq () para registrar el controlador de interrupciones (incluya \ linux \ interrupt.h):

static inline int __must_check

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,

        const char *name, void *dev)

irq : indica el número de interrupción que se asignará. Para los dispositivos de PC tradicionales , su valor se determina; para la mayoría de los dispositivos, este valor se adquiere mediante detección o se puede determinar dinámicamente mediante programación .

manejador : manejador de interrupciones:

typedef irqreturn_t (*irq_handler_t)(int, void *);

banderas : banderas de interrupción, tienen diferentes funciones:

/*
 * These flags used only by the kernel as part of the
 * irq handling routines.
 *
 * IRQF_DISABLED - keep irqs disabled when calling the action handler
 * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
 * IRQF_SHARED - allow sharing the irq among several devices
 * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
 * IRQF_TIMER - Flag to mark this interrupt as timer interrupt
 * IRQF_PERCPU - Interrupt is per cpu
 * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
 * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
 *                registered first in an shared interrupt is considered for
 *                performance reasons)
 * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
 *                Used by threaded interrupts which need to keep the
 *                irq line disabled until the threaded handler has been run.
 */
#define IRQF_DISABLED       0x00000020
#define IRQF_SAMPLE_RANDOM  0x00000040
#define IRQF_SHARED            0x00000080
#define IRQF_PROBE_SHARED   0x00000100
#define IRQF_TIMER             0x00000200
#define IRQF_PERCPU            0x00000400
#define IRQF_NOBALANCING    0x00000800
#define IRQF_IRQPOLL        0x00001000
#define IRQF_ONESHOT        0x00002000

nombre : representación de texto ASCII del dispositivo relacionado con la interrupción.

dev : se utiliza para compartir interrupciones.

  • Cuando es necesario liberar un manejador de interrupciones, dev proporcionará información de bandera única para eliminar el especificado de los muchos manejadores de interrupciones que comparten la línea de interrupción.
  • Además, el kernel le pasará este puntero cada vez que se llame al controlador de interrupciones para identificar qué dispositivo necesita ejecutar el controlador de interrupciones. En la práctica, a menudo apunta a la estructura del dispositivo del controlador.
  • Si no es necesario compartir la línea de interrupción, su valor se establece en nulo.

La función request_irq () puede estar inactiva, por lo tanto, no se puede llamar en el contexto de interrupción u otro código que no esté permitido bloquear.

Al desinstalar el controlador, debe cancelar la función de manejo de interrupciones correspondiente:

extern void free_irq(unsigned int, void *);

Si el número de interrupción no se comparte, se desactivará después de eliminar el controlador de interrupciones.

 

Controlador de interrupciones compartido

Deben cumplirse los siguientes puntos:

  1. El indicador de parámetro de request_irq () debe establecerse con el indicador IRQF_SHARED;
  2. El parámetro dev debe ser único;
  3. El manejador de interrupciones debe poder distinguir si su dispositivo genera realmente una interrupción, lo que requiere soporte de hardware y lógica de procesamiento relacionada en el manejador;

Una vez que el kernel recibe una interrupción, llamará secuencialmente a cada controlador registrado en la línea de interrupción. Esta es también la razón del punto 3 anterior.

 

Controlador de interrupciones

un ejemplo:

/*
 * Handler for RTC timer interrupt
 */
static irqreturn_t
timer_interrupt(int irq, void *dev_id)
{
    struct pt_regs *regs = get_irq_regs();
    do_timer(1);
#ifndef CONFIG_SMP
    update_process_times(user_mode(regs));
#endif
    do_profile(regs);
    RTC_RTCC = 0;               /* Clear interrupt */
    return IRQ_HANDLED;
}

El controlador de interrupciones suele ser estático porque el código no lo llamará directamente en otros archivos.

El controlador de interrupciones devuelve un valor específico (incluya \ linux \ irqreturn.h):

/**
 * enum irqreturn
 * @IRQ_NONE        interrupt was not from this device
 * @IRQ_HANDLED     interrupt was handled by this device
 * @IRQ_WAKE_THREAD handler requests to wake the handler thread
 */
enum irqreturn {
    IRQ_NONE,
    IRQ_HANDLED,
    IRQ_WAKE_THREAD,
};

No es necesario volver a ingresar al controlador de interrupciones en Linux , porque cuando el controlador de interrupciones se está ejecutando, la línea de interrupción correspondiente estará protegida en todos los procesadores.

 

Interrumpir contexto

El contexto de interrupción no puede dormir, por lo que no puede usar funciones que pueden dormir en la función de procesamiento de interrupciones.

El contexto de interrupción tiene un límite de tiempo relativamente estricto,

El manejador de interrupciones tiene su propia pila, una para cada procesador, de una página de tamaño, llamada pila de interrupciones.

El manejador de interrupciones no necesita preocuparse por cómo está configurada la pila, o el tamaño de la pila del núcleo, pero debe intentar ahorrar espacio en la pila del núcleo.

 

Entrada del programa de interrupción del procesamiento del kernel

La interrupción comienza en el punto de entrada predefinido en la plataforma específica (arch \ x86 \ kernel \ entry_32.S):

.section .init.rodata,"a"
ENTRY(interrupt)
.text
    .p2align 5
    .p2align CONFIG_X86_L1_CACHE_SHIFT
ENTRY(irq_entries_start)
    RING0_INT_FRAME
vector=FIRST_EXTERNAL_VECTOR
.rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7
    .balign 32
  .rept 7
    .if vector < NR_VECTORS
      .if vector <> FIRST_EXTERNAL_VECTOR
    CFI_ADJUST_CFA_OFFSET -4
      .endif
1:  pushl $(~vector+0x80)   /* Note: always in signed byte range */
    CFI_ADJUST_CFA_OFFSET 4
      .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6
    jmp 2f
      .endif
      .previous
    .long 1b
      .text
vector=vector+1
    .endif
  .endr
2:  jmp common_interrupt
.endr
END(irq_entries_start)
.previous
END(interrupt)
.previous
/*
 * the CPU automatically disables interrupts when executing an IRQ vector,
 * so IRQ-flags tracing has to follow that:
 */
    .p2align CONFIG_X86_L1_CACHE_SHIFT
common_interrupt:
    addl $-0x80,(%esp)  /* Adjust vector into the [-256,-1] range */
    SAVE_ALL
    TRACE_IRQS_OFF
    movl %esp,%eax
    call do_IRQ
    jmp ret_from_intr
ENDPROC(common_interrupt)
    CFI_ENDPROC

El do_IRQ aquí corresponde al código C. Tome arch \ x86 \ kernel \ irq.c como ejemplo:

unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    /* high bit used in ret_from_ code  */
    unsigned vector = ~regs->orig_ax;
    unsigned irq;
    exit_idle();
    irq_enter();
    irq = __get_cpu_var(vector_irq)[vector];
    if (!handle_irq(irq, regs)) {
        ack_APIC_irq();
        if (printk_ratelimit())
            pr_emerg("%s: %d.%d No irq handler for vector (irq %d)\n",
                __func__, smp_processor_id(), vector, irq);
    }
    irq_exit();
    set_irq_regs(old_regs);
    return 1;
}

Durante la ejecución del manejador de interrupciones, las interrupciones (parciales o todas) serán blindadas, pero estas interrupciones no serán abandonadas, el manejador de interrupciones almacenará estas interrupciones hasta que la CPU esté libre y luego enviada.

 

/ proc / interrupciones

procfs es un sistema de archivos virtual, solo existe en la memoria del kernel, generalmente instalado en el directorio / proc.

Para leer y escribir archivos en procfs, se deben llamar las funciones del núcleo, que simulan la lectura o escritura de archivos reales.

El archivo / proc / interrunpts es un ejemplo, que almacena estadísticas relacionadas con interrupciones en el sistema:

 

Interrumpir la operación de control

El kernel de Linux proporciona un conjunto de interfaces para operar el estado de interrupción en la máquina.

 

Supongo que te gusta

Origin blog.csdn.net/jiangwei0512/article/details/106144564
Recomendado
Clasificación