Examen ESO. Convocatoria ordinaria 2006 Estudio de un Sistema Operativo 3 Escuela Técnica Superior de Informática Aplicada Universidad Politécnica de Valencia o 2 de Febrero de 2006 Cuestiones Comenta brevemente la veracidad o falsedad de las siguientes afirmaciones. 1. Un programa con licencia GPL puede ser modificado sin el permiso explı́cito de su(s) autor(es) sólo con propósitos no comerciales. [0.5 puntos] Un programa GPL puede ser modificado sin el permiso explı́cito de sus autores sean cuales sean los fines con los que se lleve a cabo la modificación. Aun ası́, el programa resultante deberá estar cubierto por la licencia GPL. 2. Los procesos cuyo usuario efectivo es 0 (root) y los threads del núcleo carecen de mapa de memoria propio, por lo cual heredan el mapa de memoria del anterior proceso en ejecución. Esto implica una mejora en la eficiencia, ya que no es necesario cambiar los mapas de páginas, invalidar las TLB’s, etc. [0.5 puntos] Eso sólo es cierto para los threads del núcleo, pero no para todos los procesos con usuario efectivo 0. Los programas ejecutados como usuario root se ejecutan como procesos convencionales, pero con mayores privilegios a la hora de solicitar servicios al S.O. 3. Cuando un proceso está terminando una llamada al sistema y pierde el procesador, tras la invocación de schedule() en entry.S:259, el nuevo proceso siempre continuará su ejecución en ret from sys call tras la ejecución del salto situado en entry.S:260. [0.5 puntos] El nuevo proceso retomará la ejecución en el punto en el que invoco a schedule, que no tiene porque ser en el fichero entry.S (podrı́a ser en un semáforo, etc.) Responde brevemente a la siguiente cuestión: 4. En una llamada al sistema: a) ¿Se cambia siempre de nivel de privilegio? Si no es ası́, ¿cuándo no es ası́? [0.5 puntos] No se cambiará de nivel de privilegio si la llamada al sistema se realiza desde dentro del núcleo del S.O. Un ejemplo serı́a la implementación de la función kernel thread. 1 b) ¿Centrándonos en la arquitectura i386, serı́a posible pasar los parámetros de la llamada por la pila? ¿Si es ası́, cómo se harı́a? [0.5 puntos] Sı́, pero para ello el núcleo deberı́a copiar los parámetros de la llamada al sistema desde la zona de memoria de la pila del usuario a la pila del proceso en modo núcleo (utilizando la función copy from user). La implementación actual mediante el uso de registros es más sencilla. Problemas Resuelve sólo tres problemas de la siguiente lista, indicando con el máximo detalle (ficheros y lı́neas de código modificadas) las modificaciones que se deberı́an realizar en cada caso. 1. Para llevar a cabo un nuevo esquema de contabilidad de tiempos se requiere medir el instante en que un proceso solicita la ejecución de una llamada al sistema y el instante en que dicha llamada finaliza. Para ello se suministran las funciones que llevan a cabo dicha contabilidad, asmlinkage void begin system call(int syscall) y asmlinkage void end system call(int syscall), ya implementadas. Dichas funciones deben recibir como parámetro el número de la llamada al sistema. Modificar el código del núcleo de Linux para que implante este nuevo esquema en el mecanismo de llamadas al sistema. [2.5 puntos] 194 ENTRY(system_call) 195 pushl %eax 196 SAVE_ALL 197 GET_CURRENT(%ebx) 198 testb $0x02,tsk_ptrace(%ebx) 199 jne tracesys 200 cmpl $(NR_syscalls),%eax 201 jae badsys push %eax # Insertamos el parámetro de la función call SYMBOL_NAME(begin_system_call) pop %eax # Sacamos el parámetro y restauramos el # registro %eax que se habrá perdido al # invocar a una función en C 202 call *SYMBOL_NAME(sys_call_table)(,%eax,4) 203 movl %eax,EAX(%esp) movl ORIG_EAX(%esp), %eax # recuperamos el %eax original push %eax # lo insertamos en la pila call SYMBOL_NAME(end_system_call) pop %eax # Limpiamos la pila. (addl $4, %esp) 204 ENTRY(ret_from_sys_call) 205 cli 206 cmpl $0,need_resched(%ebx) 207 jne reschedule 208 cmpl $0,sigpending(%ebx) 209 jne signal_return 210 restore_all: 211 RESTORE_ALL 2 2. Se quiere incorporar una nueva caracterı́stica a los manejadores de dispositivo del núcleo de Linux. Se quiere que si un manejador de dispositivo tiene activado el flag SA NOT NESTED, dicho manejador no se ejecute cuando haya interrupciones anidadas. En su lugar, la ejecución del código del manejador se postpondrá mediante la función delay IRQ event(unsigned int irq, struct pt regs * regs, struct irqaction * action). 437 int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) 438 { 439 int status; 440 int cpu = smp_processor_id(); 441 442 irq_enter(cpu, irq); 443 444 status = 1; /* Force the "do bottom halves" bit */ 445 446 if (!(action->flags & SA_INTERRUPT)) 447 __sti(); 448 449 do { 450 status |= action->flags; if ((action->flags & SA_NOT_NESTED) && local_irq_count(cpu) > 1) delay_IRQ_event(irq, regs, action); else 451* action->handler(irq, action->dev_id, regs); 452 action = action->next; 453 } while (action); 454 if (status & SA_SAMPLE_RANDOM) 455 add_interrupt_randomness(irq); 456 __cli(); 457 458 irq_exit(cpu, irq); 459 460 return status; 461 } [2.5 puntos] 3 3. Se quiere modificar los semáforos del núcleo de Linux para que se comporten como cerrojos de lectura/escritura, de forma que, en un momento dado pueda haber un número indeterminado de procesos lectores dentro del semáforo o bien un único proceso escritor (se inicializarán como semáforos binarios). Para facilitar la implementación, se suministra la función is reader (struct task struct * tsk), que devuelve cierto si el proceso tsk es un lector, y un nuevo campo struct task struct * last en la estructura struct semaphore que indica cuál ha sido el último proceso que ha entrado en el semáforo. Dicho campo se puede considerar convenientemente actualizado. arch/i386/kernel/semaphore.c 57 void __down(struct semaphore * sem) 58 { 59 struct task_struct *tsk = current; 60 DECLARE_WAITQUEUE(wait, tsk); DECLARE_WAITQUEUE(reader_wait, tsk); 61 tsk->state = TASK_UNINTERRUPTIBLE; 62 add_wait_queue_exclusive(&sem->wait, &wait); add_wait_queue_exclusive(&sem->readers, &reader_wait); 63 64 spin_lock_irq(&semaphore_lock); 65 sem->sleepers++; 66 for (;;) { 67 int sleepers = sem->sleepers; ... 73 if (!atomic_add_negative(sleepers - 1, &sem->count)) { 74 sem->sleepers = 0; 75 break; 76 } if (is_reader(current) && is_reader(sem->last)) { sem->sleepers = 0; break; } 77 sem->sleepers = 1; /* us - see -1 above */ 78 spin_unlock_irq(&semaphore_lock); 79 80 schedule(); 81 tsk->state = TASK_UNINTERRUPTIBLE; 82 spin_lock_irq(&semaphore_lock); 83 } 84 spin_unlock_irq(&semaphore_lock); 85 remove_wait_queue(&sem->wait, &wait); 86 tsk->state = TASK_RUNNING; 87 wake_up(&sem->wait); if (is_reader(current)) wake_up(&sem->readers); 88 } Habrı́a que añadir el nuevo campo readers a la estructura semaphore. [2.5 puntos] 4 4. Se pretende modificar el algoritmo de planificación del núcleo de Linux para que dé prioridad a los procesos que todavı́a no se han ejecutado desde su creación. Dicho aumento de prioridad se verá reflejado en una bonificación, representada por la constante NOT EXECUTED BONUS, en el mecanismo de selección de la tarea más prioritaria. Si se considera de utilidad, se puede suponer que la tarea init task se sabe que ya ha perdido la CPU en alguna ocasión. Fichero: include/linux/sched.h Declaración del nuevo campo e inicialización. 281 struct task_struct { ... 322 unsigned long sleep_time; 323 unsigned long executed; 324 struct task_struct *next_task, *prev_task; 325 struct mm_struct *active_mm; ... 478 run_list: LIST_HEAD_INIT(tsk.run_list), executed: 0, 479 next_task: &tsk, 480 prev_task: &tsk, \ \ \ \ } Fichero: kernel/sched.c Incremento de la prioridad y actualización de la situación de ejecutado. 179 180 181 182 183 /* .. and a slight advantage to the current MM */ if (p->mm == this_mm || !p->mm) weight += 1; weight += 20 - p->nice; if (!p->executed) weight += NOT_EXECUTED_BONUS; goto out; ... 630 631 632 633 634 635 636 637 /* * from this point on nothing can prevent us from * switching to the next task, save this fact in * sched_data. */ sched_data->curr = next; task_set_cpu(next, this_cpu); next->executed= 1; spin_unlock_irq(&runqueue_lock); [2.5 puntos] 5