Examen ESO. Convocatoria ordinaria 2006

Anuncio
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
Descargar