"Diseño e implementación del kernel de Linux" Lectura de notas: temporizador y gestión del tiempo

Golpear

El temporizador del sistema se dispara a una cierta frecuencia, que se define mediante un preprocesamiento estático, denominado frecuencia de pulsación, también denominada HZ (Hertz).

El valor HZ es diferente en diferentes arquitecturas.

El valor predeterminado de HZ en la arquitectura x86 es 100 (incluya \ asm-generic \ param.h), por lo que la frecuencia de la interrupción del reloj en x86 es 100Hz, es decir, se activan 100 interrupciones cada segundo.

#ifndef HZ
#define HZ 100
#endif

jiffies se usa para registrar un resumen de los latidos desde que se inició el sistema (incluya \ linux \ jiffies.h), y también hay una versión de 64 bits jiffies_64.

extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;

El valor que aumenta jiffies en un segundo es HZ.

Los jiffies son de 32 bits, por lo que puede haber un desbordamiento.

Varias macros pueden manejar correctamente el envolvente causado por el desbordamiento:

#define time_after(a,b)     \
    (typecheck(unsigned long, a) && \
     typecheck(unsigned long, b) && \
     ((long)(b) - (long)(a) < 0))
#define time_before(a,b)    time_after(b,a)
#define time_after_eq(a,b)  \
    (typecheck(unsigned long, a) && \
     typecheck(unsigned long, b) && \
     ((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)

 

Reloj duro y temporizador

El reloj de tiempo real (RTC) se utiliza para almacenar permanentemente la hora del sistema.

El kernel inicializa el tiempo de la pared leyendo el RTC, que se almacena en la variable xtime (incluye \ linux \ time.h):

#ifndef _STRUCT_TIMESPEC
#define _STRUCT_TIMESPEC
struct timespec {
    __kernel_time_t tv_sec;         /* seconds */
    long        tv_nsec;        /* nanoseconds */
};
#endif
extern struct timespec xtime;

tv_sec almacena el tiempo transcurrido desde 19700101 en segundos;

tv_nsec registra el número de ns transcurridos desde el último segundo;

x86 almacenará periódicamente el valor de tiempo actual en RTC.

El temporizador del sistema proporciona un mecanismo para activar interrupciones periódicamente.

También hay un reloj APIC y un contador de marcas de tiempo (TSC) en x86.

El temporizador es la base para administrar el tiempo transcurrido del kernel.

El temporizador no se ejecuta periódicamente, se cancela después del tiempo de espera.

El temporizador tiene la estructura time_list para indicar:

struct timer_list {
    struct list_head entry;
    unsigned long expires;
    void (*function)(unsigned long);
    unsigned long data;
    struct tvec_base *base;
#ifdef CONFIG_TIMER_STATS
    void *start_site;
    char start_comm[16];
    int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

Definición de temporizador:

#define DEFINE_TIMER(_name, _function, _expires, _data)     \
    struct timer_list _name =               \
        TIMER_INITIALIZER(_function, _expires, _data)

Inicialización del temporizador:

#define init_timer(timer)                       \
    do {                                \
        static struct lock_class_key __key;         \
        init_timer_key((timer), #timer, &__key);        \
    } while (0)
#define init_timer(timer)\
    init_timer_key((timer), NULL, NULL)

Activar el temporizador:

extern void add_timer(struct timer_list *timer);

Cambiar temporizador:

extern int mod_timer(struct timer_list *timer, unsigned long expires);

Detén el cronómetro:

extern int del_timer(struct timer_list * timer);

Para multiprocesamiento, los temporizadores personalizados se utilizan mejor de la siguiente manera:

#ifdef CONFIG_SMP
  extern int del_timer_sync(struct timer_list *timer);

El kernel ejecuta el temporizador después de que se produce la interrupción del reloj y el temporizador se ejecuta como una interrupción suave (TIMER_SOFTIRQ) en el contexto de la mitad inferior.

 

Controlador de interrupción de reloj

El manejador de interrupciones de reloj se divide en dos partes, una parte está relacionada con la arquitectura y la otra parte no tiene nada que ver con la arquitectura.

La primera parte:

  • Obtenga el bloqueo xtime_lock (es un bloqueo seq) para que la otra parte pueda proteger jiffies_64 y wall time xtime;
  • Responda o reinicie el reloj del sistema cuando sea necesario;
  • Actualice periódicamente el reloj en tiempo real con la hora de la pared;
  • Llame a la rutina de reloj independiente de la arquitectura tick_periodic ();

La última parte es tick_periodic ():

static void tick_periodic(int cpu)
{
    if (tick_do_timer_cpu == cpu) {
        write_seqlock(&xtime_lock);
        /* Keep track of the next tick event */
        tick_next_period = ktime_add(tick_next_period, tick_period);
        do_timer(1);
        write_sequnlock(&xtime_lock);
    }
    update_process_times(user_mode(get_irq_regs()));
    profile_tick(CPU_PROFILING);
}

Operación general:

  • Agregue 1 a la variable jiffies_64;
  • Actualizar estadísticas de consumo de recursos;
  • Ejecute un temporizador dinámico que ha expirado;
  • Ejecute la función sheduler_tick ();
  • Actualice el tiempo del muro, que se almacena en la variable xtime;
  • Calcule el valor de carga promedio;

 

Ejecución retrasada

Hay muchas formas de retrasar:

Ocupado esperando:

static void
fore200e_spin(int msecs)
{
    unsigned long timeout = jiffies + msecs_to_jiffies(msecs);
    while (time_before(jiffies, timeout));
}

Permita que el kernel reprograme tareas más importantes mientras el código espera:

    for (;;) {
        smcr = ioread16(dev->base + SMCR);
        /*
         * Don't bother checking ACKE here, this and the reset
         * are handled in highlander_i2c_wait_xfer_done() when
         * waiting for the ACK.
         */
        if (smcr & SMCR_IRIC)
            return;
        if (time_after(jiffies, timeout))
            break;
        cpu_relax();
        cond_resched();
    }

La función cond_resched () programará la ejecución de un nuevo programa. No se puede utilizar en contexto de interrupción, solo en contexto de proceso.

Retraso breve:

udelay(100);

Duerme hasta la hora especificada:

set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);

Este método no puede garantizar que el tiempo de reposo sea exactamente igual al tiempo de retardo especificado y solo puede ser lo más cercano posible.

 

Supongo que te gusta

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