170 likes | 314 Views
MINIX3 CLOCK TASK Cátedra: Diseño e Implementación de Sistemas Operativos UTN-FRSF Tomado de: Operating Systems Design and Implementation, Third Edition. Generalidades. Los temporizadores son esenciales en los Sistemas Operativos Mantener Fecha y Hora Time slice Timeouts Alarmas
E N D
MINIX3CLOCK TASKCátedra: Diseño e Implementación de Sistemas Operativos UTN-FRSFTomado de: Operating Systems Design and Implementation, Third Edition
Generalidades • Los temporizadores son esenciales en los Sistemas Operativos • Mantener Fecha y Hora • Time slice • Timeouts • Alarmas • Tareas periódicas • Accounting • Estadísticas • La CLOCK Task se compila en el mismo binario que el kernel y accede a todas sus variables y funciones. • El término correcto es TIMER • Se dispone de un único temporizador en Hardware por lo que hay que virtualizar en varios temporizadores por Software.
Programmable Interrupt Timer • Dispositivo que permite generar Interrupciones en un plazo especificado. • Puede programarse en modo: • ONE SHOT: Se generará una única interrupción en un momento dado. • SQUARE WAVE: Se generarán interrupciones periódicas. TIMER_FREQ 1193182 Hz
Mantener la Fecha/Hora • Contador de 64 bits => Realizar una suma de 64 bits en cada Tick del Timer • Segundos y Ticks separados => Realiza una suma de 16 bits cada Tick del Timer y una de 32 bits cada segundo • Relativo al tiempo de booteo: Se cuentan solo los ticks desde que el sistema booteo en un contador de 32 bits. (MINIX3)
Temporizadores Software • Tabla de Vencimientos => Realizar un rastreo de toda la tabla en cada Tick del Timer O(n). Rápida inserción/remoción O(1). • Lista Ordenada por Vencimiento => Solo se decrementa el tiempo al vencimiento del primer temporizador O(1). Compleja inserción/remoción O(n).
Timeslice/Watchdogs • Cuando se inicia un proceso, se inicializa un contador con el timeslice en Tics para ese proceso. • Para cada interrupción del reloj (Tick), la TIMER ISR decrementará en 1 el valor del contador. • Cuando este contador llegue a tener valor 0, envía un notify() a CLOCK TASK para quitar al proceso de la cabeza de la Cola de READY e insertarlo al final de ella. • El planificador que se encargará de poner en marcha otro proceso
Accounting • Cada vez que se inicia un proceso de USUARIO se mantienen dos contadores (p_user_time, p_sys_time) para contabilizar el uso de CPU. • Por cada Tick se actualizan los contadores según si se ha producido una interrupción de Reloj en modo usuario o en modo kernel • Modo Usuario: Se incrementa p_user_time del proceso de Usuario • Modo Kernel: Se incrementa p_sys_time del proceso de Usuario y el p_user_time del proceso de Sistema interrumpido
Tarea de Reloj lost_ticks: Lleva la cuenta de ticks que pudiesen haberse perdido a causa de desactivar las interrupciones usando el MONITOR y la BIOS (no es útil). realtime: Permite calcular la hora del día actual. proc_ptr->p_ticks_left: Se decrementa en cada tick para controlar el cuanto de ejecución de un proceso en la CPU. next_timeout: Esta variable registra cuando podrá ocurrir la próxima alarma.
Clock Task 10465 /*===========================================================================* 10466 * clock_task * 10467 *===========================================================================*/ 10468 PUBLIC void clock_task() 10469 { 10470 /* Main program of clock task. If the call is not HARD_INT it is an error. 10471 */ 10472 message m; /* message buffer for both input and output */ 10473 int result; /* result returned by the handler */ 10474 10475 init_clock(); /* initialize clock task */ 10476 10477 /* Main loop of the clock task. Get work, process it. Never reply. */ 10478 while (TRUE) { 10479 10480 /* Go get a message. */ 10481 receive(ANY, &m); 10482 10483 /* Handle the request. Only clock ticks are expected. */ 10484 switch (m.m_type) { 10485 case HARD_INT: 10486 result = do_clocktick(&m); /* handle clock tick */ 10487 break; 10488 default: /* illegal request type */ 10489 kprintf("CLOCK: illegal request %d from %d.\n", m.m_type,m.m_source); 10490 } 10491 }
Clock Task 10494 /*===========================================================================* 10495 * do_clocktick * 10496 *===========================================================================*/ 10497 PRIVATE int do_clocktick(m_ptr) 10498 message *m_ptr; /* pointer to request message */ 10499 { 10500 /* Despite its name, this routine is not called on every clock tick. It 10501 * is called on those clock ticks when a lot of work needs to be done. 10502 */ 10503 10504 /* A process used up a full quantum. The interrupt handler stored this 10505 * process in 'prev_ptr'. First make sure that the process is not on the 10506 * scheduling queues. Then announce the process ready again. Since it has 10507 * no more time left, it gets a new quantum and is inserted at the right 10508 * place in the queues. As a side-effect a new process will be scheduled. 10509 */ 10510 if (prev_ptr->p_ticks_left <= 0 && priv(prev_ptr)->s_flags & PREEMPTIBLE) { 10511 lock_dequeue(prev_ptr); /* take it off the queues */ 10512 lock_enqueue(prev_ptr); /* and reinsert it again */ 10513 } 10514 10515 /* Check if a clock timer expired and run its watchdog function. */ 10516 if (next_timeout <= realtime) { 10517 tmrs_exptimers(&clock_timers, realtime, NULL); 10518 next_timeout = clock_timers == NULL ? 10519 TMR_NEVER : clock_timers->tmr_exp_time; 10520 } 10521 10522 /* Inhibit sending a reply. */ 10523 return(EDONTREPLY); 10524 }
Clock Task 10526 /*===========================================================================* 10527 * init_clock * 10528 *===========================================================================*/ 10529 PRIVATE void init_clock() 10530 { 10531 /* Initialize the CLOCK's interrupt hook. */ 10532 clock_hook.proc_nr = CLOCK; 10533 10534 /* Initialize channel 0 of the 8253A timer to, e.g., 60 Hz. */ 10535 outb(TIMER_MODE, SQUARE_WAVE); /* set timer to run continuously */ 10536 outb(TIMER0, TIMER_COUNT); /* load timer low byte */ 10537 outb(TIMER0, TIMER_COUNT >> 8); /* load timer high byte */ 10538 put_irq_handler(&clock_hook, CLOCK_IRQ, clock_handler);/* register handler */ 10539 enable_irq(&clock_hook); /* ready for clock interrupts */ 10540 } 10542 /*===========================================================================* 10543 * clock_stop * 10544 *===========================================================================*/ 10545 PUBLIC void clock_stop() 10546 { 10547 /* Reset the clock to the BIOS rate. (For rebooting) */ 10548 outb(TIMER_MODE, 0x36); 10549 outb(TIMER0, 0); 10550 outb(TIMER0, 0); 10551 }
Clock Task 10553 /*===========================================================================* 10554 * clock_handler * 10555 *===========================================================================*/ 10556 PRIVATE int clock_handler(hook) 10557 irq_hook_t *hook; 10558 { 10559 /* This executes on each clock tick (i.e., every time the timer chip generates 10560 * an interrupt). It does a little bit of work so the clock task does not have 10561 * to be called on every tick. The clock task is called when: 10562 * 10563 * (1) the scheduling quantum of the running process has expired, or 10564 * (2) a timer has expired and the watchdog function should be run. 10565 * 10566 * Many global global and static variables are accessed here. The safety of 10567 * this must be justified. All scheduling and message passing code acquires a 10568 * lock by temporarily disabling interrupts, so no conflicts with calls from 10569 * the task level can occur. Furthermore, interrupts are not reentrant, the 10570 * interrupt handler cannot be bothered by other interrupts. 10571 * 10572 * Variables that are updated in the clock's interrupt handler: 10573 * lost_ticks: 10574 * Clock ticks counted outside the clock task. This for example 10575 * is used when the boot monitor processes a real mode interrupt. 10576 * realtime: 10577 * The current uptime is incremented with all outstanding ticks. 10578 * proc_ptr, bill_ptr: 10579 * These are used for accounting. It does not matter if proc.c 10580 * is changing them, provided they are always valid pointers, 10581 * since at worst the previous process would be billed. 10582 */
Clock Task 10583 register unsigned ticks; 10585 /* Acknowledge the PS/2 clock interrupt. */ 10586 if (machine.ps_mca) outb(PORT_B, inb(PORT_B) | CLOCK_ACK_BIT); 10588 /* Get number of ticks and update realtime. */ 10589 ticks = lost_ticks + 1; 10590 lost_ticks = 0; 10591 realtime += ticks; 10592 10593 /* Update user and system accounting times. Charge the current process for 10594 * user time. If the current process is not billable, that is, if a non-user 10595 * process is running, charge the billable process for system time as well. 10596 * Thus the unbillable process' user time is the billable user's system time. 10597 */ 10598 proc_ptr->p_user_time += ticks; 10599 if (priv(proc_ptr)->s_flags & PREEMPTIBLE) { 10600 proc_ptr->p_ticks_left -= ticks; 10601 } 10602 if (! (priv(proc_ptr)->s_flags & BILLABLE)) { 10603 bill_ptr->p_sys_time += ticks; 10604 bill_ptr->p_ticks_left -= ticks; 10605 } 10606 10607 /* Check if do_clocktick() must be called. Done for alarms and scheduling. 10608 * Some processes, such as the kernel tasks, cannot be preempted. 10609 */ 10610 if ((next_timeout <= realtime) || (proc_ptr->p_ticks_left <= 0)) { 10611 prev_ptr = proc_ptr; /* store running process */ 10612 lock_notify(HARDWARE, CLOCK); /* send notification */ 10613 } 10614 return(1); /* reenable interrupts */ 10615 }
Clock Task 10617 /*===========================================================================* 10618 * get_uptime * 10619 *===========================================================================*/ 10620 PUBLIC clock_t get_uptime() 10621 { 10622 /* Get and return the current clock uptime in ticks. */ 10623 return(realtime); 10624 } 10626 /*===========================================================================* 10627 * set_timer * 10628 *===========================================================================*/ 10629 PUBLIC void set_timer(tp, exp_time, watchdog) 10630 struct timer *tp; /* pointer to timer structure */ 10631 clock_t exp_time; /* expiration realtime */ 10632 tmr_func_t watchdog; /* watchdog to be called */ 10633 { 10634 /* Insert the new timer in the active timers list. Always update the 10635 * next timeout time by setting it to the front of the active list. 10636 */ 10637 tmrs_settimer(&clock_timers, tp, exp_time, watchdog, NULL); 10638 next_timeout = clock_timers->tmr_exp_time; 10639 }
Clock Task 10641 /*===========================================================================* 10642 * reset_timer * 10643 *===========================================================================*/ 10644 PUBLIC void reset_timer(tp) 10645 struct timer *tp; /* pointer to timer structure */ 10646 { 10647 /* The timer pointed to by 'tp' is no longer needed. Remove it from both the 10648 * active and expired lists. Always update the next timeout time by setting 10649 * it to the front of the active list. 10650 */ 10651 tmrs_clrtimer(&clock_timers, tp, NULL); 10652 next_timeout = (clock_timers == NULL) ? 10653 TMR_NEVER : clock_timers->tmr_exp_time; 10654 } 10656 /*===========================================================================* 10657 * read_clock * 10658 *===========================================================================*/ 10659 PUBLIC unsigned long read_clock() 10660 { 10661 /* Read the counter of channel 0 of the 8253A timer. This counter counts 10662 * down at a rate of TIMER_FREQ and restarts at TIMER_COUNT-1 when it 10663 * reaches zero. A hardware interrupt (clock tick) occurs when the counter 10664 * gets to zero and restarts its cycle. 10665 */ 10666 unsigned count; 10667 10668 outb(TIMER_MODE, LATCH_COUNT); 10669 count = inb(TIMER0); 10670 count |= (inb(TIMER0) << 8); 10672 return count; 10673 }
Synchronous Alarms User Process alarm PM sys_setalarm signal SYSTASK set_timer notify CLOCK cause_alarm HARD_INT CLOCK ISR