jueves, 29 de noviembre de 2007

Práctica 1 - SO II (continuación 4ª)

3.2
Por ahora no tengo ni idea de como explicar esto. O yo soy muy retorcido o esto no se explica tan facilmente como antes.

Tras mucho tiempo inactivo doi por finalizada esta práctica poniendo las claves que la desarrollan aunque algunos ya la hayan entegregado.

Los cambios han de realizarse en los ficheros:



proc.h
main.c
do_fork.c
proc.c



Yo opino que el orden de los ficheros a modificar debe ser ese para poder comprender el resultado de las pruebas.

Antes de comenzar a desarrollar la prácita se me pasaron por la cabeza una solución así que os la voy a contar para que veais porque no la he utilizado.

SOLUCIÓN 1 (NO APTA)

Se nos pide asignar a cada proceso una antiguedad, y a mi se ocurrió después de observar el fichero PROC.H que podía añadir un campo al registro que identificaba a cada descriptor de proceso.

1.jpg

Mi idea era "idealmente" correcta pero tenía 1 defecto: NO EREA VIABLE.

Pensando, pensando se me ocurrió que podría no funcionar por 2 motivos:

PRIMER MOTIVO:


Que en algún sitio se compruebe el tamaño de la estructura con el que TANENBAUM espera que tenga por ejemplo con la sentencia
sizeof.

sizeof ( struct proc );


Este primer dilema no he podido demostrarlo ya que no me puesto a buscarlo, así que vamos por el siguiente.

SEGUNDO MOTIVO:

Minix es un Sistema Operativos por lo tanto a veces las estructuras que nosotros vemos como tales, él no las interpreta como estructuras, sino que no siempre se accede a un registro de la siguiente forma:

REGISTRO.CAMPO


Como parte del código de MINIX está escrito en ensamblador, y éste no conoce de registros y campos, para acceder a ellos se hace lo siguiente BASE + DESPLAZAMIENTO y así obtiene el campo que está buscando, logicamente BASE es la dirección de la estructura y desplazamiento hace referencia al campo que está buscando.

La BASE será la dirección del puntero struct proc y el desplazamiento para acceder al primer campo será 0 al segundo el tamaño del campo 1 ... y así sucesivamente. Espero no haberme equivocado.

Como esto es en realidad lo que ocurre, no podemos añadir un campo a STRUCT PROC cuando a nosotros nos de la gana ya que el código ensamblador no está informado de esos cambios y accede de la forma BASE + DESPLAZAMIENTO y entonces se hace la picha un lío.

Como ya sabemos por que falla la solución 1, vamos a redarctar la solución 2.



SOLUCIÓN 2 (LA CORRECTA):


En esta solución vamos a dividirla en varios pasos:
1.- Modificar proc.h
2.- Modificar main.c
3.- Modificar do_fork.c
4.- Modificar proc.c
5.- Prueba 1
6.- Prueba 2
7.- Prueba 3
8.- Conclusiones

1.- Modificar proc.h
En este caso debemos crearnos la estructura que nos indique como guardar la antiguedad. Digamos que el dibujo podría ser un array y la flecha una variable que nos indique cual ha sido la última antiguedad que hemos asignado.

2.jpg

2.- Modificar main.c

Aquí debemos solamente hacer 2 cosas inicializar procesos de la imagen de memoria, y establecer cual va a ser la antiguedad del siguiente proceso que se ejecute.

Vamos por pasos, en la imagen se ve el bucle que recorre los 12 procesos que tiene MINIX en memoria, y vosotros os preguntareis porque 12 y no 345432 procesos, pues muy facil, utilizamos el script de siempre:

3.jpg

find . -name '*.[ch]' -exec grep 'NR_BOOT_PROC' \; -print


Se obtiene como resultado:

4.jpg

Como vemos está formado por

NR_TASKS + INIT_PROC_NR + 1


Otra vez seguimos sin saber el valor de INIT_PROC_NR pues a lo mismo:

find . -name '*.[ch]' -exec grep 'INIT_PROC_NR' \; -print


Dando como resultado:

5.jpg


4 + 7 + 1 = 12


Si queremos estar seguros de que hay 12 procesos en memoria y que el resto ya son procesos de usuario nos aseguramos de que estamos en el primer tty, por lo que pulsamos ALT+F1, ahora pulsamos F1 y vemos la tabla de procesos:

Como vemos en la siguiente imagen los procesos residentes en memoria estan marcados del -4 al 17 (12 procesos)

6.jpg

El proceso IDLE aparece entre paréntesis, las tareas entre corches y el resto son valores del 0 a ..., como vemos esto nos ayuda bastante a la realización de la práctica. Además si pulsamos F3 vemos con más exactitud los procesos de la imagen de memoria.

Ahora que ya sabemos lo que hay que hacer en main.c podemos continuar, pero sin antes hacer un pequeño resumen. Primero debemos recorrer los procesos de la imagen de memoria y darles su prioridad y por último indicarle a nuestra variable cual es la última prioridad que hemos indicado.

Esta forma de hacerla no es la ideal tambien podemos guardar en nuestra variable la prioridad siguiente o lo que queramos. Además debemos saber que no es necesario inicializar todo el array a 0 es decir los 104 elementos.

3.- Modificar do_fork.c

En este apartado que es el más sencillo debemos decirle al array que el proceso que se acaba de crear (RPC) tiene una prioridad de ....

Aquí teneis que tener en cuenta el desplazamiento que se produce con los procesos. Los procesos empiezan a numerarse por el -4 y nuestro array comienza a numerarse por 0, por lo que debemos ser cuidadosos de no hacer los siguiente:


ARRAY[ -4 ] = PRIORIDAD


Como vemos esto es absurdo y hay que tenerlo en cuenta, para solucionarlos sabed que la constante del kernel NR_TASKS tiene valor 4.

4.- Modificar proc.c

En el proc.c también la modificacion es muy sencilla pero debemos acordarnos de ESTRUCTURA DE DATOS I y II y de los punteritos, aquí debemos hacer un algoritmo que ordene elementos en una lista con cabecera y final pero teniendo en cuenta que el ultimo proceso de la lista no a punta a FINAL sino a NIL_PROC.

Mi recomendación es que os aprovecheis del código de MINIX y hagais solo la parte complicada dejando por ejemplo el código original para rellenar la lista cuando está vacia. En el siguiente código de MINIX que se muestra he dejado la función ENQUEUE peladita para que vosotros solo toqueis la parte que está en rojo y creeis el código necesario para insertar de forma ordenada.

7.jpg

5.- Prueba 1

Para realizar la primera prueba:

cc -o p1.out prueba-1.c


Para ejecutarla:

./p1.out


El resultado que debe dar es:

1.....................2........................


Porque pues basicamente ya que lo que hace el código es dado un proceso padre con número 1 crear un proceso hijo con índice 2 y logicamente el padre se debe mostrar antes que el hijo ya que nuestra prioridad es por orden de creación, el que antes se cree antes se debe ejecutar y el padre ha sido creado antes que el hijo.

6.- Prueba 2

cc -o p2.out prueba-2.c


Para ejecutarla:

./p2.out


El resultado que debe dar es:



Se crea el proceso padre.
Se crea el proceso hijo.
Se ejecuta antes el padre que el hijo.
El padre espera un segundo.
Se ejecuta el hijo e imprime 11111111...
Se ejecuta el padre e imprime 222222222.....
Temina de ejecutar se el hijo con 111111....


7.- Prueba 3

cc -o p3.out prueba-3.c


Para ejecutarla:

./p3.out


El resultado que debe dar es:


El proceso padre crea un total de n hijos por los que el resulado debe ser:
Primero el padre, luego el primer hijo, despues el segundo, .... es decir:

111...222...333...444...555...666...777...nnn


Sabía que se me olvidaba algo, en la función ENQUEUE hay que hacer una cosita con la llamada a la función SCHED, dicha función lo que hacía era modificar la prioridad de un proceso, es decir mover a un proceso de su cola, por lo que hay que acordarse de eso si vemos que hace cosas raras, ya que solo debe existir una cola. ESPERO ESTAR SEGURO DE TODO ESTO LO QUE DIGO.

8.- Conclusiones

Esto es todo amigos hasta nuestra siguiente entrega.


Sau2.

sábado, 24 de noviembre de 2007

Práctica 1 - SO II (continuación 3ª)

En esta nueva solución incompleta se nos presenta la imperativa de hacer un planificador un poco mejor o pero. En nuestro caso lo que vamos a hacer es que el planificador seleccione al proceso según el campo “p_nr“. Este campo está definido en el archivo “proc.h” de la sigiente forma, pero antes debemos saber como encontramos la definción de “p_nr”, pues como diría Luis Cearra, con esta chuleta:



find . -name ‘*.[ch]’ -exec grep ‘p_nr’ {} \; -print


Lógicamente como sois casi ingenieros no hace falta que la detalle, la verdad es que es un 99,9% igual a la que utiliza Luis en sus clases asi que el que fue a ellas no necesita saberlo pero como no todos pueden ir a clase la detallaré:


FIND = BUSCA
. = AQUI (en mi caso en /usr/src/kernel/)
-name = UN ARCHIVO
‘*.[ch]’ = DE EXTENCION .c O DE EXTENSION .h
-EXEC = SI ENCENTRAS ALGUNO EJECUTA …
GREP = EJECUTA GREP (buscador de texto en fichero)
‘p_nr’ = LAS COMILLAS NO SON NECESARIAS Y LE DIGO QUE BUSQUE EL TEXTO p_nr
{} = CUANDO FIND ENCUENTRA UN FICHERO ES SUSTITUIDO POR LAS LLAVES
\; = CIERRAN EL EXEC (sin interes)
-PRINT = DAME EL NOMBRE DEL FICHERO EN DONDE ENCUENTRAS LA CADENA p_nr


Como resultado hemos obtenido:
Primero los resultados y luego el fichero en donde esta el resultado.

find1.jpg

Por lo que vemos que “p_nr” se declara en el fichero “proc.h”, lo editamos con “elvis” o con “mined”



struct proc {
struct stackframe_s p_reg;

proc_nr_t p_nr;
struct priv *p_priv;
short p_rts_flags;
short p_misc_flags;

char p_priority;
char p_max_priority;
char p_ticks_left;
char p_quantum_size;

struct mem_map p_memmap[NR_LOCAL_SEGS];

clock_t p_user_time;
clock_t p_sys_time;

struct proc *p_nextready;
struct proc *p_caller_q;
struct proc *p_q_link;
message *p_messbuf;
int p_getfrom_e;
int p_sendto_e;

sigset_t p_pending;

char p_name[P_NAME_LEN];

int p_endpoint;
};




Como vemos es de tipo:



proc_nr_t p_nr;




Como yo soy muy curiso, quiero saber como es el tipo de p_nr:



find . -name ‘*.[ch]’ -exec grep ‘proc_nr_t’ {} \; -print




Da como resultado:

find2.jpg

Ahora ya sabemos que “proc_nr_t” esta declarado en type.h y que es un int.

Ahora que ya hemos realizado la introducción vamos con la práctica.

3
3.1 Leer
3.1.1 Leer
3.1.2 Leer

Aquí nos dice únicamente que hay que modificar el proc.c, es un alivio después de ver todos los ficheros que tiene el directorio /kernel.

3.1.3 Recomendación, que la función enqueque almacene ya los procesos ordenado.

Aquí es donde yo creo que empieza la práctica, es decir hay que manipular el proc.c y claramente la función enqueque.



PRIVATE void enqueue(rp)
register struct proc *rp;
{

int q;
int front;


sched(rp, &q, &front);

/* Now add the process to the queue. */
if (rdy_head[q] == NIL_PROC) { /* add to empty queue */
rdy_head[q] = rdy_tail[q] = rp; /* create a new queue */
rp->p_nextready = NIL_PROC; /* mark new end */
}
else if (front) { /* add to head of queue */
rp->p_nextready = rdy_head[q]; /* chain head of queue */
rdy_head[q] = rp; /* set new queue head */
}
else { /* add to tail of queue */
rdy_tail[q]->p_nextready = rp; /* chain tail of queue */
rdy_tail[q] = rp; /* set new queue tail */
rp->p_nextready = NIL_PROC; /* mark new end */
}

/* Now select the next process to run. */
pick_proc();

}




Que es lo que hay que haces, PUES NO LO VOY A DECIR pero si os guiaré en este pequeño procedimiento para que sepeais lo que yo he hecho y lo que creo que se debe hacer.

UNA COSA ESTA CLARA NO ESTOY RESOLVIENDO LA PRÁCTICA SOLO HAGO LO MISMO QUE EN EL FORO DE LA ESCUELA PERO MÁS AMPLIO. NO DOY RESULTADOS SOLO POR EJEMPLO DOY EL SIGNIFICADO DE INSTRUCCIONES EN C O EXPLICO ALGUNOS DETALLES RAROS DE MINIX, UNIX, SIEMPRE QUE MI CAPACIDAD INTELECTUAL Y MI ENTENDIMIENTO LO PERMITA. ADEMÁS DOY DETALLES QUE COMO NO ENTRAN PARA EXAMEN NO OS SIRVE PARA NADA, POR EJEMPLO DETALLAR INSTRUCCIONES EN “C”, PERO QUE SI OS SIRVEN PARA ENTERDER LA PRÁCTICA. OTRA COSA ES QUE COMO A MI GUSTA HACER ESTO (EXPLICAR) ME SIENTO AGRADECIDO CON TODAS LA VISITAS A ESTE HUMILDE BLOG AUNQUE SEA SOLO POR ESTE CUATRIMESTRE, Y QUE SI A ALGUNO LE SIRVE PARA APROBER PUES AHÍ ESTA ESO. DE TODAS FORMAS CONTESTARÉ CUALQUIER DUDA EN MI CORREO O EN EL FORO DE DELEGACIÓN.

HA QUEDADO CLARO QUE MI INTENCIÓN NO ES RESOLVER LA PRÁCTICA SINO AYUDAR A AQUELLOS QUE NO TIENEN TIEMPO Y DARSELO TODO UN POCO MÁS MASTICAÍTO AUNQUE PARA ALGUNOS ESO ES PERJUDICIAL.

Continuarmos …

Vamos a detallar el código:



/* Determine where to insert to process. */
sched(rp, &q, &front);




Algunos se preguntan que hace esto pues es muy claro aunque en lo que voy a decir YO tengo una duda, yo opino a que SCHED se le pasa un descriptor de proceso (creo que se llama así, una variable “q” por referencia y una variable “front” por referencia, eso diríamos en PASCAL pero como es C se le pasas direcciones de memoria de la variable “q” y “front”.

Que hace sched, pues devuelve en q, la cola donde debe ir insertado el proceso que le pasamos como primer parámetro, y esto lo sabemos por la definición que se hace en SCHED de esas variable:



int *queue; /* return: queue to use */
int *front; /* return: front or back */




Mas abajo vemos que:



*queue = rp->p_priority;




Es decir, el CONTENIDO de la variable QUEUE es la prioridad, y esto que demonios quieres decir, QUE LA PRIORIDAD DE UN PROCESO DETERMINA LA COLA EN LA QUE LO VAMOS A INSERTAR.

Lógicamente antes de ese código hemos subido, bajado, mantenido su prioridad, etc etc etc, un proceso puede entrar en SCHED con prioridad X y salir con prioridad Y, o con X. (esto no del todo cierto tiene una errata, a ver si la encuentras).

En “front” se le dice si insertamos delante o detrás, desde mi punto de vista este código es absurdo en esta parte de la práctica ya que ahora no vamos a insertar por delante o por detras sino que vamos a ordenar en la cola de procesos a un proceso según el campo p_nr.

Es decir (antes):

HEADCOLA_7: —> P10(p_nr=13) — P14(p_nr=23) — P9(p_nr=12) P10(p_nr=13) — P14(p_nr=23) — P9(p_nr=12) — P20(p_nr=16) P9(p_nr=12) — P10(p_nr=13) — P14(p_nr=23) P9(p_nr=12) — P10(p_nr=13) — P20(p_nr=16) — P14(p_nr=23) <— TAILCOLA_7

Como vemos se ha insertado en el orden que le corresponde.

Ahora ya es cosa vuestra el curraros como se hace para insertar de forma ordenada en una lista enlazada, es un problema de ESTRUCTURA DE DATOS I, y espero que no os suponga mucho problema.

En realidad es una lista enlazada con cabecera y final pero con la peculiaridad de que:

La cabecera apunta al primer proceso listo para ejecutarse de la Cola X.
El final apunta al ultimo proceso listo para ejecutarse de la Cola X.
El último proceso listo para ejecutarse de la Cola X, no apunta al puntero que deterimina el final de la cola, esto es quizas lo más lioso de entender. Si sacais esto no tendreis problema.

Otra cosa a tener en cuenta es que cabecera y final no son 2 puntero sino son 2 array de punteros que cada uno apunta a una cola determinada.

Acordaros de cotemplar el caso base y posteriormente el caso general.

Me falta por poneros los resultados que me salen a mi cuando ejecuto las 3 pruebas con el nuevo planificador, espero ponerlo lo antes posible, no más tardar de mañana.

Ahora debes hacer aquí tu parde de la práctica.

respuesta.jpg

Salu2.

miércoles, 7 de noviembre de 2007

Práctica 1 - SO II (continuación 2ª)

ADVERTENCIA: No tienen nada que ver las carpetas \MEDIA y \IMAGES que cuelgan del directorio del QEMUMANAGER, según un razonamiento lógico al que he llegado (esto es algo absurdo lo sé), en \IMAGES están las imágenes de los sistemas que se ejecutan con QEMU y en \MEDIA los dispositivos de E/S es decir DISKETERAS, CDROM, DVD, USB, UNIDADA, UNIDADB … es decir no guardes tu imagen .img en la carpeta \IMAGES sino en la carpeta \MEDIA, ya que es en esta última donde el QEMUMANAGER busca dispositivos de E/S.

Volvemos después de un merecido descanso al curro y a comprender como funciona la planificación de procesos.

Lo primero que vamos a hacer es crearnos una imagen nueva e introducir en ella los 3 ficheros (1, 2 y 3) que se nos proporcionan en la práctica. Para poder hacer esto ver el post sobre la práctica 1, no la continuación 1ª.

Una vez hecho esto, como vemos en la imagen tenemos ya los tres ficheros dentro del fichero .img:

1

El fichero .img se encuentra en la carpeta \MEDIA.

Asegurarse de que en las propiedades de la imagen a arrancar MINIX 3.0 el disco de floppy A es el que habeis creado.

2

Ahora arrancamos MINIX 3.0, como siempre la constraseña es “root” sin las comillas.

Vemos el contenido del diskette:

#dosdir fd0

3

Ahora enviamos el contenido del fichero prueba-1.c a nuestro disco duro en mi caso yo me he creado un directorio y ahí voy a volcar todos los datos.

#dosread -a fd0 prueba-1.c > prueba-1.c

Con -a evitamos lo que le pasó a Brom en el foro de delegación, ahora ya está solucionado y ya no da problemas ningún carácter extraño.

APARTADO 1

Lo siguiente que nos pide la práctica es compilarla:

#cc prueba-1.c

Como nos ha devuelto como resultado una almohadilla “#” y no un asterisco “*”, sabemos que todo ha ido bien, además porque el compilador “cc” no nos ha dicho nada. Vemos el resultado:

#ls -l

4

Ejecutamos el fichero “a.out”:

#./a.out

El resultado es una pantalla llenas de 1 y 2 entrelazados …

5

Para salir pulsamos CTRL+C.

EXTRAIDO DEL FORO DE DELEGACIÓN
Una vez que tenemos los 3 ficheros en MINIX los compilamos yo opto por la opcion:

cc -o prueba-1.out prueba-1.c
cc -o prueba-2.out prueba-2.c
cc -o prueba-3.out prueba-3.c

con la opcion -o el fichero objeto que se genera el independiente y podemos probar uno y otro sin tener que utilizar el mismo nombre para los 3.

para ejecutarlos ./prueba-1.out y así con los otros 3.

APARTADO 2

Básicamente tenemos que hacer lo que pide.

2.1
2.1.1 —> Localizar la función
2.1.2 —> Sustituir (yo he utilizado elvis)
2.1.3 —> Grabar y cambiar de direcorio
2.1.4 —> Compilar e instalar la nueva imagen
2.1.5 —> Reiniciar
2.1.6 —> ¿que ocurre?

6

respuesta_chica.jpg

2.1.7 —> Ya está justificado anteriormente.
2.2
2.2.1 —> Arrancar la imagen de fábrica.
2.2.2 —> Buscar la función “sched”.
2.2.3 —> Eliminar sentencias.
2.2.4 —> Modificar código.

PISTA:

PRIVATE void sched(rp, queue, front)
register struct proc *rp; /* process to be scheduled */
int *queue; /* return: queue to use */
int *front; /* return: front or back */


Se ve claramente que la variable "front" sirve para insertar al principio o al final. Os queda a vuestra libertad la interpretación del siguiente código.

int time_left = (rp->p_ticks_left > 0); /* quantum fully consumed */


*front = time_left;


2.2.5 ---> Grabar.
2.2.6 ---> Compilar.
2.2.7 ---> Reiniciar con la nueva imagen.
2.2.8 ---> Compilación y ejecución de ./a.out
2.2.9 --->
Antes de nada vamos a ver como funciona la función sched (...). Esta función primero lo que hace es bajarle la prioridad a un proceso si no ha terminado de ejecutarse (en lineas generales y sin haber tocado nosotros nada). Luego determina en que cola debe añadirse ese proceso, lo cual se sabe viendo que prioridad tiene, es decir COLA = PRIORIDAD, si tiene la prioridad 10 lo metemos en la cola 10, por último debemos saber si añadir el proceso por delante o añadirlo por detrás de la cola, entonces nos hacemos la siguiente pregunta:

¿Le quedan tics al proceso n?
* SI: Insertamos el proceso por la cabecera de la cola.
* NO: Insertamos el proceso por el final de la cola.

Cuando digo insertamos me refiero a que la función ENCOLAR se encarga de añadir al final o al principio el proceso en la cola correspondiente. La función ENCOLAR pregunta a sched(...) donde debe introducir y sched(...) lo devuelve en un parámetro que es FRONT.

¿Que diferencia hay?

respuesta_chica.jpg

2.2.10 ---> ¿Por qué se produce este comportamiento?

respuesta_chica.jpg

2.2.11 ---> ¿Cual es el orden?

respuesta_chica.jpg

2.2.12 ---> ¿Es el resultado obtenido el perseguido?

respuesta_chica.jpg


[ CONTINUARÁ ]

sábado, 3 de noviembre de 2007

Práctica 1 - SO II (continuación 1ª)

Bueno, bueno chicos y chicas aki estamos otra vez, como ahora estoy de vacaciones no me voy a poner a explicar como desarrollar el contenido de la práctica por dos motivos:

1.- No me he leído de práctica. (no tengo tiempo)
2.- Algo tendreis que hacer vosotros.

Pues nada más por hoy, mañana prometo que me la leo y expondré un poco como se debe desarrolloar aunque intentaré no dar demasiados detalles ya que como dije en el punto 2 algo debereis hacer vosotros.

Por cierto todo esto SI Y SOLO SI yo consigo hacer la práctica que puede que no tenga ni pajolera idea, y os pida ayuda.

Salu2.