Está en la página 1de 10

75.

566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

Presentación
Esta práctica plantea una serie de actividades con el objetivo que el estudiante
pueda aplicar sobre un sistema UNIX algunos de los conceptos introducidos en
los primeros módulos de la asignatura.
El estudiante deberá realizar diversos experimentos y responder a las preguntas
planteadas. También deberá escribir un programa en lenguaje C.
La práctica puede desarrollarse sobre cualquier sistema Unix (la UOC os facilita
la distribución Ubuntu 14.04). Se aconseja que mientras desarrolléis los experi-
mentos no haya otros usuarios trabajando en el sistema porque el resultado de
algunos experimentos puede depender de la carga del sistema.
Cada pregunta sugiere una posible temporización para poder acabar la práctica
antes de la fecha lı́mite ası́ como el peso de la pregunta en la evaluación final.
El peso de esta práctica sobre la nota final de prácticas es del 40 %.

Competencias
Transversales:
Capacidad para adaptarse a las tecnologı́as y a los futuros entornos ac-
tualizando las competencias profesionales
Especı́ficas:
Capacidad de analizar un problema en el nivel de abstracción adecuado a
cada situación y aplicar las habilidades y conocimientos adquiridos para
abordarlo y resolverlo
Capacidad de diseñar y construir aplicaciones informáticas mediante técni-
cas de desarrollo, integración y reutilización

Enunciado
Para realizar esta práctica os facilitamos el fichero pr1so.zip que contiene
ficheros fuente. Descompactadlo con la orden unzip pr1so.zip. Para compilar,
por ejemplo prog.c, debéis ejecutar la orden gcc -o prog prog.c
1. Módulo 2 [ Del 2 al 14 d’octubre ] (25 % = 5 % + 5 % + 5 % + 10 %)
Os facilitamos los programas count1.c, count2.c y el shellscript launch.sh
(no es necesario que estudiéis cómo están implementados).
count1.c ejecuta un bucle infinito donde cada iteración incrementa
un contador (inicializado a 0). Cuando count1.c recibe la notifica-
ción ası́ncrona que indica que ha de finalizar, imprime el valor del
contador y finaliza. count1 emula un proceso de cálculo intesivo.
count2.c ejecuta un bucle infinito donde cada iteración incrementa
un contador (inicializado a 0) y espera un milisegundo (bloqueándo-
se). Cuando count2.c recibe la notificación ası́ncrona que indica que

1
75.566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

ha de finalizar, imprime el valor del contador y finaliza. count2 emula


un proceso interactivo.
launch.sh inicia la ejecución concurrente de N procesos que ejecu-
tan el programa count1 y otros N procesos que ejecutan el progra-
ma count2 (N es un parámetro del shellscript). Por defecto, una vez
transcurridos 3 segundos, provoca la finalización de todos los proce-
sos count. Si queremos que la ejecución dure una cantidad de tiempo
diferente, el shellscript admite un segundo parámetro que indica el
tiempo de ejecución deseado.
A continuación se muestra un ejemplo de funcionamiento. A los 3 segundos
se escriben tantos contadores como procesos creados (primero los de los
procesos count1 y después los de los procesos count2).

Compilad count1.c y count2.c y comprobad que launch.sh funciona en


vuestro sistema de forma similar al ejemplo (deberı́an aparecer la misma
cantidad de números, aunque los valores concretos serán diferentes).
Contestad las siguientes preguntas justificadamente:
1.1. Estudio de los programas count:
1.1.1. A partir de la descripción del comportamiento de count1, di-
bujad un grafo de estados con tres nodos (uno para cada estado
posible del proceso: Ready, Run y Wait) y con los arcos que mues-
tren los cambios de estado posibles mientras un proceso ejecuta
count1.

2
75.566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

En este caso el proceso nunca se blo-


quea porque no solicita nada al sistema
operativo. Por tanto, el proceso count1
sólo cambiará de estado de Run a Ready
y viceversa cuando el planificador pro-
voque un cambio de contexto. El proce-
so morirá cuando reciba la notificación
ası́ncrona.

1.1.2. De forma análoga a 1.1.1., dibujad el grafo que muestre los cam-
bios de estado que puede experimentar un proceso que ejecute
count2.

En este caso el proceso solicita al SO el


servicio de esperar un milisegundo con
lo que en cada iteración entrará en el
estado de Wait y lo abandonará para
pasar a Ready una vez transcurrido ese
tiempo. Como en el caso anterior, el pla-
nificador provocará cambios de estado
de Run a Ready y viceversa.

1.2. Análisis del hardware:


1.2.1. Indicad sobre qué modelo de procesador estáis trabajando. Para
hacerlo, podéis proceder como en el siguiente ejemplo:

1.2.2. ¿Cuántos núcleos fı́sicos (cores) tiene vuestro procesador? Si


vuestro procesador es Intel, os puede resultar útil consultar la
página http://ark.intel.com.

Consultando la página http://ark.intel.com/ vemos que In-


tel i5-650 tiene dos. También puede contestarse consultando el
fichero /proc/cpuinfo.

1.2.3. ¿Cuántos núcleos estáis utilizando realmente? Este número pue-


de ser diferente al obtenido en 1.2.2. en función de cómo esté
configurado el sistema operativo, la máquina virtual (en caso
que utilicéis una) o si tenéis activado el Hyperthreading. Para
contestar esta pregunta, contad cuántas lı́neas escribe la siguien-
te orden y adjuntad un screenshot del resultado obtenido.
grep processor /proc/cpuinfo
Si el resultado es inferior al número que habéis obtenido en 1.2.2.,
reconfigurad vuestro sistema/máquina virtual para conseguir que
el número de núcleos que utilizáis sea, como mı́nimo, igual al

3
75.566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

número de núcleos disponibles. Indicad cómo habéis reconfigura-


do el sistema y volved a mostrar el resultado de grep processor
/proc/cpuinfo.

En la máquina de ejemplo se utilizan dos núcleos.

1.3. Ejecución del script launch.sh


1.3.1. Ejecutad el shellscript launch.sh diversas veces, modificando el
valor del parámetro desde 1 hasta, como mı́nimo, el doble del
valor que habéis contestado en 1.2.3.. Explicad qué tendencia
siguen los contadores impresos por los procesos count1 (los N
primeros contadores mostrados) a medida que aumenta el valor
del parámetro y relacionadla con las respuestas 1.2.2. y 1.2.3..
Mostrad screenshots de las ejecuciones.

Los procesos count1 son de cálcu-


lo intensivo, solicitan utilizar la CPU
contı́nuamente. Mientras el valor del
parámetro es menor o igual que el núme-
ro de cores, cada proceso count1 se eje-
cuta sobre un procesador fı́sico diferen-
te con lo que los contadores alcanzan un
valor similar (en el i5-650, ≈ 2,2 × 109 ).
Si el valor del parámetro es superior al
número de cores, tenemos más procesos
count1 (solicitando continuamente uso
de procesador) que procesadores fı́sicos,
con lo que el planificador de la CPU
ha de ejecutar de forma concurrente los
procesos sobre los procesadores, repar-
tiendo el tiempo de procesador entre los
procesos. En estos casos, si sumamos los
contadores de todos los procesos count1
obtenemos un valor similar (en nuestro
caso, ≈ 4,4 × 109 ): la potencia global
de cálculo del procesador se divide en-
tre varios procesos.

1.3.2. ¿Siguen la misma tendencia los números que escriben los procesos
count2 (los N últimos números)? Justificad la respuesta.

No, el valor de los contadores count2 es aproximadamente el mis-


mo en todos los casos. Esto se debe a que los procesos count2
emulan procesos interacticos, es decir, la mayor parte de su tiem-
po de vida están bloqueados. Por tanto, sus necesidades de CPU
son muy inferiores a la de los procesos count1. Para el número de
procesos count2 creados, la potencia del procesador es suficiente

4
75.566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

para poderlos ejecutar concurrentemente.

1.4. Ejecución de la orden top


1.4.1. Desde otra ventana, poned en funcionamiento la orden top, que
muestra información sobre el uso de la CPU y los procesos que
más la utilizan. Desde la ventana original ejecutad ./launch.sh
1 20 y observad la información que muestra top mientras launch
está en ejecución. ¿Qué podéis concluir?

Vemos un proceso count1 ocupando un core prácticamente al


100 %. Como el proceso no tiene competencia para utilizar la
CPU, el SO puede asignarle un core prácticamente todo el tiem-
po. Los procesos count2 difı́cilmente aparecen en el listado mos-
trado por top porque consumen muy poca CPU.

1.4.2. Repetid el experimento ejecutando launch.sh pasándole como


primer parámetro el valor más grande que habéis utilizado en
el apartado 1.3.1. y 20 como segundo parámetro. ¿Qué podéis
concluir?

Vemos todos los procesos count1 utilizando la CPU a un por-


centaje similar pero claramente por debajo del 100 %. En mi
caso, una máquina con 2 cores y 5 procesos count1 en ejecución,
el porcentaje de utilitzación de de cada proceso es próximo al
40 %. Los 5 procesos compiten para utilizar los 2 cores y el SO
ha de repartir el potencial disponible (200 %) entre los 5 procesos
con lo que cada uno recibe aproximadamente 200/5 = 40 %.

2. Módulo 3: Memoria [ Del 15 al 24 de octubre ] (30 % (30 % + 30 %))


2.1. Os facilitamos el programa stack.c que admite un parámetro numéri-
co (1, 2, 3, 4, 5 o 6). Estudiad su código fuente, compiladlo y eje-
cutadlo. Os adjuntamos un ejemplo de su ejecución. Es posible que
en vuestro sistema la secuencia de números generada en cada ca-
so sea diferente (más larga o más corta) que la de los ejemplos; sin
embargo, el sistema operativo lo aborta en todos los casos (mensaje
Segmentation Fault).

5
75.566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

Contestad a las siguientes preguntas:


2.1.1. En todos los casos, el programa acaba realizando un acceso a me-
moria inválido y el sistema operativo aborta el proceso. Indicad
justificadamente en qué punto del programa se provoca este
acceso inválido en cada caso.

El todos los casos el programa realiza llamadas recursivas, donde


cada llamada recursiva necesita un cierto espacio de pila para
almacenar la dirección de retorno, variables locales y variables
temporales.
En los casos 1, 2, 3, 4 y 5, como la recursividad no finaliza nun-
ca, llega un momento en el que agotamos el espacio máximo
disponible de pila y el SO aborta el proceso. Ésto se produce
en el momento de hacer una llamada recursiva porque en este
momento se ha de reservar espacio en la pila.
En el caso 6 se produce un acceso a memoria incorrecto porque
intentamos escribir más allá de la posición 99 del vector global[]
(ubicado en la zona de datos del proceso, no en la pila). Cuando
el SO lo detecta, el SO aborta el proceso.

2.1.2. ¿A qué se debe que el tiempo que tarda el programa en abortar


sea diferente en cada caso? Indicada justificadamente de qué
depende.

En los tres primeros casos, porque la cantidad de espacio de pila


necesario en cada recursión en la pila es diferente: rec1 no declara
variables locales, rec2 y rec3 declaran un puntero (4 o 8 bytes,
en función de vuestra arquitectura) y pide memoria dinámica
(128 o 256 bytes, pero ésta no se ubica en la pila) y rec4 y rec5
declaran vectores de 16 o 32 integers en cada llamada que se
ubican en la pila. Consecuentemente, rec5 agotará la pila antes
que rec4 y rec4 antes que rec3 y rec2 (que lo harán a la vez),
y éstos antes que rec1..

2.2. Escribid un programa que lea strings del teclado hasta detectar que

6
75.566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

un mismo string ha sido introducido dos veces. Para implementarlo


se utilizarán las rutinas de gestión de memoria dinámica. Se adjunta
un ejemplo de una posible ejecución.

Vuestra solución deberı́a almacenar los strings en una lista donde


cada elemento de la lista almacene el contenido de un string y un
puntero al siguiente elemento de la lista. La lista crecerá dinámica-
mente a medida que leáis strings. Para comprobar si un string ya ha
sido leı́do, el programa debe recorrer todas las posiciones de la lista
y buscar si ya existe.
Como orientación, os falicitamos el esqueleto de una posible solución
(mem3 base.c). Podéis utilizarlo como referencia e implementar el
código ausente.
En el informe de la práctica deberéis entregar el código fuente del
programa y, en caso de funcionar, una captura de pantalla que lo
muestre.
Observaciones:
La solución descrita a este problema no es la más eficiente, pero
es la solución que os pedimos implementar.
El número de strings que hay que leer hasta detectar una repe-
tición puede ser arbitrariamente grande.
Podéis asumir que el tamaño máximo de cada string será de 80
caracteres.
Es necesario que, antes de finalizar, el programa libere toda la
memoria dinámica que haya solicitado.
Para comparar strings debéis utilizar la rutina strcmp().

/∗ i n s e r t s a s t r i n g i n t o t h e l i s t ∗/
struct e l e m e n t ∗ i n s e r t ( char ∗ i n p u t , struct e l e m e n t ∗ l i s t )
{
struct e l e m e n t ∗tmp ;

tmp = m a l l o c ( s i z e o f ( struct e l e m e n t ) ) ;
i f ( tmp == NULL) p a n i c ( ”memory” ) ;

7
75.566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

tmp−>i n p u t = m a l l o c ( s t r l e n ( i n p u t ) + 1 ) ;
i f ( tmp−>i n p u t == NULL) p a n i c ( ”memory” ) ;
s p r i n t f ( tmp−>i n p u t , ” %s ” , i n p u t ) ;

tmp−>n e x t = l i s t ;
return ( tmp ) ;
}

/∗ D e t e c t s i f a s t r i n g i s a l r e a d y p r e s e n t i n t h e l i s t ∗/
i n t r e p e a t e d ( char ∗ i n p u t , struct e l e m e n t ∗ l i s t )
{
struct e l e m e n t ∗tmp = l i s t ;
int idx = 1 ;

while ( tmp != NULL) {


i f ( strcmp ( i n p u t , tmp−>i n p u t ) == 0 ) return ( i d x ) ;
tmp = tmp−>n e x t ;
i d x ++;
}
return ( 0 ) ;
}

/∗ Free a l l o c a t e d memory ∗/
void f r e e l i s t ( struct e l e m e n t ∗ l i s t )
{
struct e l e m e n t ∗tmp = l i s t , ∗ aux ;

while ( tmp != NULL) {


aux = tmp−>n e x t ;
f r e e ( tmp−>i n p u t ) ;
f r e e ( tmp ) ;
tmp = aux ;
}
}

3. Módulo 4: Entrada/Salida [ Del 25 al 28 d’octubre ] (15 %)


El programa args.c muestra la lista de parámetros que recibe por la lı́nea
de órdenes. Se adjuntan diversos ejemplos de ejecución:

Estudiad su código, compiladlo y comprobad que funciona como se indica.


Contestad justificadamente a las siguientes preguntas:

8
75.566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

3.1. En el tercer ejemplo, ¿por qué argc tiene el valor 3 y no 5?

Se debe a que el sı́mbolo | es un metacarácter del intérprete de órde-


nes (shell). Al detectarlo, el shell interpreta el siguiente parámetro
(wc) como un fichero ejecutable y comunica mediante una pipe la sa-
lida estándar del proceso que ejecutará args con la entrada estándar
del proceso que ejecutará wc, sin que el proceso sea consciente (es
decir, no lo ve en el vector argv).

3.2. En el cuarto ejemplo, ¿por qué argc tiene el valor 4 y no 2?

Se debe a que el sı́mbolo * es un metacarácter del intérprete de


órdenes (shell). Al detectarlo, el shell busca todos los ficheros cuyo
nombre encaje con el patrón /bin/l*s. En nuestro caso son tres:
/bin/less, /bin/loadkeys, /bin/ls. Por tanto, el shell invoca el
programa ./args pasándole como parámetros estos tres nombres de
fichero. Consecuentemente, argc valdrá 4.

3.3. Substituid en args.c las dos apariciones de stderr por stdout. Com-
pilad el programa y volved a ejecutar el tercer ejemplo. Explicad a
qué se debe el nuevo comportamiento.

Ahora args escribe su resultado por la salida estándar (stdout). Co-


mo con el metacarácter | comunicamos la salida estándar de args
con la entrada estándar de wc, en esta ejecución todo lo que el progra-
ma escribe por su salida estándar va a parar a wc. Como wc cuenta el
número de lı́neas, palabras y caracteres que le llegan por su entrada
estándar, éste es el resultado que observamos.

Recursos
Módulos 1, 2, 3 y 4 de la asignatura.
El aula ”Laboratorio de Sistemas Operativos” (dudas de Unix, C,...).
Documento ”Intérprete de comandos UNIX” (disponible en el aula) o cual-
quier otro manual similar.
Cualquier manual básico de lenguaje C.

Formato y fecha de entrega


Se entregará un fichero zip que tenga por nombre vuestro identificador en el
campus y que contenga un fichero pdf con la respuesta a las preguntas y un
fichero .c con el código fuente del programa.

9
75.566 · Sistemas Operativos · PRA1 · 20201 · Grado en Ingenierı́a informática

Fecha lı́mite de entrega: 24:00 del 28 de octubre de 2020.

10

También podría gustarte