domingo, 23 de diciembre de 2007

CVS a lo cutre

Acabo de inaugurar esta parte de mi blog para que veáis hasta que punto una persona puede volverse loco en una tarde.
El objetivo de este apartado es observar los cambios realizados en el sistema operativo MINIX 3 sin tener porque saber que ficheros hemos tocado. Para poder desarrollarlo me he tenido que devanar los sesos y recordar aquellas maravillosas clase de UNIX en primero de carrera.

Descargar Link: cambios_shx.pdf

1. Lo primero que he hecho ha sido bajarme una imagen nueva de la página de la asignatura.
2. Una vez que tenemos la imagen nueva, vamos a hacer una copia del directorio “/usr/src” y subdirectorios, pero necesitamos saber a dónde.
3. Para ver cómo está distribuido el sistema de ficheros utilizamos el commando:
#df

1.jpg

Observamos que en “/usr” hay un 99% de ocupación por lo que no nos vale, necesitamos por ejemplo utilizar “/home” para nuestro fin, ya que su índice de ocupación es del 11%.
4. Una vez que sabemos en donde vamos a realizar la copia, procedemos a ello:

#cp -r /usr/src /home

5. Con esta sencilla operación obtenemos en el directorio “/home” un subdirectorio “/home/src” en donde está todo el código fuente de MINIX 3.0.
6. Ahora viene la tediosa tarea de hacer la práctica 0, 1, 2 ó 3 (esta parte os la dejo a vosotros).
7. Una vez que hemos realizado la práctica correspondiente tenemos que ir al punto 8.
8. NOS LLEVAMOS LAS MANOS A LA CABEZA PORQUE NO SABEMOS COMO VER TODOS LOS CAMBIOS QUE HEMOS REALIZADO NI QUEREMOS RECORRERNOS TODO EL SISTEMA DE FICHEROS, PUES TRANQUILOS LA SOLUCIÓN ESTÁ EN EL PUNTO 9.
9. Como hemos realizado una copia de cada fichero en “/usr/src” en “/home” antes de haberlos modificado solo tenemos que comparar los 4343 ficheros que tiene “/usr/src” de uno a uno.
Este cálculo ha sido aproximado y realizado de la siguiente forma:

#find . | wc –l

10. Como eso es una tarea muy muy tediosa me he currado un script que hace lo siguiente.
11. Antes de nada indicar que mis conocimientos de UNIX y sus maravillosos comandos, es muy reducido, seguro que existe una forma más sencilla de hacerlo o con menos pasos, así que acepto sugerencias que no soy perfecto. Lo que hace básicamente este script es:
a. Borra la pantalla (para empezar no está mal ehhhh)

clear

b. Utilizo 2 ficheros temporales cada vez que se inicia el script comprueba si existen y si existen se borran.

if test -f fich_1.txt ; then
rm fich_1.txt ;
fi
if test -f fich_2.txt ; then
rm fich_2.txt ;
fi


c. Busco en el directorio actual, (por lo que me posiciono en “/usr/src”), todos los ficheros que sean FICHEROS y que no sean código objeto, la lista de ficheros resultantes se inserta en el fichero “fich_1.txt”.

find . \( ! -name '*.o' -a -type f \) > fich_1.txt ;

d. El contenido del fichero “fich_1.txt” es del tipo reseñado a continuación por lo que aplico el siguiente filtro.

./directorio/directorio/ficheroA
./directorio/directorio/ficheroB
./directorio/directorio/ficheroC
./directorio/directorio/ficheroD
./directorio/directorio/ficheroE


e. Extraigo de cada línea de “fich_1.txt” el “./” y lo sustituyo por “NADA”. Utilizo para ello el comando “sed”, donde busca con una expresión regular el comienzo de línea con “./” y lo cambia por “NADA”. El resultado lo envía a “fich_2.txt”.

sed 's/^\.//g' fich_1.txt > fich_2.txt ;

f. El contenido del fichero “fich_2.txt” es del tipo reseñado a continuación por lo que aplico el siguiente filtro.

directorio/directorio/ficheroA
directorio/directorio/ficheroA
directorio/directorio/ficheroA
directorio/directorio/ficheroA
directorio/directorio/ficheroA


g. A continuación utilizo “awk” ya que este comando trata cada “aparición” de texto en una línea como un campo por lo que voy a poder tratar cada línea como un campo.

awk -f rules.awk fich_2.txt > fich_1.txt

h. El comando anterior aplica el fichero de reglas para “rules.awk” a “fich_2.txt” y el resultado lo envía a “fich_1.txt”. El comando “awk” funciona de forma que se puede aplicar un filtro al inicio, otro a cada campo de cada línea y otro al final, en mi caso solo me interesa aplicar un filtro a cada campo de cada línea, pero como en cada línea solo hay un campo, sólo se hará una vez por línea. El contenido de “rules.awk” es el siguiente:

{
print "if test -f /home/src" $1 " ; then ";
print " diff /home/src" $1 " /usr/src" $1 " > /res/" ar[split($1,ar,"/")] ".diff ;" ;
print " echo -n + ;";
print "fi" ;
}


i. Las reglas para “awk” hacen los siguiente para cada línea, primero tenemos en cuenta que la línea 1 va a ser el campo 1, que se representa por $1. Lo que aquí estamos haciendo es simplemente escribir un texto NO ESTAMOS EJECUTANDO NADA. El campo $1 contiene una serie de directorio y termina en un fichero. Comprobamos si el fichero de la línea i-ésima existe, si es así escribe la sentencia:

“diff /home/src/[contenido_de_$1] /usr/src/[contenido_de_$1] > [fichero_de_$1].diff”

j. Después de escribir la línea anterior escribe un “echo” y finaliza el “if”.
k. Resultado:

If test -f /home/src/dir1/dir2/fich1 ; then
diff /home/src/dir1/dir2/fich1 /usr/src/dir1/dir2/fich1 > /res/fich1.diff ;
echo -n + ;
fi


l. Una vez que tenemos esta sentencia por cada línea del fichero, se envía todo esto a un nuevo fichero temporal.
m. Ejecutamos con “sh” ese fichero temporal, el resultado será que si existe el fichero hace la comparación y guarda el resultado en el directorio “/res” añadiéndole la extensión “.diff”. Confío en que no se modifique en 2 sitios diferentes 2 ficheros que tengan el mismo nombre y modificaciones diferentes.
n. Si todo va bien crea el directorio de resultados y ejecuta con “sh”.

if test ! -d /res ; then
mkdir /res ;
cd /res ;
fi
echo "Aplicando filtro [diff]..." ;
sh fich_1.txt


o. Como al hacer diff se puede crear ficheros de tamaño 0, con la siguiente sentencia los elimino.

find /res -size 0 -exec rm {} \;

p. Por último muestro con “ls” el total de ficheros que han cambiado.

ls -l /res


q. El resultado es lo que se muestra a continuación.

2.jpg
3.jpg

Fichero .awk: (solo he podido subirlo en PDF): rulesawk.pdf
Fichero .sh: (solo he podido subirlo en PDF): cambiossh.pdf

Espero que esto os sirva de ayuda o no, quien sabe.

Salu2

domingo, 9 de diciembre de 2007

Práctica 3 - SO II (práctica 2 opción 2)

Buenas tardes, empiezamos esta didáctica guía sobre los estresijos de la práctica 3 de SOII. Este es el resultado que yo he conseguido, como veis hay cosas que no se pedían pero así queda mas bonito. Esta imagen está recortada para que "quepa" en el blog.

Se muestra el número de proceso, el nombre y el número de mensajes enviados.

31.jpg

Imagen en grande: LINK.

Cualidades:
1.- Muestra solo los procesos QUE EXISTEN ACUALMENTE.
2.- Muestra el nombre de los procesos para no estar constantemente pulsando F1 o F3.
3.- Cada vez que se crea un proceso BORRA su contador.
4.- Actualiza el contador no solo al enviar un menaje sino tambien al RECIBIR. (Acordaros de que estamos en rendevouz).

En mi caso yo he realizado una copia del CODIGO FUENTE para mostraros más tarde una cosilla interesante.

Todo el texto que está antes del punto 5.1 es conveniente leerlo para saber de que estamos hablando pero aún asi y todo comenzamos:

5.1 Lo primero que vamos a hacer es proporcionar una funcionalidad sencilla a una tecla de función (F8) que originalmente no tenía ninguna acción asociada. Las teclas de función están definidas bajo el servidor IS en el directorio /usr/src/servers/is. El fichero a modificar para ello se llama dmp.c. Una prueba sencilla de definición de la tecla de función puede ser utilizar la tecla F8 para duplicar la funcionalidad de una ya definida (por ejemplo, F9). Es importante también incrementar la constante que representa el número de enganches definidos (NHOOKS) en el mismo fichero, ya que dicha constante refleja el tamaño del array hooks de teclas de función. Cuando hayamos realizado estos cambios en el fichero dmp.c, recompilaremos el sistema y probaremos su nuevo funcionamiento.


21.jpg

Una vez que modificamos NHOOKS y que duplicamos para la tecla F8 continuamos.

5.2 En este paso vamos a definir las variables y código necesario para contar dentro del kernel el número de mensajes que ha enviado un proceso. Para contar el número de mensajes, una posible solución sería añadir un nuevo campo a los descriptores de la Tabla de procesos, llamado por ejemplo messg_sn. El problema es que estaríamos modificando una estructura muy delicada del kernel de MINIX, de la que también depende el código ensamblador. Por ello, a continuación recomendamos un solución alternativa.


5.2.1 Vamos a definir un array messg_sn con el mismo número de elementos que la tabla de procesos. Este array contendrá el contador de mensajes enviados por cada proceso, de tipo long. La declaración de este array se realizará en el fichero /usr/src/kernel/proc.h como sigue:


EXTERN long messg_sn[NR_TASKS+NR_PROCS];



Además, bastará con pasar una copia íntegra de este array al IS cuando éste lo solicite para efectuar el volcado por pantalla.


Nos quedaremos con éste párrafo ya que será importante para el final de la práctica.


5.2.2 A continuación debemos dar el valor inicial 0 a todos los elementos del array de “mensajes enviados” en el fichero /usr/src/kernel/main.c.


Para esto no hace falta que os diga nada, ya sabeis como poner un array a 0. Solo teneis que tener en cuanta que aquí se debe de inicializar TODO el array.


5.3 En el apartado anterior se han inicializado los contadores de mensajes de los procesos. Ahora vamos a introducir la actualización de dichos contadores de acuerdo con los mensajes enviados en el fichero /usr/src/kernel/proc.c.


En este apartado vamos a tener en cuenta el siguiente gráfico.

111.jpg
211.jpg
311.jpg

5.3.1 El contador de mensajes enviados por un proceso debe incrementarse cada vez que se llame al procedimiento mini_send. Si quisiéramos hacer un control más exacto de los mensajes enviados, deberíamos tener en cuenta cuáles son realmente recibidos pero, por simplicidad, vamos a mantener la solución indicada.


Mas o menos lo que intento decir es que si un proceso NO ESTÁ ESPERANDO un mensaje, el emisor se bloquea y al revés, si no está esperando recibirlo se bloquea el emisor hasta que el receptor quiera.

En la práctica solo se pide que contabilizar cuando un proceso envía, pero tambien sería conveniente aunque no es estrictamente lo que se pide, contabilizar cuando un proceso recibe, la explicación es la siguiente:

Si un proceso A intenta enviar un mensaje un proceso B y éste último no está esperando recibirlo, el proceso A se bloquea. En el supuesto solicitado en la práctica este mensaje enviado se pierde (su cuenta) ya que, el receptor recibe el mensaje cuando hace un mini_receive. Digamos que la práctica solo pide que se contabilice cuando un mensaje es enviado directamente de a un receptor que está esperando recibirlo.

5.3.2 Lo siguiente que nos interesa es posibilitar que el núcleo proporcione al IS información sobre los contadores de mensajes enviados, para que éste pueda proporcionársela, a su vez, a los procesos de usuario. El núcleo proporciona una llamada al sistema sys_getinfo para poder extraer distintas informaciones internas del mismo. Esta llamada se implementa en /usr/src/kernel/system/do_getinfo.c, a través de una sentencia case (switch en C) que extrae la información especificada. Vamos
a definir una nueva etiqueta GET_MESSGSN para indicar en la sentencia case que queremos obtener información sobre los
contadores de mensajes enviados por los procesos. En do_getinfo.c vamos a añadir un nuevo caso etiquetado con la constante GET_MESSGSN. Para dar valor a esta constante, debemos tener en cuenta cuál es el siguiente valor disponible una vez
definidas todas las anteriores etiquetas de la sentencia case. El esqueleto de la sentencia a incluir sería:

case GET_MESSGSN: {
length = ......;
src_phys = vir2phys(......);
break;
}


donde debemos indicar en length la longitud de nuestro array de contadores y como parámetro de vir2phys la dirección física de
messg_sn para que luego se envíe con phys_copy.


En este caso no hace falta comentar mucho, los puntos suspensivos son para decir que en LENGTH se debe poner la longitud del array, si no somos muy listos vamos un par de páginas antes y vemos ejemplos de como se hace y veremos lo siguiente:

a.jpg

Ehhhh que nuestro array no es un STRUCT, no vaya a ser que alguno pongo lo que no es.

En el siguiente apartado tenemos que poner la dirección física y todos sabemos que la dirección física de algo en C se obtiene con:


a1.jpg

5.3.3 Al tratarse de una constante que debe importarse desde el kernel y desde el IS, la constante GET_MESSGSN debe definirse en el fichero /usr/src/include/minix/com.h, y su valor debe ser diferente al de otras etiquetas ya definidas.


Aquí puede pasarte que en vez de poner:

#define GET_MESSGSN .........

pongas

#define GET_MESSGSN 16

y que MINIX al compilar te de un error de identificador por duplicado, PUES NO TE PREOCUPES, me pasó a mi y a un montón de gente, incluso algún profesor seguro que también ha picado. Cuando te dice que el valor debe ser diferente pones uno más al que existe como último, en este caso el último es 16, pues tu sabrás cual es el siguiente.


5.3.4 El fichero /usr/src/include/minix/syslib.h contiene prototipos para permitir que algunos componentes del sistema operativo puedan acceder a los servicios de otros componentes. Por cuestiones estéticas y por seguir el método utilizado por los desarrolladores de MINIX, recomendamos definir en dicho fichero la siguiente macro:

#define sys_getmessgsn(dst) sys_getinfo(GET_MESSGSN,dst,0,0,0)



Esta es una de las líneas más interesantes de la práctica y ya sabrás al final porque, ahora acuerdate que unas lineas arriba dije que recordaras un párrafo, pues este el segundo párrafo que debes recordar.



5.4 Para poder probar que el código introducido es correcto, vamos a conectar la tecla de función que definimos en el paso 1
con la impresión de los contadores de mensajes enviados. Para ello, vamos a empezar por definir una función que nos permita
imprimir los contadores de mensajes enviados.

5.4.1 Tomando como modelo las funciones xxxxdmp del fichero /usr/src/servers/is/dmp_kernel.c, vamos a definir una función
messgsn_dmp que traiga los contadores del kernel y los imprima.


Pues eso te vas al fichero dmp_kernel.c y te creas una función que por ejemplo su ÚNICO codigo para probar sea:

printf("\nMI PRIMER PROGRAMA EN C");

jejejejej

El resultado saldrá cuando COMPILES, REINCIES CON LA OPCIÓN 3 Y PULSES F8.

5.4.2 Para poder definir la función messgsn_dmp en un fichero y asociarla a una tecla en otro, es necesario definir un prototipo
para la misma en /usr/src/servers/is/proto.h.


Pues más de lo mismo, te vas al fichero proto.h y haces un COPY & PASTE de alguna función similar.

5.4.3 Ahora sólo nos queda enganchar la tecla F8 en el fichero /usr/src/servers/is/dmp.c a la nueva función messgsn_dmp.


Esta es la parte más delicada, si no haces esto todo lo que has leído no sirve para nada. Lo que tienes que hacer es decirle a la tecla F8 que ahora su función ya no es la que definistes en el punto 5.1 sino la que tu has creado en el punto 5.4.1

5.4.4 Vamos a compilar en este punto para comprobar si podemos mostrar el array de contadores en su estado actual. Tras
compilar, ejecutaremos los comandos:

halt
boot


Esta parte no necesita explicación.

Una vez dentro de MINIX, teclearemos F8 y veremos si los contadores se han actualizado de forma razonable.


Mi aportación personal es:

1.- Que a la hora de hacer fork se borre el contador de ese proceso.
2.- Mostrar el nombre del proceso i-ésimo.
3.- Mostar solo los proceso NO MUERTOS, es decir VIVOS.

Ahora vienen los párrafos que os decía que recordárais:

1.- El primero es muy importante ya que sin esta pequeña aclaración NO COMPILA. El erro que da es el siguiente:

a2.jpg

Para solucionarlo tienes 2 opciones:

A) Hacer la variable MESSG_SN global en GLO.H (esta opción no la he probado).
B) Hacer la variable MESSG_SN pública en DMP_KERNEL.C (esto es lo que yo hice).

2.- Lo segundo que teneis que tener en cuenta es que por mucho que hagamos público el array hay que llenarlo de datos. Mas concretamente os digo que para hacer que el array tenga datos, hay que llamar a la función SYS_GETMESSGSN, esta macro llamará a SYS_GETINFO y con eso obtenemos en MESSG_SN el array deseado.

Esto es todo, espero haber sido claro al 99,9% tampoco quiero serlo al 100% ya que me llevaría a malo entendidos.

Resultado:

a4.jpg
a3.jpg
a33.jpg
a22.jpg
Archivo completo: LINK.

Salu2.

lunes, 3 de diciembre de 2007

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.

miércoles, 31 de octubre de 2007

Práctica 0 - SO II

Nunca me he planteado hacer un manual de como se deben hacer las cosas, ya que eso da a entender que soy un experto en dichas materias, pero ni mucho menos, se que tengo un interés muy grandes y mucha paciencia por eso me dedico a hacer estas miniFAQ's con un único fin, darme cuenta de si mis conocimientos están verdaderamente asentados.

Empezamos el desarrollo de la práctica 0...

Abrimos el qemu:

qemu1.jpg

Iniciamos (pulsando play) el s.o. MINIX 3.0:

minix42.jpg

Como contraseña escribirmos "root" (sin las comillas) y si no queremos hacer nada más escribirmos:

#cd /usr/src/kernel

Podemos hacer

#ls -l

para ver el contenido, localizamos el fichero main.c, que es donde se encuentra el "main" del kernel.

minix51.jpg

Ahora accedemos al fichero main.c con el editor "elvis", para quien no esté familiarizado con main puede utilizar otros. El editor "elvis" es similiar al "vi" así que para cualquier duda podemos acudir a la página del profesor Luis Cearra y visitando su página obtenemos más información sobre el editor "vi" y los comándos que son válidos para "elvis".

Una vez que entramos en el fichero main.c buscamos la función:

PRIVATE void announce (void) { ... }

en donde debemos añadir nuestro código.

minix6.jpg

Yo por ejemplo he decidido hacerlo en el hueco que veis en la imagen.

Salimos del editor pulsando :wq.

EXPLICACION: Si pulsamos : accedemos al modo comando de "elvis" y con wq lo que hacemos es salvar y salir, es decir "write and quit".

Ahora si seguimos leyendo el enuncido de la práctica nos dice que accedamos al directorio

#/usr/src/tools

que es en donde se encuentran la utildades para compilar y dejar la nueva imagen lista para funcionar.

Escribimos:

#make install

con esto lo que hacemos es compilar el código fuente e instalar la nueva imagen que se instalará en el directorio /boot/image

En el directorio /boot encontraremos las imágenes del s.o.

Compilamos escribiendo lo anterior #make install

minix7.jpg

Una vez que hemos terminado veremos el mensaje DONE al finalizar ahora solo debemos escribir los siguientes comandos:

#halt <--- para salir de MINIX 3.0

Una vez en el monitor escribimos:

menu <--- arrancamos MINIX 3.0 de nuevo

Una vez en el menu de arranque pulsamos el número 3 para arrancar la imagen PERSONALIZADA. Tambien podríamos haber puesto reboot pero eso va a gusto de cada uno, ahora veremos que nuestro mensaje se ha puesto en el arranque.

minix8.jpg

Como vemos yo no soy perfecto y he cometido un error me he olvidad de poner "\n" al final de la setencia kprintf para provocar un salto de linea.

Esto es todo hasta la próxima.

Salu2

martes, 30 de octubre de 2007

Práctica 1 - SO II

La práctica 0 de Minix era una cosa muy sencilla así que no hace falta que la explique.

Me parece injusto no explicar la práctica 0, por lo que la pongo en un post nuevo.

Empezamos entonces con la práctica 1, hace unos días hemos recibido una clase magistrar de Luis Cearra sobre como comunicar el MINIX 3.0 con el sistema operativo (en mi caso Windows XP).

Antes de nada voy a dar unos pasos de como deber hacerse en Windows XP pero seguro que en Linux es igual, aunque no lo he probado por falta de tiempo.

1.- El emulador de Minix qemu vieno por defect sin una unidad virtual A:\, por lo que arrancamos el emulador.

qemu.jpg

2.- Entramos en el asistente (pulsando en el boton que está rodeado con un círculo):

asistente.jpg

3.- Pulsamos en la opción de disco de floppy

asistente2.jpg

4.- Indicamos que queremos crear una imagen nueva.

asistente3.jpg

Nos inventamos un nombre en el cuadro de dialogo y pulsamos FINALIZAR.

Ahora ya tenemos en la carpeta de qemu el nuevo fichero POR EJEMPLO "UNIDADA.IMG", este fichero será la unidad A virtual que utilizará qemu.

5.- Vamos a las opciones del qemu y le indicamos ...

boton_opciones.jpg

Pinchamos en la pestaña CONFIGURACION DE DISCO y seleccionamos como disco floppy A (fda) el fichero que hemos creado antes.

opciones.jpg

6.- Nos descargamos de la web de la asignatura los ficheros .c que nos han entregado.

prueba-1.c
prueba-2.c
prueba-3.c

Una vez hecho esto podemos utilizar el programa POR EJEMPLO winimage que nos ayuda a abrir un fichero de extension .img y añadir ficheros en el.

El programa winimage lo podeis conseguir aquí.

winimage.jpg

Abrimos el fichero .img que hemos creado y que SEGURO QUE HAS COLOCADO EN LA CARPETA \MEDIA DE QEMU para que todo vaya bien. Una vez abierto el winimage, arrastras los 3 ficheros al interior del programa y guardas los resultados, ahora ya tienes dentro del fichero "UNIDADA.IMG" los tres ficheros y que MINIX 3.O podrá leer.

winimage2.jpg

Cierras la imagen, la guardas y ya todo listo para arrancar MINIX 3.O

7.- Arrancar minix 3.0

minix.jpg

Entra con la opción 3 para tener tambien modificado el kernel ya que HAS REALIZADO LA PRACTICA 0 SEGURO QUE CORRECTAMENTE.

8.- Ver el contenido de la unidad fda (fichero "UNIDADA.IMG")

Escribe en el prompt:

dosdir fd0

NO DEBERÁ APARECER NADA. como en la imagen.

minix2.jpg

Para escribir... (tener el cuenta que el programa doswrite utiliza la entrada estandar):

date | doswrite fd0 fichero.txt

Esto escribirá en el fichero.txt por la entrada estandar en el dispositivo fd0.

Aquí se muestran más comandos.

minix3.jpg

Recuerda que doswrite no puede MACHACAR FICHEROS por lo que cada vez que crees uno debes cambiarle de nombre, en la imagen se ve lo que ocurre cuando se intenta escribir en un fichero existente.

Ahora vemos el contenidos de los ficheros .c:

minix4.jpg

Una vez que hemos teminado ya sabes:

#halt ---> para la maquina.
(una vez el prompt del monitor pulsar) off ---> Esto apaga la máquina.

Ahora ya puedes abrir SI QUIERES el fichero .img con winimage otra vez para comprobar los cambios.

winimage3.jpg

Ahora solo nos queda una cosa. Pasar los ficheros que están en la imagen .img a sistema de ficheros de minix para poder trabajar con ellos.

Salu de ael.