55
 Práctico1 Introducción ¿Cuales son las dos funciones principales de un Sistema Operativo? GESTIÓN DE RECURSOS: control de discos duros, CDROM y DVDROM, ge stión de periféricos (teclado, ratón, etc...), asignación de cantidades de memoria, etc... INTERFAZ DE USUARIO: nos referimos al modo que tiene el ordenador de pre sentar la información al usuario. Ésta puede ser: GRÁFICA (un escritorio con distintos iconos y barras de menú gobernados por ratón). Es el interfaz comúnmente utilizado por todos nosotros. ?Que es la multiprogramacion? permite que dos o más procesos ocupen la misma unidad de memoria principal y que sean ejecutados al "mismo tiempo" en la unidad central de proceso o CPU. Una de las razones por las que las Interfaces Graficas de Usuario (GUI) fueron adpotadas lentamente, fue por el costo del hardware necesario para soportarlas. ¿Cuanta RAM de video se necesita para una terminal modo texto de 80x25 (columnas, lineas)? 4000 bytes de RAM de vídeo ¿Cuanta para una pantalla grafica de 1024x768 con 24 bits de profundidad de color? 2MB de RAM En 1980 el costo de la memoria era de u$s5/KB, ?Cuanto cuesta en la actualidad? Por su parte, el Commodore VIC-20 (1980 en Japón, 1981 en Europa) fue el precursor del C64, con 5 Kb de RAM y un procesador 6502 a 1 MHz. Fue el primer ordenador en vender más de un millón de unidades, a pesar de que su precio de lanzamiento era de $299 en aquella época. ¿Cuales de las siguien tes instruccione s deberian permitirse en modo kernel? Deshabilitar todas las interrupciones Leer el Reloj de Tiempo Real (RTC) Escribir el RTC Cambiar el mapa de memoria Leer el Reloj de Tiempo Real (RTC) Escribir el RTC si el procesador de textos Writer quiere escribir un archivo (una operació n de sali da) realiza una gran c antidad de opera ciones para formatear la salida y prepararla para enviarla al dispositivo de salida. Un microprocesador tiene un pipeline de 4 etapas. Todas las etapa demoran 1nseg en procesar. ?Cuantas intrucciones por segundo (IPS, KIPS, MIPS) puede ejecutar este micro? Un nanosegundo es la milmillonésima parte de un segundo, 10-9, entonces mil millone s d e nanosegundos son 1 segundo 1 nano segundo es = 0.0000000001 segundos RESPUESTA=0.0000000004

TAREA

Embed Size (px)

Citation preview

Page 1: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 1/55

 

Práctico1 Introducción¿Cuales son las dos funciones principales de un Sistema Operativo?GESTIÓN DE RECURSOS: control de discos duros, CDROM y DVDROM, gestión deperiféricos (teclado, ratón, etc...), asignación de cantidades de memoria, etc...

●INTERFAZ DE USUARIO: nos referimos al modo que tiene el ordenador de presentar lainformación al usuario. Ésta puede ser:○GRÁFICA (un escritorio con distintos iconos y barras de menú gobernados porratón).Es el interfaz comúnmente utilizado por todos nosotros.?Que es la multiprogramacion?permite que dos o más procesos ocupen la misma unidad de memoria principaly que sean ejecutados al "mismo tiempo" en la unidad central de procesoo CPU. 

Una de las razones por las que las Interfaces Graficas de Usuario (GUI) fueronadpotadas lentamente, fue por el costo del hardware necesario parasoportarlas. ¿Cuanta RAM de video se necesita para una terminal modo textode 80x25 (columnas, lineas)?4000 bytes de RAM de vídeo¿Cuanta para una pantalla grafica de 1024x768 con 24 bits de profundidad decolor?2MB de RAM

En 1980 el costo de la memoria era de u$s5/KB, ?Cuanto cuesta en laactualidad?Por su parte, el Commodore VIC-20 (1980 en Japón, 1981 en Europa) fue elprecursor del C64, con 5 Kb de RAM y un procesador 6502 a 1 MHz. Fue elprimer ordenador en vender más de un millón de unidades, a pesar de que suprecio de lanzamiento era de $299 en aquella época.¿Cuales de las siguientes instrucciones deberian permitirse en modo kernel?Deshabilitar todas las interrupcionesLeer el Reloj de Tiempo Real (RTC)Escribir el RTCCambiar el mapa de memoria

Leer el Reloj de Tiempo Real (RTC)Escribir el RTCsi el procesador de textos Writer quiere escribir un archivo(una operación de salida) realiza una gran cantidad de operaciones paraformatear la salida y prepararla para enviarla al dispositivo de salida.

Un microprocesador tiene un pipeline de 4 etapas. Todas las etapa demoran1nseg en procesar. ?Cuantas intrucciones por segundo (IPS, KIPS, MIPS)puede ejecutar este micro?Un nanosegundo es la milmillonésima parte de un segundo, 10-9, entonces milmillones de nanosegundos son 1 segundo

1 nano segundo es = 0.0000000001 segundosRESPUESTA=0.0000000004

Page 2: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 2/55

 

La MMU normalmente compara la direccion virtual entrante con el RegistroLimite, provocando una excepcion cuando la primera se excede. Un diseñoalternativo consiste en primero sumar la direccion virtual al Registro Base ydespues comparar el resultado con la direccion fisica del Registro Limite. ?Sonequivalentes estos metodos? ?Son equivalentes en velocidad?

Cuando una dirección relativa es encontrada es adicionada al registro base ycomparada con el registro limite.Requiriria conjuntos separados de pares de regitros base/limite dedicados paraacceder a espacio de memoria privado y compartido

Cuando un programa de usuario efectua un SysCall para leer o escribir unarchivo en disco, este provee el archivo necesita, un puntero a un buffer dedatos y la cantidad de datos. Entonces, el control pasa al SO, el que llama aldriver apropiado. Supongamos que el driver le da la orden al disco y terminacuando llega una interrupcion. En caso de una lectura, obviamente el programaque llamo tiene que permanecer bloqueado (pues no hay datos). ?Es lo mismo

para una escritura a disco?, es decir, ?Necesita bloquear el programa que pidiola escritura hasta que se complete la transferencia?El n_umero de syscall elegida se pasa en eax y los primerospar_ametros se pasan por registros, en orden: ebx, ecx, edx, esi, edi.Cada syscall tiene un n_umero _unico. Ejemplo en x86: exit es 1,fork es 2, read es 3, write es 4, etc. 5Actualmente hay m_as de 300 syscalls en x86.

De condiciones de falla para cada uno de los SysCall que siguen: fork(), exec()y unlink().pid = fork() - crea un proceso hijo idéntico al proceso padre.s = execve (name, argv, envp) - sustituye la imagen en memoria de un proceso.s = unlink (name) - elimina una entrada del directorio.?Puede la siguente llamada a sistema retornar en count un valor menor quenbytes? Explicar.count = write(fd, buffer, nbytes)Indique la diferencia escencial entre un dispositivo de caracteres y uno debloques.La diferencia es que los dispositivos de bloque tienen un búfer para laspeticiones, por lo tanto pueden escoger en qué orden las van a responder. Esto

es importante en el caso de los dispositivos de almacenamiento, donde es másrápido leer o escribir sectores que están cerca entre sí, que aquellos que estánmás desperdigados. Otra diferencia es que los dispositivos de bloque sólopueden aceptar bloques de entrada y de salida (cuyo tamaño puede variarsegún el dispositivo), en cambio los dispositivos de carácter pueden usarmuchos o unos pocos bytes como ellos quieran. La mayoría de los dispositivosdel mundo son de carácter, porque no necesitan este tipo de buffering, y nooperan con un tamaño de bloque fijo. Se puede saber cuándo un fichero dedispositivo es para un dispositivo de carácter o de bloque mirando el primercarácter de la salida de ls -l. Si es `b' entonces es un dispositivo de bloque, y sies `c' es un dispositivo de carácter.

Laboratorio 2 :)

Page 3: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 3/55

 

Tome un *nix personal (Linux, OpenBSD, MINIX, Darwin, etc.) que puedaromper sin problemas. Escriba un shell script que intente crear un numeroilimitado de procesos hijos y observe lo que sucede. Nota: no intente esto en unlaboratorio de acceso publico.El comando strace da un registro (traza) de todas las SysCalls que llama el

comando que le sigue. Obtenga las trazas de los siguientes comandos,tratando de identificar todos los SysCalls que producen (el comando manpuede ayudar).strace cat /etc/hostsstrace cat (CTRL-D puede ayudar a terminar)strace echo 1strace mozilla (busquese una silla comoda)Intente identificar patrones comunes que correspondan a codigos deinicializacion y terminacion de procesos.28 de Septiembre 2004

Caracteristicas de Linux

Linux implementa la mayor parte de las características que se encuentran enotras implementaciones de UNIX, más algunas otras que no sonhabituales. En esta sección nos daremos una vuelta por todo ello.Linux es un sistema operativo completo con multitarea y multiusuario (comocualquier otra versión de UNIX). Esto significa que pueden trabajar varios

usuarios simultáneamente en él, y que cada uno de ellos puede tener variosprogramas en ejecución.El sistema Linux es compatible con ciertos estándares de UNIX a nivel decódigo fuente, incluyendo el IEEE POSIX.1, System V y BSD. Fue desarrolladobuscando la portabilidad de los fuentes: Encontrará que casi todo el softwaregratuito desarrollado para UNIX se compila en Linux sin problemas. Y todo loque se hace para Linux (código del núcleo, drivers, librerías y programas deusuario) es de libre distribución.En Linux también se implementa el control de trabajos POSIX (que se usa enlos shells csh y bash), las pseudoterminales (dispositivos pty), y tecladosnacionales mediante manejadores de teclado cargables

dinámicamente. Además, soporta consolas virtuales, lo que permite tener másde una sesión abierta en la consola de texto y conmutar entre ellasfácilmente. A los usuarios del programa "screen" les resultará familiar esto.El núcleo es capaz de emular por su cuenta las instrucciones del coprocesador387, con lo que en cualquier 386 con coprocesador o sin él se podrán ejecutaraplicaciones que lo requieran.Linux soporta diversos sistemas de ficheros para guardar los datos. Algunosde ellos, como el ext2fs, han sido desarrollados específicamente paraLinux. Otros sistemas de ficheros, como el Minix-1 o el de Xenix tambiénestán soportados. Y con el de MS-DOS se podrán acceder desde Linux a losdisquetes y particiones en discos duros formateados con MS-DOS. Además,también soporta el ISO-9660, que es el estándar seguido en el formato de losCD-ROMs.

Page 4: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 4/55

 

Linux implementa todo lo necesario para trabajar en red con TCP/IP. Desdemanejadores para las tarjetas de red más populares hasta SLIP/PPP, quepermiten acceder a una red TCP/IP por el puerto serie. También seimplementan PLIP (para comunicarse por el puerto de la impresora) y NFS(para acceso remoto a ficheros). Y también se han portado los clientes de

TCP/IP, como FTP, telnet, NNTP y SMTP.

El núcleo de Linux ha sido desarrollado para utilizar las características delmodo protegido de los microprocesadores 80386 y 80486. En concreto, haceuso de la gestión de memoria avanzada del modo protegido y otrascaracterísticas avanzadas. Cualquiera que conozca la programación del 386 enel modo protegido sabrá que este modo fue diseñado para su uso en UNIX (otal vez Multics). Linux hace uso de esta funcionalidad precisamente.El núcleo soporta ejecutables con paginación por demanda. Esto significa quesólo los segmentos del programa que se necesitan se cargan en memoriadesde el disco. Las páginas de los ejecutables son compartidas mediante la

técnica copy-on-write, contribuyendo todo ello a reducir la cantidad de memoriarequerida para las aplicaciones.Con el fin de incrementar la memoria disponible, Linux implementa lapaginación con el disco: puede tener hasta 256 megabytes de espacio deintercambio o "swap" en el disco duro. Cuando el sistema necesita másmemoria, expulsará páginas inactivas al disco, permitiendo la ejecución deprogramas más grandes o aumentando el número de usuarios que puedeatender a la vez. Sin embargo, el espacio de intercambio no puede suplirtotalmente a la memoria RAM, ya que el primero es mucho más lento que ésta.La memoria dedicada a los programas y a la cache de disco está unificada. Porello, si en cierto momento hay mucha memoria libre, el tamaño de la cache dedisco aumentará acelerando así los accesos.Los ejecutables hacen uso de las librerías de enlace dinámico. Esto significaque los ejecutables comparten el código común de las librerías en un únicofichero, como sucede en SunOS. Así, los ejecutables serán más cortos a lahora de guardarlos en el disco, incluyendo aquellos que hagan uso de muchasfunciones de librería. También pueden enlazarse estáticamente cuando sedeseen ejecutables que no requieran la presencia de las librerías dinámicas enel sistema. El enlace dinámico se hace en tiempo de ejecución, con lo que elprogramador puede cambiar las librerías sin necesidad de recompilación de losejecutables.

Para facilitar la depuración de los programas, el núcleo de Linux puede generarvolcados de la imagen de memoria de los programas (ficheros core). Entreesto y la posibilidad de compilar ejecutables con soporte de depuración, elprogramador podrá averiguar la causa de los fallos de su programa.Introducción a bash: crear un sencillo scriptabril 27th, 2009 Posted in Bash, linuxCon este artículo voy a iniciar una corta serie de artículos para aprender aescribir sencillos scripts de bash.Bash es un intérprete de comandos de Linux . Los que han trabajado conWindows/MS-Dos les sonarán seguramente los archivos .bat. Estos scriptsbash son algo muy similar.

Para crear nuestro primer script debemos crear un fichero, por ejemploprimero.sh y copiamos el siguiente contenido:

Page 5: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 5/55

 

echo "Eh, este es mi primer script bash" Ahora tenemos que hacerlo “ejecutable”, para ello teclearemos en la consola:  chmod +x primero.shy ahora podemos ejecutarlo:./primero.sh

Y ahora vamos a darle un poco más de gracia al asunto. En un script podemosusar los mismos comandos que si estuviéramos en la consola de Linux, porejemplo: ls -las. Vamos a modificar el script:echo "Este es el listado de directorios y ficheros:"ls -lasMás adelante veremos que se pueden pasar parámetros a un script bash, sepueden usar variables, bucles, condiciones, arrays, etc… 

Práctica2 ProcesosProcesos e Hilos?Por que un hilo dejaria voluntariamente la CPU con un thread_yield si sabeque despues de esto puede que se quede sin CPU para siempre?los threads tienden a monopolizar la CPUsede el control si se hacen llamadas que dejen dormido al thread en espera dealgo, como wait(), sleep()En realidad nuestro ordenador, salvo que tenga varias cpu, no ejecutará variascosas a la vez. Cuando digo "a la vez", me refiero a que el sistema operativo iráejecutando cachos de programa por turnos (por rodajas de tiempo) de formamuy rápida, dando la sensación de simultaneidad.En un sistema con hilos, ?hay un solo stack por hilo o un stack por procesocuando se usan hilos en espacio de usuario?

Una pila (stack en inglés) es una estructura de datos de tipo LIFO (del inglésLast >In First Out, último en entrar, primero en salir) que permite almacenar yrecuperar >datos. Se aplica en multitud de ocasiones en informática debido asu simplicidad >y ordenación implícita en la propia estructuraLo más importante de esta estructura es que en cada momento sólo se tieneacceso a la parte superior del stack (no a las cosas que están apiladas debajo).Entonces cada subrutina puede guardar sus datos en el stack, y las subrutinasa las que llame no los afectarán.(Bueno, en realidad dentro de la memoria hay varios stacks, en general cadaprograma tiene el suyo.?Y si se usan hilos a nivel de kernel?

El intercambio de los hilos no necesita los privilegios del modo kernel, por quetodas las estructuras de datos están en el espacio de direcciones de usuario deun mismo proceso. Por lo tanto, el proceso no debe cambiar a modo kernelpara gestionar hilos. Se evita la sobrecarga de cambio de modo y con esto elsobrecoste.Para el siguiente programa decidir que valores se pueden imprimir.

int a, *ptr_b;a = 0;ptr_b = malloc(sizeof(int)); *ptr_b = 0;if (fork()!=0) {a = 2;*ptr_b = 4;printf("%d %d\n", a, *ptr_b);

Page 6: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 6/55

 

} else {a = 5;*ptr_b = 7;printf("%d %d\n", a, *ptr_b);}

LOS VALORES DE “a” y "b” Pensar en el mismo codigo, solo que en vez de procesos tenemos hilos.Condiciones de CarreraDado el siguiente par de procesos que insertan datos en el spooler deimpresion, donde cola es un espacio de memoria compartida y libre es unavariable local a cada proceso.

P0:.libre := cola->fin+1;

cola->buffer[libre] := job4;cola->fin := libre;.

P1:.libre := cola->fin+1;

cola->buffer[libre] := job5;cola->fin := libre;.

Dar una planificacion o escenario que produce la perdida de un trabajo.Dar una planificacion o escenario que funciona correctamente.?Cuantas planificaciones existen para estas 3+3 lineas de codigo? ?Cuantasson correctas y cuantas producen problemas? (determine el %)Se tienen 2 procesos P0 y P1, P0 con N acciones atomicas y P1 con Macciones atomicas. Calcular cuantos escenarios posibles de ejecucion sepueden dar en un entorno concurrente.Considere los procesos P0 y P1, con un valor inicial de x=0. ?Cuales son losvalores finales de x?

{ x=0 }

P0:x:=x+1;

P1:x:=x+1;

X=1(para ambos valores)Si ahora consideramos que x:=x+1 no es atomico a nivel de ensamblador y secompila en una secuencia de accesos a memoria y operaciones de la ALU,equivalentes al siguiente multiprograma. ?Cuales son los valores finales de x?

{ x=0 }

P0:A := x;A := A+1;x := A;

P1:A := x;A := A+1;x := A;

Regiones CriticasMostrar que agregando un re-testeo de lock=0 luego del busy waiting enmetodo de la variable candado, no soluciona nada.Mostrar que en alternancia estricta el proceso P0 puede impedir al P1 entrar ala CS aunque P0 este fuera de ella.Deshabilitar interrupciones no funciona para regiones criticas anidadas (una

dentro de la otra). Reescribir Begin/EndRegion para arreglar este problema.Generalizar la alternancia estricta a 3 procesos. Generalizarla a n.

Page 7: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 7/55

 

Demostrar de manera rigurosa que el algoritmo de Peterson no adolece delproblema de progreso de la alternancia estricta.{Dificil} Demostrar de manera rigurosa que el algoritmo de Peterson cumple conla propiedad de seguridad de la Region Critica (#proc_in_cs≤1).?Sigue cumpliendo con la propiedad de seguridad el algoritmo de Peterson si

intercambiamos las 2 primeras asignaciones de su BeginRegion? Demostrar odar contraejemplo.Implementar un spin lock con TestInc.

atomic function TestInc(var lock:integer):integer;result:=lock;lock:=lock+1;return result;

Implementar un spin lock con Swap.atomic procedure Swap(var v,w: integer);

tmp:=v;v:=w;w:=tmp;

Primitivas de sincronizacionDados 3 procesos P0, P1 y P2 que realizan las acciones A0, A1 y A2, poner P'sy V's de semaforos antes y despues de las acciones para sincrinizarlos demanera tal que se ejecuten en secuencia A0,A1,A2. Especificar el/los valor/esinicial/es de el/los semaforo/s.Implementar utilizando semaforos la Region Critica N-1, es decir dentro de laregion critica puede haber a lo mas N-1 procesos.{Dificil} Implemente semaforos generales (el semaforo almacena valores

arbitrarios) usando semaforos binarios (solo pueden valer 0 o 1).Implementar semaforos utilizando monitores.{Dificil} La sincronizacion en monitores usa variables de condicion y dosoperaciones especiales, wait y signal. Una forma mas general desincronizacion son las esperas por condiciones con auto-señalizacion, dondeescribimos await CondicionBooleana para bloquear el proceso hasta que lacondicion se cumpla. Por ejemplo el BeginWrite del problema de lectores yescritores se resume a escribir await nr=0∧nw=0 dentro del monitorClaramente este esquema es mas general y abstracto que los monitores deHoare y Brinch-Hansen, pero no se usan. ?Por que?Ayuda: pensar en la implementacion.{Mediano} El problema de lectores y escritores puede ser formulado de variasformas respecto a cuando cada categoria de procesos puede empezar. Demanera cuidadosa describa 3 variaciones distintas del problema, cada unafavoreciendo (o desfavoreciendo) alguna categoria de procesos. Para cadavariacion, especifique que sucede cuando un lector o escritor esta listo paraacceder a la base de datos y que sucede cuando un proceso ha terminado deusar la base de datos.Tenemos un Baño Unisex donde puede haber de manera excluyente varones ymujeres. Mediante semaforos sincronice la entrada y la salida del baño porparte de varones y mujeres (EntraVaron, SaleVaron, EntraMujer, SaleMujer) de

manera que se cumpla con el invariante nv=0∨

nm=0, es decir que no semezclen.Este problema clasico de sincronizacion tambien se conoce con el nombre de

Page 8: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 8/55

 

problema del Puente de Una Via, y modela el acceso a un recurso que puedeser compartido por muchos procesos de un tipo o (excluyente) de otro.PlanificadoresLos planificadores RR mantienen una lista de todos los procesos listos paracorrer, donde cada proceso aparece exactamente una vez. ?Que pasaria si

hubiera 2 o mas apariciones de un proceso en la lista de procesos para correr??Para que serviria??Se podra "medir" si un proceso es IO-bound o CPU-bound analizando elcodigo fuente? (analisis estatico) ?Como se podria determinar en tiempo deejecucion? (analisis dinamico)Mediciones realizadas en un sistema muestran que en promedio un procesocorre por T tiempo antes de bloquearse en I/O. Un cambio de proceso requierede un tiempo S, denominado overhead (sobrecarga). Para un planificador RRcon un quanto Q, dar una formula que mida la eficiencia de la CPU (tiempousado en CPU/tiempo total) para los siguientes casos.Q = infinito

Q>TS<Q<TQ=SQ casi 0El planificador SPN (shortest process next) necesita de una medida deestimacion del tiempo de computo. Con un "factor de memoria" α=1/2, dondelos ultimos tiempos de corrida fueron: 40, 20, 40 y 15 msec. ?Cual es laestimacion del tiempo siguiente?Dar un conjunto de procesos con sus tiempos de arribo y sus tiempos de usode CPU, que muestren que SJF!=SRTN (shortest job first, shortest remainingtime next).Completar la tabla para las siguientes politicas de planificacion: FCFS, SJN,SRTN, RR (Q=1), RR (Q=5).

Proc Arribo UsoCPUA 0 3B 2 6C 3 10D 7 1E 8 5F 15 2

G 25 7

Práctico3 Deadlock Procesos e HilosEstudiantes trabajando en sus computadoras personales de un laboratorioenvian sus archivos para imprimir hacia un server que hace spooling en eldisco duro. ?Bajo que condiciones puede ocurrir un deadlock si el espacio dedisco para el print spool esta limitado?Eliminando la exclusión mutua: ningún proceso puede tener acceso exclusivo aun recurso. Esto es imposible para procesos que no pueden ser encolados

(puestos en un spool), e incluso con colas también pueden ocurririnterbloqueos.

Page 9: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 9/55

 

¿Como se puede evitar dicho deadlock?Eliminando la exclusión mutua: ningún proceso puede tener acceso exclusivo aun recurso. Esto es imposible para procesos que no pueden ser encolados(puestos en un spool), e incluso con colas también pueden ocurririnterbloqueos. La condición de retención y espera puede ser eliminada

haciendo que los procesos pidan todos los recursos que van a necesitar antesde empezar. Este conocimiento por adelantado muchas veces es imposiblenuevamente. Otra forma es requerir a los procesos liberar todos sus recursosantes de pedir todos los recursos que necesitan. Esto también es impráctico engeneral.La condición de no expropiación puede ser también imposible de eliminar dadoque un proceso debe poder tener un recurso por un cierto tiempo o elprocesamiento puede quedar inconsistente.La condición de espera circular es la más fácil de atacar. Se le permite a unproceso poseer sólo un recurso en un determinado momento, o una jerarquíapuede ser impuesta de modo tal que los ciclos de espera no sean posibles.

En el deadlock tipico de 2 procesos y 2 recursos, ?Cambiaria la situacion si sedevuelven los recursos en otro orden?En el siguiente ejemplo, dos procesos compiten por dos recursos que necesitanpara funcionar, que sólo pueden ser utilizados por un proceso a la vez. Elprimer proceso obtiene el permiso de utilizar uno de los recursos (adquiereel lock sobre ese recurso). El segundo proceso toma el lock del otro recurso, yluego intenta utilizar el recurso ya utilizado por el primer proceso, por lo tantoqueda en espera. Cuando el primer proceso a su vez intenta utilizar el otrorecurso, se produce un interbloqueo, donde los dos procesos esperan laliberación del recurso que utiliza el otro proceso.Las trayectorias de recursos o diagramas de procesos bidimensionales tienentrayectorias horizontales o verticales. ?Cuando puede suceder que lastrayectorias sean diagonales?

?Que es un deadlock de un solo proceso?Es un proceso que solicita recursos, y si los recursos no están disponibles enese momento, el proceso entra en un estado de espera. Puedesuceder que los procesos en espera nunca cambien de estado, debidoa que los recursos que han solicitado están siendo detenidos por otrosprocesos en espera.

?Porque esta situacion no causa ninguna preocupacion dentro del SistemaOperativo?Un sistema consiste de un número finito de recursos que sondistribuidos entre un número de procesos que compiten por ellos.Los recursos son clasificados en diferentes tipos, cada uno de loscuales consiste de algún número de instancias igualesa. Ciclos de CPUb. Espacio en memoriac. Archivos y dispositivos de E/S (tales como impresoras, unidadesde cinta y de disco, etc.) son ejemplos de tipos de recursos. Siun sistema tiene dos CPU's, entonces el tipo CPU tiene dos

instancias.Un proceso debe solicitar un recurso antes de usarlo y liberarlo

Page 10: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 10/55

 

después de usarlo.Un proceso puede solicitar tantos recursos como sean necesarios parallevar a cabo la tarea para la cual ha sido diseñado. Obviamente elnúmero de recursos solicitados no debe exceder el número derecursos disponibles en el sistema.

?Se puede estar en estado de asignacion de recursos (EAR) que no este endeadlock y que no sea seguro? Ejemplo o demostracion de lo contrario.Una solución alternativa sería corregir el deadlock, y una opción para esto esque al solicitar unrecurso (por ejemplo, un registro de una base de datos) se inicialice uncontador que indique un tiempo de espera para recibir el bloqueo y si el tiempode espera expira entonces se decide abortar. Esta opción tendrá un impactomuy grande dependiendo si el tiempo de espera es muy grande o pequeño, yaque no se sabe si el plazo expiró debido a un deadlock real o no. De estamanera, si el tiempo de espera es muy pequeño, crece la probabilidad deabortar una gran cantidad de transacciones que no estaban en deadlock, si el

tiempo es muy grande se corre el peligro de desperdicialo cuando el deadlockexiste e incluso de provocar otros.Un sistema tiene 4 recursos del mismo tipo compartidos por 3 procesos, dondecada uno de los procesos necesita a lo mas 2 recursos. Muestre que el sistemaesta libre de deadlocks.Para detectar un deadlock, se puede usar el mismo algoritmo del banquero,que aunque no dice que hay undeadlock, sí dice cuándo se está en estado inseguro que es la antesala deldeadlock. Sin embargo, paradetectar el deadlock se pueden usar las 'gráficas de recursos'. En ellas sepueden usar cuadrados para indicarprocesos y círculos para los recursos, y flechas para indicar si un recurso yaestá asignado a un proceso o si unproceso está esperando un recurso. El deadlock es detectado cuando se puedehacer un viaje de ida y vueltadesde un proceso o recurso. Por ejemplo, suponga los siguientes eventos:evento 1: Proceso A pide recurso 1 y se le asigna.evento 2: Proceso A termina su time slice.evento 3: Proceso B pide recurso 2 y se le asigna.evento 4: Proceso B termina su time slice.evento 5: Proceso C pide recurso 3 y se le asigna.

evento 6: Proceso C pide recurso 1 y como lo está ocupando el proceso A,espera.evento 7: Proceso B pide recurso 3 y se bloquea porque lo ocupa el procesoC.evento 8: Proceso A pide recurso 2 y se bloquea porque lo ocupa el proceso B

Una computadora tiene 6 grabadoras de CDs, con n procesos compitiendo porellas. Cada proceso necesita 2 grabadoras. ?Para que valores de n estesistema esta libre de deadlocks?

Se tiene el siguente Estado de Asignacion de Recursos donde juegan 3

procesos y 2 tipos de recursos.Proceso C M A

Page 11: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 11/55

 

P0 3 4 1 5 2 3P1 2 3 3 6P2 4 2 2 0

Dibujar el grafo de asignacion de recursos.

Decidir si algun subconjunto de procesos esta en deadlock.Se tiene el siguente Estado de Asignacion de Recursos:Proceso C M AP0 0 0 1 2 0 0 0 0 1 5 2P1 1 0 0 0 0 7 5 0P2 1 3 5 4 1 0 0 2P3 0 6 3 2 0 0 2 0P3 0 0 1 4 0 6 4 2

Decidir si el estado es seguro.?Si llega un pedido del P1 por (0,4,2,0), se lo puede otorgar inmediatamente?Tenemos un EAR con 4 procesos y 5 clases de recursos. Los valores de losvectores y matrices son como siguen.

Proceso C M AP0 1 0 2 1 1 0 1 0 0 2 0 0 x 1 1P1 2 0 1 1 0 0 2 1 0 0P2 1 1 0 1 0 1 0 3 0 0P3 1 1 1 1 0 0 0 1 1 1

?Para que valores de x resulta seguro?Tenemos 2 procesos, P0 y P1, cada uno de ellos necesita 3 registros de una

base de datos (R0, R1, R2). Si P0 pide los registros en ese orden y P1tambien, no hay posibilidad de deadlock, en cambio si P0 pide en el orden(R0,R1,R2) y P1 en (R2, R1, R0), hay posibilidad de deadlock. Con 3 recursoshay 3! combinaciones para pedir estos 3 recursos por parte de cada proceso.?Que fraccion de estas combinaciones estan libres de deadlock?Un estudiante de ciencias de la computacion que esta pensando en el tema dedeadlocks piensa en la siguiente idea brillante para eliminar deadlocks. Cuandoun proceso pide un recurso, especifica un limite de tiempo. Si el proceso sebloquea porque el recurso no esta disponible, se inicia un temporizador. Si seexcede el limite de tiempo, se libera el proceso y se lo reinicia.Si usted fuera el profesor, ?que nota le pondria a la propuesta del alumno?

?Por que?

Tarea 4Parte ABuscar información acerca de la estructura del directorio /proc, y averiguar lossiguientes datosTipo y modelo de CPU.Versión del kernel.Tiempo en días, horas, minutos y segundos que han transurrido desde que seinició el sistema operativo.

Page 12: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 12/55

 

Cuanto tiempo de CPU ha sido empleado para procesos de usuario, de sistemay cuando tiempo no se usó.Cuanta memoria tiene y cuanta está disponible.Cuantos pedidos de lectura/escritura a disco se han realizado.Cuantos cambios de contexto han sucedido.

Cuantos procesos se crearon desde que inició el sistema.cpuinfo: información estática de la CPUmeminfo: información de uso de la memoriapartitions: información sobre las particionesfilesystems: sistemas de ficheros soportados por el kernelversion: versión y fecha del kernelbus/: directorio con información de los buses PCI y USBcmdline: línea de arranque del kerneldevices: dispositivos del sistema de caracteres o bloqueside/: directorio con información del bus IDEmodules: módulos del kernel

net/: directorio con información de redel directorio desde que se invoco el proceso (enlace cwd)nombre del ejecutable (enlace exe) y la línea de comandos con la que fueinvocado (fichero cmdline)entorno en que se ejecuta el proceso (fichero environ)estado del proceso (fichero status)descriptores de ficheros abiertos y archivos o procesos relacionados(directorio fd)mapa de memoria (fichero maps)

Estos datos se tomaron del servidor de alumnos:Type: GenuineIntelModel: Intel(R) Xeon(TM) CPU2.40GHzKernel: 2.4.18-custom.1.3UpTime: 1D 5:32:47.43UserTime: 1:49.9SysTime: 6:55.32IdleTime: 1D 5:24:03.02TotalMem: 922587136FreeMem: 15532032

Disk: 667810Context: 16336100Processes: 18224Parte BEscriba una versión inicial del programa que inspeccione las variables delkernel a través del /proc e informe por stdout:Tipo y modelo de CPU.Versión de Kernel.Cantidad de tiempo transcurrido desde que se inició el sistema operativo, conel formato ddD hh:mm:ss.También se pide incluir una cabecera donde se indique el nombre de lamáquina y la fecha y hora actuales.

Page 13: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 13/55

 

Una llamada correcta a open() devuelve un entero que corresponde aldescriptor de fichero para manejar el fichero abierto. Cada proceso una tablade descriptores de fichero que le permiten manejar dichos ficheros de formasencilla. Inicialmente las entradas 0, 1 y 2 de esa tabla están ocupadas por losficheros STDIN, STDOUT y STDERR respectivamente, es decir, la entrada

estándar, la salida estándar y la salida de error estándar:Parte CEscriba una segunda versión del programa que imprima la misma informaciónque la versión por defecto, pero en caso de invocarse con la opción -s, agregala siguiente información:Cantidad de tiempo de CPU utilizado para usuarios, sistema y proceso idle.Cantidad de cambios de contexto.Fecha y hora cuando el sistema fue iniciado.Número de procesos creados desde el inicio del sistema.Para cerrar un fichero basta con pasarle a la syscallclose() el descriptor de fichero como argumento:

int close( int fd)Resulta bastante sencillo. Veamos todo esto en acción en un ejemplo:#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main( int argc, char *argv[] ){int fd;if( (fd = open( argv[1], O_RDWR )) == -1 ){perror( "open" );exit( -1 );}Programación de Sistemas 25printf( "El fichero abierto tiene el descriptor %d.\n", fd );close( fd );return 0;}Inicialmente tenemos los ficheros de cabecera necesarios, tal y comohemos venido explicando hasta aquí. Seguidamente declaramos la variable“fd” que contendrá el descriptor de fichero, y realizamos una llamada a open(), guardando en “fd” el resultado de dicha llamada. Si “fd” es –1 significa

que se ha producido un error al abrir el fichero, por lo que saldremosadvirtiendo del error. En caso contrario se continúa con la ejecución delprograma, mostrando el descriptor de fichero por pantalla y cerrando elfichero después. El funcionamiento de este programa puede verse aquí:txipi@neon:~$ gcc fichero.c –o ficherotxipi@neon:~$ ./fichero fichero.cEl fichero abierto tiene el descriptor 3.El siguiente paso lógico es poder leer y escribir en los ficheros quemanejemos. Para ello emplearemos dos syscalls muy similares: read() y write().Aquí tenemos sus prototipos:ssize_t read( int fd, void *buf, size_t count )

ssize_t write( int fd, void *buf, size_t count )La primera de ellas intenta leer “count” bytes del descriptor de fichero  

Page 14: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 14/55

 

definido en “fd”, para guardarlos en el buffer “buf”. Decimos “intenta” porque  es posible que en ocasiones no consiga su objetivo. Al terminar, read()devuelve el número de bytes leídos, por lo que comparando este valor con lavariable “count” podemos saber si ha conseguido leer tantos bytes como  pedíamos o no. Los tipos de datos utilizados para contar los bytes leídos

pueden resultar un tanto extraños, pero no son más que enteros e estaversión de GNU/Linux, como se puede ver en el fichero de cabeceras:txipi@neon:~$ grep ssize /usr/include/bits/types.htypedef int __ssize_t; /* Type of a byte count, or error. */ txipi@neon:~$ grep ssize /usr/include/unistd.h#ifndef __ssize_t_definedtypedef __ssize_t ssize_t;# define __ssize_t_defined[…] El uso de la función write() es muy similar, basta con l lenar el buffer “buf” con lo que queramos escribir, definir su tamaño en “count” y especificar el fichero en el que escribiremos con su descriptor de fichero en “fd”. Veamos  todo esto en acción en un sencillo ejemplo:#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>Programación de Sistemas 26#define STDOUT 1#define SIZE 512int main( int argc, char *argv[] ){int fd, readbytes;char buffer[SIZE];if( (fd = open( argv[1], O_RDWR )) == -1 ){perror( "open" );exit( -1 );}while( (readbytes = read( fd, buffer, SIZE )) != 0 ){ /* write( STDOUT, buffer, SIZE ); */ write( STDOUT, buffer, readbytes );}

close( fd );return 0;}Como se puede observar, inicialmente definimos dos constantes, STDOUTpara decir que el descriptor de fichero que define la salida estándar es 1, ySIZE, que indica el tamaño del buffer que utilizaremos. Seguidamentedeclaramos las variables necesarias e intentamos abrir el fichero pasado porparámetro (argv[1]) con acceso de lectura/escritura. Si se produce un error (lasalida de open() es –1), salimos indicando el error, si no, seguimos. Despuéstenemos un bucle en el que se va a leer del fichero abierto (“fd”) de SIZE en  SIZE bytes hasta que no quede más (read() devuelva 0 bytes leídos). En cada

vuelta del bucle se escribirá lo leído por la STDOUT, la salida estándar.Finalmente se cerrará el descriptor de fichero con close().

Page 15: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 15/55

 

En resumidas cuentas este programa lo único que hace es mostrar elcontenido de un fichero por la salida estándar, parecido a lo que hace elcomando “cat” en la mayoría de ocasiones.  Existe una línea de código que está comentada en el listado anterior: /* write( STDOUT, buffer, SIZE ); */ 

En esta llamada a write() no se está teniendo en cuenta lo que hadevuelto la llamada a read() anterior, sino que se haya leído lo que sehaya leído, se intentan escribir SIZE bytes, es decir 512 bytes. ¿Quésucederá al llamar al programa con esta línea en lugar de con laotra? Bien, si el fichero que pasamos como parámetro esmedianamente grande, los primeros ciclos del bucle whilefuncionarán correctamente, ya que read() devolverá 512 comonúmero de bytes leídos, y write() los escribirá correctamente. Pero enla última iteración del bucle, read() leerá menos de 512 bytes,porqueProgramación de Sistemas 27es muy probable que el tamaño del fichero pasado por parámetro no

sea múltiplo de 512 bytes. Entonces, read() habrá leído menos de512 bytes y wr ite() seguirá tratando de escribir 512 bytes. Elresultado es que write() escribirá caracteres “basura” que se  encuentran en ese momento en memoriaParte DLa tercera parte involucra imprimir todo lo de las versiones anteriores, perocuando se invoca con la opción -l interval duration imprime además:Número de peticiones a disco realizadas.Cantidad de memoria configurada en el hardware.Cantidad de memoria disponible.Lista de los promedios de carga de 1 minuto.Asi por ejemplo ksamp -l 2 100 mostrará el promedio de carga de 1 minuto por100 segundos tomando muestras en intervalos de 2 segundos.Aclaración 29 Agosto: Hay mucha gente que preguntó por este último punto, elenunciado no esta del todo claro. El comando ksamp -l a b, lee los datosindicados arriba (peticiones de disco, cantidades de memoria, promedios decarga) de /proc, y los imprime repetidamente cada a segundos; esto se repitehasta que hayan pasado b segundos. Por ejemplo:[so2004@hal so2004]$ ksamp -l 5 15Peticiones a disco: 12345Memoria disponible / total: 1000000 / 8000000

Promedio de carga en el último minuto: 0.88[Pausa de 5 segundos]Peticiones a disco: 12348Memoria disponible / total: 2000000 / 8000000Promedio de carga en el último minuto: 0.82[Pausa de 5 segundos]Peticiones a disco: 12645Memoria disponible / total: 500000 / 8000000Promedio de carga en el último minuto: 0.98[Pausa de 5 segundos]Peticiones a disco: 99995

Memoria disponible / total: 300000 / 8000000Promedio de carga en el último minuto: 1.07

Page 16: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 16/55

 

En particular noten que el promedio de carga de un minuto es un valor que elsistema provee ya calculado, no es algo que ustedes tengan que calcular. Finde aclaración 29 Agosto.Notar que cada opción incluye a la otra, por lo que ksamp -s -l 2 100 no deberíaser aceptado y en tales casos resulta útil imprimir por la salida standard un

resumen de las opciones aceptadas.Con system() nuestro programa consigue detener su ejecución para llamara un comando de la shell (“/bin/sh” típicamente) y retornar cuando éste haya acabado. Si la shell no está disponible, retorna el valor 127, o  –1 si seproduce un error de otro tipo. Si todo ha ido bien, system() devuelve el valorde retorno del comando ejecutado. Su prototipo es el siguiente:int system(const char *string);Donde “string” es la cadena que contiene el comando que queremos ejecutar, por ejemplo:system(“clear”); Esta llamada limpiaría de caracteres la terminal, llamando al comando“clear”. Este tipo de llamadas a system() son muy peligrosas, ya que si no indicamos el PATH completo (“/usr/bin/clear”), alguien que conozca nuestra llamada (bien porque analiza el comportamiento del programa, bien por usarel comando strings, bien porque es muy muy muy sagaz), podría modificar elPATH para que apunte a su comando clear y no al del sistema (imaginemosqueel programa en cuestión tiene privilegios de root y ese clear se cambia poruna copia de /bin/sh: el intruso conseguiría una shell de root).La función system() bloquea el programa hasta que retorna, y ademástiene problemas de seguridad implícitos, por lo que desaconsejo su uso másallá de programas simples y sin importancia.Programación de Sistemas 39La segunda manera de crear nuevos procesos es mediante fork(. )Estafunción crea un proceso nuevo o “proceso hijo” que es exactamente igual  que el “proceso padre”. Si fork( se ejecu ) ta con éxito devuelve:  • Al padre: el PID del proceso hijo creado.• Al hijo: el valor 0. Cómo atacar los problemasLa página del manual de Linux man 5 proc contiene suficiente información alrespecto, también se pueden ver artículos de la revista Linux Magazine " AnOverview of the Proc Filesystem " o The Official Red Hat Linux ReferenceGuide en su capítulo "The /proc Filesystem ". En general basta con realizar una

búsqueda de "/proc filesystem" en cualquier buscador de la Web para encontrarinformación en cualquier idioma.Una vez encontrados los archivos de /proc donde está la información, esnecesario abrirlos, leer la información y cerrarlos. Esto se puede lograr con lasfunciones de la biblioteca "C" fopen, fgets (o fscanf) y fclose. Un ejemplo deuso sería el siguiente.

#include <stdio.h>#define BUFFSIZE 256

int

main(int argc, char *argv[]) {

Page 17: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 17/55

 

FILE *fd;char buffer[BUFFSIZE+1];fd =fopen("/proc/sys/kernel/hostname","r");fgets(buffer, BUFFSIZE+1, fd);

printf("Hostname: %s\n",buffer);fclose(fd);

}Para facilitar el proceso de lectura y análisis de los archivos, les facilitamos unTAD para hacer lexing (separación de la entrada en tokens, es decir elementoscomo palabras, números, simbolos, espacios en blanco). Pueden usarlo en suproyecto tal como está, y les aconsejamos hacerlo así no pierden tiempo eneste aspecto del proyecto. El fuente del TAD lo pusimos en un paqueteLexer.tar.gz. Para leer los argumentos de entrada e interpretarlos (proceso de parsing) hayque hacer uso de las variables int argc y char *argv[]. La primera indica cuantosargumentos se pasaron y argv es el arreglo de tamaño argc con cada uno delas cadenas de los argumentos. Notar que como el propio comando se incluyeen la lista de argumentos una llamada ksamp -l 10 100 implica que al inicio delmain se cumple{argc=4 & argv[]={"ksamp", "-l", "10", "100"}}.Pueden utilizar la función getopt de la glibc para no volver a inventar la rueda.Pueden encontrar información en man 3 getopt o bien en las Infopagesinvocando pinfo libc y buscado este tema. Este último comando muestra laGNU C Library reference manual .

El ejemplo anterior resulta sencillo en cuanto a que no tenemos que"interpretar" la secuencia de caracteres, sólo la tomamos y la imprimimos. Sinembargo, muchos de los parámetros que se necesitan imprimir requieren decierto tratamiento. Este proceso de transformar una secuencia de caracteres enalguna representación más adecuada para su tratamiento se denominaparsing. Por ejemplo el tiempo transcurrido desde que el sistema inició seexpresa en segundos, y se encuentra en cierta parte del archivo. Entoncestenemos que extraerlo y convertirlo a un entero sin signo. Cuando ya tenemosun entero sin signo, resulta sencillo operar matemáticamente y generar unasecuencia de caracteres (imprimir) con el formato adecuado. Esto último sedenomina pretty printing.

Funciones como atoi y sus relacionadas pueden ser útiles. También resulta útilsscanf.Para obtener la fecha y hora que va en el encabezado del informe que brinda elprograma, se pueden utilizar la funciones de la glibc gettimeofday y ctime, obien leer el tiempo de inicio del sistema y sumarlo al transcurrido para luegoconvertirlo a fecha.Cuando se necesite realizar las muestra del promedio de carga de 1 minuto,puede ser útil la función sleep que duerme un proceso por un intervalodeterminado de tiempo.Qué se debe EntregarCódigo (funcionando bajo las especificaciones dadas y bajo cualquier caso detest de parametros de entrada)Dividir en módulos de manera juiciosa.

Page 18: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 18/55

 

Estilo de código.Usar TADs, por ejemplo no estaría mal un TAD que contenga toda lainformación que se quiere con métodos para leer del kernel e imprimir en unfiledescriptor los datos, o uno que contenga las opciones de la línea decomandos y que acepte un método para leerlas a partir de argc y argv.

Utilizar Makefile.Informe del desarrollo del proyectoCon make deberemos definir solamente una vez las opciones decompilación de cada módulo o programa. El resto de llamadasseránProgramación de Sistemas 16sencillas gracias a su funcionamiento mediante reglas de compilación.Además, make es capaz de llevar un control de los cambios que ha habido enlos ficheros fuente y ejecutables y optimiza el proceso de edicióncompilación-depuración evitando recompilar los módulos o programas queno han sido modificados.1.4.1 Makefile, el guión de make

Los Makefiles son los ficheros de texto que utiliza make para llevar lagestión de la compilación de programas. Se podrían entender como losguiones de la película que quiere hacer make , o la base de datos que informasobre las dependencias entre las diferentes partes de un proyecto.Todos los Makefiles están ordenados en forma de reglas, especificandoqué es lo que hay que hacer para obtener un módulo en concreto. El formatode cada una de esas reglas es el siguiente:objetivo : dependenciascomandosEn “objet iv” defi o nimos el módulo o programa que queremos crear,  después de los dos puntos y en la misma línea podemos definir qué otrosmódulos o programas son necesarios para conseguir el “objet iv”.o Por último,  en la línea siguiente y sucesivas indicamos los comandos necesarios parallevar esto a cabo. Es muy importante que los comandos estén separadospor un tabulador de el comienzo de línea. Algunos editores como el mceditcambian los tabuladores por 8 espacios en blanco, y esto hace que losMakefiles generados así no funcionen. Un ejemplo de regla podría ser elsiguiente: juego : ventana.o motor.o bd.ogcc –O2 –c juego.c –o juego.ogcc –O2 juego.o ventana.o motor.o bd.o –o juego

Para crear “juego” es necesario que se hayan creado “ventana.o”, “motor.o” y  “bd.o” (típicamente habrá una regla para cada uno de esos ficheros objeto en  ese mismo Makefile).En los siguientes apartados analizaremos un poco más a fondo la sintaxisde los Makefiles.1.4.1.1 Comentarios en MakefilesLos ficheros Makefile pueden facilitar su comprensión mediantecomentarios. Todo lo que esté escrito desde el carácter “#” hasta el final dela línea será ignorado por make. Las líneas que comiencen por el carácter “#” serán tomadas a todos los efectos como líneas en blanco.Es bastante recomendable hacer uso de comentarios para dotar de mayor

claridad a nuestros Makefiles. Podemos incluso añadir siempre una cabeceracon la fecha, autor y número de versión del fichero, para llevar un control de

Page 19: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 19/55

 

versiones más eficiente.Programación de Sistemas 171.4.1.2 VariablesEs muy habitual que existan variables en los ficheros Makefile, parafacilitar su portabilidad a diferentes plataformas y entornos. La forma dedefinir una variable es muy sencilla, basta con indicar el nombre de la

variable (típicamente en mayúsculas) y su valor, de la siguiente forma:CC = gcc –O2Cuando queramos acceder al contenido de esa variable, lo haremos así:$(CC) juego.c –o juegoEs necesario tener en cuenta que la expansión de variables puede darlugar a problemas de expansiones recursivas infinitas, por lo que a veces seemplea esta sintaxis:CC := gccCC := $(CC) –O2Empleando “:=” en lugar de “=” evitamos la expansión recursiva y por lo tanto todos los problemas que pudiera acarrear.

Además de las variables definidas en el propio Makefile, es posible haceruso de las variables de entorno, accesibles desde el intérprete de comandos.Esto puede dar pie a formulaciones de este estilo:SRC = $(HOME)/src juego :gcc $(SCR)/*.c –o juegoEmpleando “:=” en lugar de “=” evitamos la expansión recursiva y por lo tanto todos los problemas que pudiera acarrear.Un tipo especial de variables lo constituyen las variables automáticas,aquellas que se evalúan en cada regla. A mí, personalmente, me recuerdan alos parámetros de un script. En la siguiente tabla tenemos una lista de lasmás importantes:Variable Descripción$@ Se sustituye por el nombre del objetivo de la presente regla.$* Se sustituye por la raíz de un nombre de fichero.$< Se sustituye por la primera dependencia de la presente regla.$^Se sustituye por una lista separada por espacios de cada unade las dependencias de la presente regla.$?Se sustituye por una lista separada por espacios de cada una

de las dependencias de la presente regla que sean más nuevasque el objetivo de la regla.$(@D)Se sustituye por la parte correspondiente al subdirectorio de laruta del fichero correspondiente a un objetivo que seencuentre en un subdirectorio.$(@F)Se sustituye por la parte correspondiente al nombre del ficherode la ruta del fichero correspondiente a un objetivo que seencuentre en un subdirectorio.Programación de Sistemas 18Tabla 1.4.2 Lista de las variables automáticas más comunes en Makefiles.

1.4.1.3 Reglas virtualesEs relativamente habitual que además de las reglas normales, los ficheros

Page 20: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 20/55

 

Makefile pueden contener reglas virtuales, que no generen un fichero enconcreto, sino que sirvan para realizar una determinada acción dentro denuestro proyecto software. Normalmente estas reglas suelen tener unobjetivo, pero ninguna dependencia.El ejemplo más típico de este tipo de reglas es la regla “c lean” que  

incluyen casi la totalidad de Makefiles, utilizada para “limpiar” de ficheros  ejecutables y ficheros objeto los directorios que haga falta, con el propósitode rehacer todo la próxima vez que se llame a “make ”:  clean :rm –f juego *.oEsto provocaría que cuando alguien ejecutase “make c lean”, el comando asociado se ejecutase y borrase el fichero “juego” y todos los ficheros objeto.  Sin embargo, como ya hemos dicho, este tipo de reglas no suelen tenerdependencias, por lo que si existiese un fichero que se llamase “clean” dentrodel directorio del Makefile, make consideraría que ese objetivo ya estárealizado, y no ejecutaría los comandos asociados:

txipi@neon:~$ touch cleantxipi@neon:~$ make cleanmake: `clean' está actualizado.Para evitar este extraño efecto, podemos hacer uso de un objetivoespecial de make, .PHONY. Todas las dependencias que incluyamos en esteobjetivo obviarán la presencia de un fichero que coincida con su nombre, yse ejecutarán los comandos correspondientes. Así, si nuestro anteriorMakefile hubiese añadido la siguiente línea:.PHONY : cleanhabría evitado el anterior problema de manera limpia y sencilla.1.4.1.4 Reglas implícitasNo todos los objetivos de un Makefile tienen por qué tener una lista decomandos asociados para poder realizarse. En ocasiones se definen reglasque sólo indican las dependencias necesarias, y es el propio make quiendecide cómo se lograrán cada uno de los objetivos. Veámoslo con un ejemplo: juego : juego.o juego.o : juego.cCon un Makefile como este, make verá que para generar “juego” es preciso generar previamente “juego.o” y para generar “juego.o” no existencomandosProgramación de Sistemas 19que lo puedan realizar, por lo tanto, make presupone que para generar un

fichero objeto basta con compilar su fuente, y para generar el ejecutablefinal, basta con enlazar el fichero objeto. Así pues, implícitamente ejecuta lassiguientes reglas:cc –c juego.c –o juego.occ juego.o –o juegoGenerando el ejecutable, mediante llamadas al compilador estándar.1.4.1.5 Reglas patrónLas reglas implícitas que acabamos de ver, tienen su razón de ser debidoa una serie de “reglas patrón” que implícitamente se especifican en los Makefiles. Nosotros podemos redefinir esas reglas, e incluso inventar reglaspatrón nuevas. He aquí un ejemplo de cómo redefinir la regla implícita

anteriormente comentada:%.o : %.c

Page 21: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 21/55

 

$(CC) $(CFLAGS) $< -o $@Es decir, para todo objetivo que sea un “.o” y que tenga como  dependencia un “. c”, ejecutaremos una llamada al compilador de C ($(CC))  con los modificadores que estén definidos en ese momento ($(CFLAGS)),compilando la primera dependencia de la regla ($<, el fichero “.c”) para 

generar el propio objetivo ($@, el fichero “.o”).  1.4.1.6 Invocando al comando makeCuando nosotros invocamos al comando make desde la línea de comandos,lo primero que se busca es un fichero que se llama “GNUmakefile”, si no se encuentra se busca un fichero llamado “makefile” y si por último no se encontrase, se buscaría el fichero “Makefile”. Si no se encuentra en el directorio actual ninguno de esos tres ficheros, se producirá un error y makeno continuará:txipi@neon:~$ makemake: *** No se especificó ningún objetivo y no se encontró ningún makefile.Alto.

Existen además varias maneras de llamar al comando make con el objetode hacer una traza o debug del Makefile. Las opciones “-d”, “-n”, y “-W” están expresamente indicadas para ello. Otra opción importante es “- jN”, donde indicaremos a make que puede ejecutar hasta “N” procesos en paralelo, muy  útil para máquinas potentes.1.4.1.7 Ejemplo de MakefileLa manera más sencilla de entender cómo funciona make es con unMakefile de ejemplo:# Makefile de ejemplo## version 0.1Programación de Sistemas 20#CC := gccCFLAGS := -O2MODULOS = ventana.o gestion.o bd.o juego.PHONY : clean installall : $(MODULOS)%.o : %.c$(CC) $(CFLAGS) –c $<.c –o [email protected] : ventana.cbd.o : bd.c

gestion.o : gestion.c ventana.o bd.o$(CC) $(CFLAGS) –c $<.c –o $@$(CC) $* -o $@ juego: juego.c ventana.o bd.o gestion.o$(CC) $(CFLAGS) –c $<.c –o $@$(CC) $* -o $@clean:rm –f $(MODULOS)install:cp juego /usr/games/juegoTips

Page 22: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 22/55

 

No intenten hacer todo de golpe, vayan de a partes, y sobre todo discutan,analicen y trabajen en ideas de forma grupal. Una hora pensando en papellibremente suele ahorrar muchos problemas en el momento de codificar.Utilicen debuggers de línea de comandos como gdb o interfaces gráficas paraestos como ddd . También pueden realizar compilación condicional para hacer

debugging de la siguente manera.

#define STDOUT 0#define DEBUG..#ifdef DEBUGfprintf(STDOUT,"DEBUG> argc:%d\n",argc);#endif..

PRACTICA5 Lab2: Un BaashObjetivosUtilizar los mecanismos de concurrencia y comunicación de gruesagranularidad que brinda UNIX.Comprender como los interpretes de línea de comando reflejan la arquitectura yestructura interna de éstas primitivas de comunicación y concurrencia.Implementar de manera sencilla un intérprete de línea de comandos (shell) al

estilo de Bourne shell.IntroducciónLa interfaz más tradicional de un sistema operativo UNIX-like (*NIX) es elintérprete de línea de comandos. Este programa, que ejecuta en modo usuario,funciona en cualquier *NIX que soporte interface de caracteres y su función esaceptar comandos ingresados por entrada estandar (teclado), parsearlos,ejecutar la órden y mostrar el resultado en salida estandar (pantalla), paraluego volver a repetir el proceso.Por defecto UNIX ejecuta un proceso shell cada vez que un usuario interactivoingresa al sistema. Aunque esto puede ser configurado de otra manera (ver elúltimo campo de cada línea del archivo /etc/passwd), en la mayoría de los

casos luego de ingresar nuestro nombre de usuario y contraseña, el procesoque maneja el ingreso de usuarios genera un proceso hijo que ejecuta un shell,con el uid/gid (identificador de usuario y grupo) correspondiente al usuario. Eneste momento la pantalla se suele presentar de la siguiente manera:

[juan@hal juan]$Después de este texto inicial llamado prompt, que contiene información deentorno como por ejemplo el nombre del usuario, el nombre del host y el últimotramo del directorio corriente, el shell espera datos a través de la stdin quenormalmente se asocia al dispositivo teclado. Podemos escribir el comando

que deseamos que el shell ejecute, e iniciar la ejecución ingresando el caracter

Page 23: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 23/55

 

NEWLINE '\n' generalmente asociado con la tecla Enter o Return.

[juan@hal juan]$ sleep 10Hará que el shell ejecute un proceso con el programa binario que se encuentraen /bin/sleep, pasándole el argumento "10".Operación BásicaLa sintáxis básica del intérprete de comandos más usual de *NIX, conocidocomo Bourne shell (Bourne again shell - bash, en Linux) es la siguiente,

comando argumento1 argumento2 ...donde el comando y los argumentos son secuencias de caracteres separadospor uno o más espacios.La semántica dice que al presionar Enter el shell buscará el comando dentro delos comandos internos y si no lo encuentra tratará de buscar un archivoejecutable con ese nombre, siguiendo las reglas de camino de *NIX ocomponiendo el comando a la secuencia de la variable de entorno PATH, paraluego crear un proceso hijo que cargará y ejecutará el contenido de ese archivocon los argumentos correspondientes.Los comandos internos son manejados directamente por el shell, sin requerirde ningún archivo externo. Un ejemplo es el comando de cambio de directoriocd, el cual no se encuentra como archivo ejecutable en ningún lugar del árbolde directorio (el comando find /bin /sbin /usr/bin /usr/sbin -perm +400 -type f -name cd no devuelve nada).Con man builtin obtenemos una lista de todos los comandos internosimplementados en bash.Si el comando no es un builtin, el shell deberá buscar un archivo dentro de susistema de archivos, cargarlo y ejecutarlo pasándole los argumentos. El

problema principal es dónde buscar este archivo. Existen tres formas:Camino absolutoCamino relativoBúsqueda en secuencia PATHCuando el comando comienza con /, este se toma como un camino absolutodentro del árbol del filesystem, el shell cargará en memoria y ejecutará elcomando. En cambio si el comando comienza con el nombre de un directorio, .o .., se debe seguir las reglas usuales de camino relativo de *NIX, cargar yejecutar el archivo comando, relativo al camino actual (ver comando pwd).Otro mecanismo entra en juego cuando el comando no comienza con undelimitador de camino absoluto o relativo. La variable de entorno PATH, que

puede ser leida con el comando env o con echo $PATH, sirve de secuencia decaminos absolutos o relativos, separados por ':' que serán prefijados acommando hasta encontrar un archivo que pueda ser leído y ejecutado.Usemos el archivo ejecutable /bin/date que nos proporciona la fecha y hora delsistema para ejemplificar los mecanismos de camino absoluto, relativo ysecuencia PATH.

[juan@hal juan]$ /bin/dateSun Aug 31 15:33:22 ART 2003[juan@hal juan]$ cd /usr[juan@hal /usr]$ ../bin/dateSun Aug 31 15:33:57 ART 2003[juan@hal /usr]$ cd / 

Page 24: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 24/55

 

[juan@hal /]$ bin/dateSun Aug 31 15:34:24 ART 2003[juan@hal /]$ cd ~[juan@hal juan]$ dateSun Aug 31 15:35:02 ART 2003

Todos los comandos ejecutados por bash son el mismo /bin/date.Ejercicios¿Cómo ejecutamos un archivo que se llama exactamente como un builtin?Tenemos que instalar build-essential, ya que esta es una lista informativa depaquetes esenciales para poder compilar (voy a poner todo lo necesarioteniendo en cuenta a los usuarios muy novatos que quieran programar), deesta manera:Esto lo escribes en terminal, presionas ALT + F2 y en el cuadro que te apareceescribesgnome-terminal y presionas Entersudo apt-get install build-essentialTe pedirá una contraseña, que es la que utilizas para "entrar" a Ubuntu(Ojo, si tienes el CD de Ubuntu, mételo en la unidad de CD o DVD, el quetengas y te dirá si quieres abrir el gestor de paquetes, le das clic en esta opcióny se abrirá synaptic, entonces buscas build-essential y lo marcas para intalar,clic con el botón derecho y marcar para instalar, eso si, desconecta por estemomento la conexión a Internet, para que descargue los paquetes desde el CDy no de Internet, das clic en el botón aplicar, que se encuentra en la barra deherramientas, esperas un rato y listo)Si todo marcho a la perfección, ya estamos listos para programar

LENGUAJE C EN UBUNTU

1.- Abres terminal (como te explique mas arriba) y haces lo siguiente (parainiciar, solo usaremos consola, aunque existen otras alternativas gráficas comoAnjuta, Code::Blocks, Eclipse, etc...):usuario@equipo:~$ pico ejemplo.c(usuario y equipo dependen de tu equipo, los pongo para referencia)(En este ejemplo uso el editor pico, y el archivo que voy a crear es el ejemplo.c)Se va a "limpiar" la terminal y ahí puedes poner el código para tu programa, eneste caso voy a hacer el famoso "Hola mundo"#include <stdio.h>int main()

{printf("Hola mundo");printf("\n");return 0;}Una vez terminado, oprimes CONTROL + O (Es O, no un cero)para indicar quelo vamos a guardar, la terminal te va a decir si realmente quieres guardarlo coneste nombre (en este caso ejemplo.c) y para indicar que si, basta con apretarEnter, ahora a salir de este editor con un CONTROL + X y regresas a terminal,ahora para compilarlo, tienes que escribir en terminal:gcc ejemplo.c -o ejemplo

Page 25: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 25/55

 

Con esto le indicamos que vamos a compilar el archivo ejemplo.c con elcompilador gcc, que es el utilizado en Linux para C, y que a la salida (-o, ojo,no es un cero, es una o) de el ejecutable ejemplo.Ahora bien, a ejecutar nuestro programa con un:./ejemplo

Desde terminal y obtenemos esto:usuario@equipo:~$ ./ejemploHola mundoTe pongo este otro que suma 2 números:En terminal escribimos:pico suma.cY en el editor ponemos este código:#include <stdio.h>int x,y,z;int main(){printf("Dame el primer numero: ");

scanf("%d",&x);printf("\n");printf("Dame el segundo numero: ");scanf("%d",&y);z=x+y;printf("\n\n El resultado de la suma es :%d\n",z);return 0;}Lo guardamos con CONTROL + O, presionamos Enter y salimos conCONTROL + X.Lo compilamos y creamos el ejecutable:gcc suma.c -o sumaAhora lo ejecutamos:./sumaY este es el resultado (voy a sumar 88 mas 77) :usuario@equipo:~$ ./sumaDame el primer numero: 88Dame el segundo numero: 77El resultado de la suma es: 165Todo esto lo hemos hecho desde la carpeta personal, por lo tanto los archivosse encuentran ahí, tanto el código fuente (los archivos con extensión .c) como

los ejecutables (los archivos que llevan el mismo nombre que los del códigofuente pero sin extensión)

¿Por qué las recomendaciones de seguridad indican que es peligroso tener ./ en PATH al más puro estilo DOS?Listado de Código 1.7: Correr el programa de arriba$ ./myenvThe editor environment variable is set to (null)Hmmm... Como la variable de entorno EDITOR no poseía ningún valor, elprograma en C recibe una cadena nula. Intentemos nuevamente dándole unvalor específico:

Listado de Código 1.8: Probar con un valor específico$ EDITOR=xemacs

Page 26: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 26/55

 

$ ./myenvThe editor environment variable is set to (null)Aunque pudiste haber esperado que myenv imprima el valor "xemacs", nofuncionó, porque no exportamos la variable de entorno EDITOR. Esta vez loharemos funcionar:

Listado de Código 1.9: Mismo programa luego de exportar la variable$ export EDITOR$ ./myenvThe editor environment variable is set to xemacsAsí, has visto con tus propios ojos que otro proceso (en este caso nuestroprograma en C) no puede ver la variable de entorno hasta que esta esexportada. Incidentalmente, si tú quieres, puedes definir y exportar una variablede entorno usando una sola línea.Listado de Código 1.11: Borrar una variable de entorno

 

$ unset EDITOR$ ./myenv

 

The editor environment variable is set to (null)tiene que listar el directorio actual y salir.Supongamos que existen 2 comandos posibles dentro de la secuencia quecontien PATH, donde el primero en la secuencia no está marcado comoejecutable y el segundo si. ¿Qué hace el intérprete bash, ejecuta el segundo oinforma que el primero no tiene permiso de ejecución? (incorporte estasemántica a baash)EL COMANDO PATHEl comando PATH permite indicar al MS-DOS donde ha de buscar un archivode comandos si este no se encuentra en el directorio activo. Puede indicar unoo mas directorios, el directorio raíz o cualquier otro subdirectorio y cualquierunidad de disco.El comando PATH tiene tres parámetros:PATH <UNIDAD> <RUTA>Ejemplo :A:\>PATH C:\DOS; C: ;C:\WINDOWSVISUALIZAR LA ESTRUCTURA DE DIRECTORIOSEl comando TREE permite ver la estructura del directorio desde el prompt delsistema.El comando TREE posee dos parámetros principales:TREE <UNIDAD> /F

 /F: Presenta una lista de archivos en cada directorioEL COMANDO APPEND(OTRO TIPO DE RUTA)El comando PATH configura la ruta de los archivosejecutables; APPEND configura además la ruta de los archivos de datosEl comando APPEND tiene los siguientes parámetros:Ejemplo:A:\>TYPE INFORME1.DOCEl sistema responde archivo no se encontróA:\>APPEND \MARKET\PTA:\>TYPE INFORME1.DOC

También puede usar punto y comaCOMO COPIAR DIRECTORIOS

Page 27: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 27/55

 

Para copiar un directorio y sus subdirectorios, se puede usar el comandoXCOPY, ambos comando copian archivos de un directorio o disco a otro. Elcomando XCOPY es similar al comando COPY, pero XCOPY trabaja con undirectorio o grupo de directorios.Ejemplo:

A:\>XCOPY \MARKET\PREPTO C:A:\>XCOPY A: C:A:\>XCOPY A: C:\DISCO /S/ECopia toda la estructura del directorio del disco al disco C:, creando la carpetaDISCO.El interprete de comandos:Un interprete de comandos (o shell) es un programa que recoge lo que elusuario ha introducido y lo traduce a instrucciones, en el MS-DOS el interpretede comandos es el COMMAND.COM e incluso el mismo Windows. En Linuxexisten muchas shell como bash, ssh, o el sistema X-Window.El interprete arranca nada mas terminar de arrancar el sistema.

Por ejemplo, veamos un inicio de sesión:localhost login: pedviPassword: Welcome to localhost!

pedvi@localhost:~$pedvi@localhost:~$ es el prompt del interprete que indica que esta listo pararecibir ordenes, a partir de ahora lo abreviaremos usando solamente "$" paraun usuario normal y "#" para el root.

Cuando el interprete de comandos recibe un comando primero analiza laexpresión y luego entrega la orden al comando .Por ejemplo:$ cp hola /home/pedviEn este caso no tendría que expandir ni modificar nada, simplementeentregaría "hola" y "/home/pedvi" al comando "cp" como argumentos (másadelante veremos para que sirve este comando).$ cp ho* ~Ahora si tendría que cambiar unas cosas: El "ho*" lo sustituiría por cualquierarchivo del directorio cuyo nombre empezara por "ho" y "~" lo sustituiría por eldirectorio del usuario (en este caso /home/pedvi, en el caso del usuario2 /home/usuario2 y en el caso del root /root).

En el caso de que solo hubiera un archivo que empiece por "ho", las dosordenes hacen lo mismo.

Pero, ¿como sabe el interprete donde esta el comando "cp"?Muy fácil: en la mayoría de sistemas operativos existe una variable quecontiene la dirección donde están los comandos.En Linux esta variable se llama "PATH", para ver lo que contiene una variableusaremos en comando "echo" que sirve para mostrar los argumentos que se leden.Para mostrar el contenido de una variable con "echo" hay que anteponer "$" alnombre de la variable:

$ echo $PATH

Page 28: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 28/55

 

 /usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games

El interprete buscara el comando en estos directorios en el orden en el queestán en PATH.Para cambiar el contenido de una variable haremos:

$ variable=valor$ export variableEn el caso de PATH haríamos lo siguiente para añadir a su valor el directorio /home/pedvi:$ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games$ PATH=/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/home/pedvi$ export PATHEs importante que copiemos el valor de PATH antes de cambiarla para que noperdamos ningún directorio.

Indique qué sucede cuando se tiene un directorio en el pwd actual con elmismo nombre que un archivo ejecutable en el PATH. ¿Dice que no puedeejecutar un directorio o ejecuta el comando que encontró? (siga esta forma enbaash)Al ingresar al sistema cada usuario entra en su directorio propio, un directorioprivado que no es tocado por el sistema ni por los otros usuarios. El directorioen el cual se encuentra posicionado el usuario en un momento dado sedenomina su directorio actual.cd /usr/binpwd

cambia al directorio /usr/bin; el directorio actual es /usr/bin.cdpwd

devuelve al usuario a su directorio propio, que es ahora el directorio actual.echo $HOME

nuestra el nombre del directorio propio. HOME es una variable de ambienteque contiene el nombre del directorio propio del usuario.cd $HOME

tiene el mismo efecto que cd.Nombres de directorios.Un directorio puede contener otros directorios así como archivos ordinarios, lo

que genera una jerarquía o árbol de directorios. El directorio superior odirectorio raíz se denomina /.cd / pwd

el directorio actual es el directorio raíz.cd

vuelve al directorio propio del usuario.

El caracter / se usa también para separar los componentes de un nombre dearchivo completo: /export/home/usuario1/nota.La porción /export/home/usuario es la ruta, nota es el nombre del archivo. Si se

omite la ruta, se asume que el archivo se encuentra en el directorio actual. Unnombre tal como

Page 29: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 29/55

 

textos/libro/capitulo.1 indica que su ruta comienza a partir del directorio actual.Sería lo mismo escribir ./textos/libro/capitulo.1 ya que el punto designa eldirectorio actual. Dos puntos seguidos designan el directorio de nivel superior alactual. Si el directorio actual es /home/usuario1ls ../usuario2

muestra el contenido de los archivos en /home/usuario2

El caracter / separa directorios incluídos como parte de un nombre de archivo odirectorio.ls dir1

es un direccionamiento relativo;ls /home/esteban/dir1

es un direccionamiento absoluto.

Pueden usarse comodines para referenciar directorios y archivos:cat /home/esteban/*/*

Obtenga la lógica más sencilla que unifique los tres tipos de búsqueda.búsquedas informacionales, de navegación o transaccionales.

La búsqueda informacional implica la necesidad de encontrar datos sobre unhecho o tema concreto, la de navegación persigue hallar un sitio de internetespecófico, y la transaccional realizar algún tipo de compra o transacción.

"Nuestros resultados tienen grandes implicaciones para los buscadores y elcomercio electrónico", afirman, pues "podrían clasificar las intenciones delusuario en tiempo real".• Los robots que recorren la red para escrutarla. Son programas que buscancontinuamente por todos los servidores de Internet construyendo un índice delo hallado. También son conocidos como «arañas» por su continuodesplazamiento sobre la red o telaraña.• La base de datos que es construida por los robots. Contiene todos losdirecciones electrónico o «URL» encontrados, y asociados a ellos, lainformación relativa sobre sus contenidos:- Título.- Parte de texto.- Hiperenlaces.

- Descriptores o palabras claves.Esta base de datos es actualizada continuamente por los robots que añadennuevas páginas o referencias y borran las que ya no existen.• El motor de búsqueda que facilita la consulta a la base de datos. Es la parteque se ve cuando se realiza la búsqueda. Después de introducirle una peticiónde búsqueda, el motor de búsqueda la coteja con la base y devuelve una listaordenada de coincidencias¿Podemos poner directorios relativos en PATH? (haga lo propio con baash) As´ı como PATH tambi´en existen muchas otras variables. Algunasdeterminadas por el sistema, yotras determinadas por nosotros.

Llamamos entorno al conjunto de variables, como el PATH, el nombre deusuario, el directorio

Page 30: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 30/55

 

actual, el directorio principal, las preferencias de lenguaje, etc. que determinana la consola que estamos utilizando en este momento. Podemos ver cuales sonlas variables de nuestro entorno escribiendo:set.Estas variables de entorno, nosotros podemos agregar nuevas variables. Para

ello podemos escribir: variable=valor. Y haciendo echo $variable podremos verel valor que le asignamos a lavariable.Al ejecutar un programa, este programa recibe una copia de nuestro entorno,donde algunasvariables pueden mantenerse (variables exportadas), y otras pueden no estar.Un programa puedemodificar las variables que tiene en su entorno, pero no las del entorno anterior.A su vez, dentro deese programa podemos ejecutar un nuevo programa, que tendr´a su propioentorno, con sus propias

variables.Para hacer que los programas hereden las variables que nosotros definamos,existe un comandollamado export, que nos asegura que los programas que se ejecuten, recibanesa variable en su entorno.Procesos en Segundo PlanoEn el paradigma básico para ejecutar un comando, el shell es el proceso padreque crea a un hijo que cargará y ejecutará el comando que se encuentra en elfilesystem. Mientras tanto el padre quedará esperando hasta que el procesohijo termine.La semántica puede ser cambiada para que el padre no espere la terminacióndel hijo, si le agregamos al final el símbolo '&'.Este operador, que puede ser pensado como operador de composiciónparalela, crea la posibilidad de ejecutar procesos de manera concurrente, esdecir, si el comando que ejecutamos termina con & y demora un tiemporelativamente largo, todo lo que iniciemos desde el shell y el shell mismoestarán ejecutándose al mismo tiempo (paralelamente, concurrentemente).Esta característica resulta muy útil cuando nuestro kernel esta corriendo sobreun motherboard SMP (symmetric multiprocessing), que soporta variosmicroprocesadores compartiendo una única imagen de memoria principal. Sitenemos un par de programas que tardan mucho en calcular decimales de pi o

de e, podemos ponerlos a correr de manera que cada uno ocupe unmicroprocesador, mientras utilizamos un poco de los dos micros para jugar.

[juan@deepblue calculosLargos]$ ./decimalesPi salidaPi.txt&[juan@deepblue calculosLargos]$ ./decimalesE salidaE.txt

 

&[juan@deepblue calculosLargos]$ freecivExiste un problema con los procesos hijos en background, la entrada y salidaestandar se comparten entre todos, con lo cual tendremos salidas de pantallaentrelazadas y las entradas por teclado serán no-deterministicamenteasignadas a alguno de los procesos que están corriendo.

Page 31: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 31/55

 

Este ejemplo nos muestra el lio que se puede armar.

[juan@hal juan]$ yes "HOLA" & yes "CHAU" &... ud. acaba de perder el control de esa shell ...EjerciciosInvestigue cuales son los comandos internos para manejo de procesos enbackground de bashEl comando, para dejarnos ejecutar otros, esto se denomina ejecución deprocesos en background y lo indicamos finalizando el comando con un '&'.$ comando-en-background &[1] 363$Veamos un ejemplo donde se note que el proceso se ejecuta en background:$ sleep 5$Se ve que el comando sleep 5, produce un retardo de 5 segundos, en loscuales no podemos iniciar otros comandos, este proceso se ejecutoen foreground, ahora realicemos el mismo ejemplo ejecutándolo enbackground:$ sleep 5 &[1] 316$[1]+ Done sleep 5Luego de ejecutar el comando sleep, el interprete nos da inmediatamente suindicador de listo ($), podemos ejecutar otros sin que haya finalizado el sleep.Vimos que un comando simple se puede ejecutar en foreground o en

background (agregándole un '&' al final), no se permite la ejecución de mas deun proceso en foreground por terminal, mientras que en background puedenejecutarse varios a la vez.Existen procesos que no dejan ejecutarse en background, porque necesitanutilizar constantemente la terminal, un ejemplo es el editor vi:$ vi &[1]+ Stopped viSe ve que al tratar de ejecutarlo en background se detiene (Stopped) y no seejecuta :))

En el ejemplo de arriba el operador '&' funciona como operador de composición

paralela. ¿Cuál es el operador de composición secuencial en Bourne shell?bash - El mas utilizado y el que trataremos en este texto, es la versión GNU delBourne Shell.ksh - Es la versión GNU del Korn Shell.tcsh - Version GNU y compatible con el C Shell de Berkeley5- Si la contraseña es incorrecta login devuelve el control de la terminal al getty.Estos 3 interpretes tienen diferencias en su funcionamiento, a partir de ahoracuando nos referimos a "interprete de ordenes" será exclusivamente a bash.

Investigue como bash forma el árbol de procesos cuando ejecutamos cmd1 &cmd2 & cmd3 & ... & cmdN. Piense la respuesta y luego utilice pstree paracorroborarla. Aplique los resultados de su observación a baash.Indique cuantas letras 'a' debería imprimir el siguiente programa. Generalice.

Page 32: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 32/55

 

fork();fork();fork();printf("a");3 letras… 

Redirección de la Entrada/SalidaCuando se crea un proceso, se le asignan tres identificadores de archivos (filedescriptors): stdin, stdout y stderr. Si se lee desde stdin, los datos que sereciben serán dirigidos desde el teclado al file descriptor stdin. Similarmente,los datos escritos en stdout, stderr serán mapeados a la pantalla de la terminal.El usuario puede redefinir el stdin desde la línea de comandos del shell si seprovee al final del comando un embudo de entrada '<' y un nombre de archivo.Este archivo reemplazará la corriente estandar de entrada, por lo que losvalores que lea el comando serán extraidos desde el archivo.Similarmente podemos redefinir el file descriptor de stdout con el embudo desalida '>' y un nombre de archivo al final del comando y los argumentos, paraindicar que toda la salida estandar del comando se acumule en el archivo.Como ejemplo podemos recolectar estadísticas sobre un header del códigofuente del kernel y ponerlo en un archivo de estadísticas.

[juan@hal juan]$ wc < kernel.h > kernel.h.statsO si nuestros programas cpu-bound generan sus resultados en pantallapodemos ejecutarlos concurrentemente y mantener sus salidas separadas,mientras las monitoreamos con el comando tail.

[juan@deepblue juan]$ forest3d -n 0.2 > n0.2.out &

[juan@deepblue juan]$ forest3d -n 0.3 > n0.3.out &[juan@deepblue juan]$ tail -f n0.2.out n0.3.outEjerciciosLas corrientes estandar stdout y stderr están dirigidas ambas a la consola.¿Cómo podemos utilizar los opeardores de redirección para separarlas?De más ejemplos de como usar la redireccion de entrada? Tenga cuidado quemuchos programas deshabilitan la posibilidad de redirigir la entrada estandar aotra cosa que no sea una terminal.¿Podemos redirigir de manera independiente la entrada y la salida de unasecuencia de comandos separados por &? ¿Qué sucede con la composición

sequencial ;? (piense en este comportamiento en caso de que realice lospuntos complementarios).Tuberías (o ... la plomería continua)Las tuberías o pipes son el principal mecanismo de Comunicación entreProcesos (IPC) para *NIX. Una tubería se compone de dos puntas, una entraday una salida, cada una conectada a un proceso. Las puntas de una tubería sonun par de filedescriptors, uno para escribir y el otro para leer. Un pipe esbásicamente un buffer FIFO, donde tenemos un productor, sobre el write-end yun consumidor sobre el read-end. Como sabemos que todas las colas que seencuentran en el sistema operativo son acotadas (memoria finita), además detener un read bloqueante cuando no hay datos para consumir, tenemos un

send bloqueante cuando la capacidad del buffer está excedida.

Page 33: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 33/55

 

El shell provee la funcionalidad de un pipe a través del operador '|',denominado justamente pipe, el cual conecta la salida estandar del procesolanzado por el comando de la izquierda del pipe, con la entrada estandar delproceso que se genera con el comando a la derecha del símbolo de tubería.Las tuberías se pueden componer secuencialmente para generar una linea de

producción de datos, el cual es un estilo arquitectural denominado pipes&filters.Estos ejemplos cuentan cuantas veces Juan ingresó al sistema, los pids(process identifiers) de todos los morzillas, y el listado ordenado de mayor amenor de todos los usuarios del sistema que tienen bash,

[juan@hal juan]$ last juan | wc -l[juan@hal juan]$ ps aux | grep mozilla-bin[juan@hal juan]$ grep bash /etc/passwd | cut -d ":" -f 1 |sort -rmientras que el siguiente es un poco más complicado y permite reestablecerinteractivamente un dump de una partición ext2, que ha sido partido parasortear la limitación de 2GB de tamaño del sistema de archivos ext2.

[juan@hal juan]$ cat backupL0.gza? | gzip -dc | /sbin/restore -i -f -Ejercicios¿Cúal es el mecanismo para que este estilo pipes&filters, hasta ahoraabsolutamente lineal, permita bifurcaciones? Si tuvieramos bifurcaciones (o"T's" para un plomero), podríamos capturar la salida estandar en un archivo y almismo tiempo visualizarla en consola.Los pipes existen en el filesystem mientras dura el comando. ¿Dónde seencuentran y que atributos tiene?

¿Cómo es el pipe de comandos para generar el dump de nivel 0 que sereestablece interactivamente con el ejemplo anterior?Muestre el uid máximo en su sistema NIS (ypcat passwd realiza el trabajobásico).Muestre todos los login repetidos de su sistema NIS.TareasEl problema se divide en 3 partes, de manera que su concreción se realice enforma progresiva. Es una guía de como llegar al resultado final, nuestro baash,sin tener que sortear todos los problemas de diseño y técnicos de una sola vez.Parte AEscriba un programa en "C" que actúe com una concha de intérprete de líneade comandos para el sistema operativo Linux. Su programa deberá utilizar lamisma sintaxis de Bourne shell para correr programas.comando argumento1 argumento2 ...Efectuando la búsqueda del comando según se presente como path absoluto,relativo o involucre una búsqueda en la secuencia de la variable de entornoPATH.El programa deberá generar un proceso hijo para correr el comando, a fin deprotegerse de comportamientos malos, y pasarle los argumentoscorrespondientes.Es preferible imprimir un prompt con información relevante a la izquierda de

donde se introduce el comando. El nombre del host, el nombre del usuario y elcamino corriente pueden ser algunos ejemplos.El único comando interno que se implementa es exit, el que termina con la

Page 34: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 34/55

 

ejecución del intérprete de comandos. Nota 18 Sept: También hace faltaimplementar el comando interno cd (cambiar de directorio actual). Fijense lasyscall chdir().Luego de implementar esta parte su baash deberá ser capaz de invocar desdesimples peticiones como date,que involucran búsquedas en PATH, hasta

llamadas a complicados comandos de compilación o linking utilizando pathrelativos y decenas de opciones.Parte BAgregue al shell de la parte anterior, la funcionalidad de correr programas comoprocesos en background, o mejor dicho concurrentemente con el mismo baash.El usuario incluirá el operador '&' a la derecha del último argumento paraactivar esta capacidad.Pruebe la nueva versión de la siguiente manera

baash-0.01> ./ksamp -l 3 120 &baash-0.01> ./ksamp -l 1 120 &Debería obtener un entrelazado no determinístico (efectúe varias corridas) dela información generada por el programa del laboratorio 1.Parte CIncorpore las funcionalidades de redirección y tuberías a baash, de manera talque se interpreten los opeardores < filename, > filename y | command2argumento2_1 argumento2_2 ... al final de los argumentos del comando.Deberá soportar sólo uno de los cuatro operadores al mismo tiempo.Utilize todo el poder de baash con los ejemplos dados en la introducción querespeten la restricción de "a lo más un operador".Cómo atacar los problemas

La principal fuente de información son los capítulos V, VI de:D. M. Ritchie and K. Thompson, "The UNIX Time-Sharing System" [html ] [ps ][pdf ]Este documento indica de manera extremadamente clara como se logró laimplementación del shell.Parte ASe puede ir paso a paso, generando programas de prueba que realicen tareascada vez más complejas y cercanas al enunciado de la parte A. Si usted sesiente confiado respecto a poder solucionar el problema sin hacer desarrolloincremental, puede saltar estos pasos.Escriba un programa que inicialice las variables e ingrese en un lazo que pida

entrada por teclado y que termine sólo cuando se ingresa una condición deEOF (end of file) como Ctrl-D o el comando interno exit.Refine el punto anterior de manera tal que haga parsing de la línea decomandos y lo ponga en el array char *argv[]. También compute argc. Amanera de debug imprima, luego del parsing, el contenido de argc y argv.En el siguiente refinamiento, use argv[0] para encontrar el archivo ejecutable.En esta versión sólo imprima el fullpath del archivo.Haga una versión simple que solamente pueda encontrar archivos de comandoque están en el directorio corriente.Mejore su programa para que acepte path absolutos y relativos.Active en su prototipo la posibilidad de buscar en los directorios de la listaPATH.Ahora si cree el hijo y ejecute el comando con sus argumentos.

Page 35: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 35/55

 

Esta parte debería ser un reflejo exacto del poder de las llamadas a sistemapara manejo de procesos fork, execv, y wait. Todo lo que resta es el parsing dela línea de comandos y la impresión del prompt.Veamos un ejemplo de uso del fork y wait para crear toda una parentela deprocesos. Notar el interleaving que se produce en las ejecuciones.

fork.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>#define N 8intmain (int argc, char **argv){

unsigned int i,j,h,s;int status;

if (argc!=2) {fprintf(stderr,"Uso: %s

 

cantidad_de_hijos\n",argv[0]);exit(1);

}h = atoi(argv[1]);for (i=0;i<h;i++) {

s = fork();if (s<0) {

perror("Creando el hijo");exit(1);}

else if (s==0) {for(j=0;j<N;j++)

 

printf("Hijo pid=%5d: iteración%3d\n",getpid(),j);

 

return 0; /*no quiero nietos!*/ }

elseprintf("PADRE: hijo pid=%5d, creado\n",s);

}for (i=0;i<h;i++) {s = wait(&status);

if (s<0) {perror("Esperando el hijo");

exit(1);}

printf("PADRE: hijo pid=%5d, terminado\n",s);

 

}return 0;

}

Page 36: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 36/55

 

El otro syscall a utilizar es execv, el cual reemplaza la imagen del proceso poruna nueva que se encuentra en un archivo. En este pequeño ejemplo, sereemplaza el proceso actual por el que ejecuta el programa date.

execvp.c

#include <stdio.h>#include <unistd.h>intmain (int argc, char **argv)

 

{execvp("date",argv);

printf("Llega hasta aca?\n");return 0;

}Aunque el syscall execvp, parece que le soluciona todos los problemasrelativos a la búsqueda el fullpath del ejecutable, no se permite su utilización ydeberán codificar o reutilizar algoritmos y estructuras de datos apropiadas.Parte BEl proceso que ejecuta el programa baash deberá correr concurrentemente conel proceso hijo creado para ejecutar el comando, si es que hay un operador '&'a la derecha de todos los argumentos. Simplemente no hay que esperar!Parte CCuando se crea un proceso, este hereda de los descriptores de archivosabiertos de su padre. Además como el proceso 1, denominado init y padre delos restantes procesos del sistema (ps aux | grep init, o bien pstree paraobtener el árbol genealógico), tiene abierto los tres primeros identificadores de

archivos stdin, stdout y stderr (0,1 y 2) a descriptores de archivos asociados ala terminal de entrada y a la terminal de salida; todos los hijos heredan estosfileid's asociados a los filedescriptors.Para implementar los operadores de redirección bastaría con cambiar losfiledescriptors asociados a los fileid's de stdin, stdout y stderr. Esto se puedehacer con la syscall dup, que duplica el filedescriptor indicado en su argumento,en el menor fileid libre. Veamos un ejemplo que redirige la salida a un archivo.Notar que las syscall para manejo de archivos que no son las habituales de lalibc (fopen, fclose, ...).

dup.c

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>

 

#include <sys/stat.h>#include <fcntl.h>#define STDOUT_FID 1intmain (int argc, char **argv){

int fid;int flags,perm;

flags = O_WRONLY|O_CREAT|O_TRUNC;

Page 37: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 37/55

 

perm = S_IWUSR|S_IRUSR;fid = open("redireccion.out", flags, perm);

if (fid<0) { perror("open");exit(1); }close(STDOUT_FID); /* stdout def. en stdio.h es un

FILE* */ 

if (dup(fid)<0) { perror("dup"); exit(1); }printf("Hello World!\n");

 

close(fid);return 0;

}Para implementar el operador de tubería '|', debemos utilizar la syscall pipe, lacual crea en el espacio del filesystem un nuevo nodo de este tipo y devuelvedos fileid's que serán la salida y la entrada de la tubería.Veamos un sencillo ejemplo de IPC.

pipe.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>intmain (int argc, char **argv){

int fork_id;int srv_client_pipe[2];

int client_srv_pipe[2];

if (pipe(srv_client_pipe)<0) {

 

perror("srv_client_pipe");exit(1); }if (pipe(client_srv_pipe)<0) {

 

perror("client_srv_pipe");exit(1); }if ((fork_id = fork())<0) { perror("fork");exit(1); };

if (fork_id!=0) { /* soy padre, soy cliente */ int status;

int x,fx;x = 8;

printf("CLIENTE: envia %d\n",x);write(client_srv_pipe[1], &x, sizeof(int));

read(srv_client_pipe[0], &fx, sizeof(int));printf("CLIENTE: recibe %d\n",fx);

 

wait(&status);close(srv_client_pipe[0]);

 

close(srv_client_pipe[1]);close(client_srv_pipe[0]);close(client_srv_pipe[1]);

}else { /* soy hijo, soy server */ 

int in,out;read(client_srv_pipe[0], &in, sizeof(int));

printf("SERVIDOR: recibe %d\n",in);out = in*in;

Page 38: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 38/55

 

printf("SERVIDOR: envia %d\n",out);write(srv_client_pipe[1], &out, sizeof(int));

}return 0;

}

Notar que este programa aunque concurrente, presenta sólo un interleaving ensu salida. Se dice que el programa está fuertemente sincronizado debido a lasprimitivas de comunicación.TipsEl problema de la búsqueda en la secuencia PATH es un simple problema debúsqueda de secuencia, que con una buen TAD secuencia implementadosobre listas se debería resolver de manera compacta.Planteen el problema de parseo de forma general, ya saben todas lasposibilidades que hay para parsear, asi que no debería ser difícil realizar unafuncion parse_command que devuelva una estructura command que contegatoda la información necesaria para ejecutar el o los comandos, y aplicar losoperadores.Si es lo suficientemente general, los puntos adicionales saldrán sin esfuerzoadicional.El shell refleja tan bien la estructura interna de los syscalls de *NIX paracreación de procesos, manejo de corrientes e IPC, como se refleja en "TheUNIX Time-Sharing System " en su sección VI, que el programa principaldebería ser muy compacto. Si no logra esto reorganice sus ideas intentandoser lo más económico posible.Se recomienda fuertemente leer este artículo. Estudiantes del 2003 y 2004realizaron una traducción al castellano de las secciones V y VI del paper "The

UNIX Time-Sharing System".El libro "Advanced Linux Programming ", muestra le uso del assert en la página30, y como manejar correctamente los errores que pueden devolver lasllamadas a sistema en la página 33. Estudien este material antes de comenzarla codificación. Los ejemplos de código contienen un poco de programacióndefensiva.Para ver cuanto se sigue la programación defensiva en la práctica usual dedesarrollo de software vayan a freshmeat.net , busquen proyectos cualquieraen C, con fuentes disponibles, bajen la última versión, y encontrar en losfuentes una syscall de la cual se ignora el resultado.Miren el código de OpenSSH. Qué notan?

Este tip está tomando la forma de ejercicio obligatorio y debe comentarse en ladocumentación del laboratorio.Material adicional:"Pipes, Redirection, and Filters " del libro "The Art of Unix Programming".Capítulos 3 (Process) y 5 (Interprocess Communication) del libro "AdvancedLinux Programming".Capítulo "Implementing a Job Control Shell", de "The GNU C Library ReferenceManual"Tareas AdicionalesSi les sobra tiempo pueden hacer las siguientes mejoras:Posibilidad de mezclar los 4 operadores "&<>|", a lo más una vez cada uno, enla misma línea de comandos.

Page 39: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 39/55

 

Operador de composición secuencial ';', operador de composición paralelageneral '&'.Manejar bien los procesos lanzados en background para que no se generenzombies. Pueden consultar la sección 3.4.3 de "Advanced LinuxProgramming ", que está en la página 57.

Prompt configurable desde la variable de entorno PS1.

PRACTICA6 Lab3: TADs sincronizadosObjetivosComprender y utilizar los mecanismos de sincronización de granularidad finaque provee POSIX (PThreads).Solucionar problemas típicos de concurrencia: condiciones de carrera, abrazosmortales, inhanición, etc.Implementar TAD sincronizados.

IntroducciónEn el Laboratorio anterior utilizamos las facilidades de concurrencia de gruesagranularidad de UNIX. Creamos y esperamos la finalización de procesos quese ejecutaban en espacios de memoria disjuntos, y donde sólo se compartíanlos descriptores de archivos.Esta concurrencia hacía uso del concepto de proceso como unidad básica deejecución, el cual brindaba la posibilidad de ejecutar una componente de unmultiprograma en un espacio de memoria disjunto y a la vez protegido. Al nocompartir memoria, los procesos efectuan su comunicación utilizando algunade las estructuras que sí se comparten, como por ejemplo el sistema dearchivos, y vimos cómo a través de pipes podíamos comunicar y sincronizar

procesos.Un hilo o proceso liviano (ligthweight process - LWP) es otro de los posiblesmodelos de ejecución de un multiprograma que proveen los sistemasoperativos en general y *NIX en particular, y brinda otra posibilidad pararealizar multiprogramación.Esta segunda opción existe para que el programador tenga la posibilidad deelegir una solución de compromiso entre dos cualidades deseables: proteccióny eficiencia.Normalmente estos los hilos tienen vida dentro de los procesos, y podemospensar en los procesos simples que utilizamos en el Lab2 como procesos quesólo contienen un hilo de ejecución interno.

El problema reside en que un proceso maneja un estado relativamente grande,que involucra los registros del microprocesador, la pila de ejecución, el mapade memoria, los descriptores de archivos y tabla de manejadores de señal, conlo cual el nacimiento, la vida (cambios de contexto) y la muerte de los procesos,son operaciones que consumen muchos recursos de tiempo (cpu) y espacio(memoria).Los hilos en cambio, lo único que no comparten son los registros delmicroprocesador y el espacio de la pila, por lo que las opearciones de creación,cambio de contexto y muerte, resultan mucho más económicas.La comunicación entre las componentes del multiprograma es otro factor quediferencia los procesos de los hilos. Mientras en los hilos es posible transferir

información entre componentes simplemente leyendo y escribiendo en elespacio de memoria compartida, los procesos tienen que efectuar un secuencia

Page 40: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 40/55

 

de llamadas al núcleo, para poder completar la transferencia de información:espacio_de_usuario1, kernel, espacio_de_usuario_2, incurriendo no sólo ensobrecarga por copia, sino también por cambios de niveles de protección.El sistema operativo Linux, incorpora procesos livianos en un modelo conocidouno-a-uno utilizando clone, la generalización del syscall fork. A cada hilo se le

asocia un proceso, donde se comparte la memoria y los descriptores dearchivos.Lamentablemente esta implementación de procesos livianos, pierde algunas delas características esperables (LinuxThreads Frequently Asked Questions ,Programación utilizando C threads , FAQ de comp.programming.threads ).Es muy interesante la discusión que se propone en "The Art of UnixProgramming" en el capítulo 7 "Multiprogramming ", sobre todo en lasubsección "Threads Threat or Menace? ", atacando abiertamente el uso dehilos en la multiprogramación.Cómo crear hilosVeamos, a través un ejemplo, como crear 2 hilos que impriman por la salida

estandar, N veces "Hola Mundo" y M veces"Mundo Hola" respectivamente.Necesitamos incluir la cabecera de la biblioteca pthreads, parte de la glibc deLinux, que nos brinda acceso a todas las llamadas pthreads_*. Nosotros sóloutilizaremos en este ejemplo pthread_create y pthread_join, donde esta últimatiene una semántica parecida al wait de los procesos. Observar la declaración yuso de arreglos constantes.

holaMundo.c#include <stdio.h>#include <pthread.h>

#define N 8#define M 16

const char *mensaje[2] = {"Hola Mundo", "Mundo Hola"};

 

const int cantidad[2] = {N, M};

void imprime_mensaje(void *ptr) {int i;int id;

id = *(int *) ptr;for(i=0; i<cantidad[id]; i++)printf("%s\n",mensaje[id]);return;}

int main(int argc, char *argv[])

 

{pthread_t hilo0, hilo1;int id0=0, id1=1;

pthread_create(&hilo0, NULL, (void *) &imprime_mensaje, (void

Page 41: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 41/55

 

*) &id0);pthread_create(&hilo1, NULL, (void *) &imprime_mensaje, (void*) &id1);

pthread_join(hilo0, NULL);

pthread_join(hilo1, NULL);

return 0;}Para compilar este ejemplo debemos incluir la biblioteca pthread, de lasiguiente manera:

 

[juan@hal juan]$ gcc -Wall -lpthread -o holaMundoholaMundo.cNotamos que la llamada a pthread_create está pasando (un puntero a) unafunción como argumento y que se necesitan varios typecasts para poder

compatibilizar los tipos actuales con los requeridos por la función (ver manpthread_create).Puede parecer extraño que definamos las variables id0, id1 con dosconstantes, pero esto también es necesario cuando tenemos que pasarle ladirección del argumento y no el valor en sí mismo. Las funciones pthread_join,evitan que al terminar el main, acabe con la vida de sus hilos. La terminacióndel hilo se puede hacer con un simple return, que resulta equivalente apthread_exit(NULL).Lamentablemente en máquinas con procesadores rápidos, no podemosobservar ni el interleaving entre los mensajes, ni el no determinismo, esto sedebe a que toma tan poco tiempo imprimir los N o M mensajes definidos, que el

planificador casi no actua y el orden es puramente secuencial.¿De qué nos sirve entonces un entorno de multiprogramación donde lasplanificaciones de procesos cortos resultan seriales? Realmente de poco, puestoda la problemática de interferencia entre componentes, regiones críticas,abrazos mortales se trivializa. Sin embargo podemos simular interleavings másricos, indicando explícitamente al planificador que queremos entregar el controla otro proceso (recordemos que en Linux, el planificador no distingue entreproceso e hilo), mediante la función sched_yield().Estos sched_yield nos permitirán estar más confiados en que nuestrosmultiprogramas funcionarán de manera correcta independientemente delhardware o situación de carga concreta en donde se esté ejecutando.EjerciciosPredecir y comprobar los comportamientos posibles quitando alguno o ambospthread_join.Comprobar los interleavings que se producen dependen de la velocidad y factorde carga de la CPU. Deberían observar diferencias de ejecución entre las HPnuevas y las viejas.Forzar otros interleavings utilizando sched_yield.Si ponemos M y N de manera tal que los hilos tarden un tiempo considerable ycorremos nuestro programa, mientras que en otra terminal observamos losprocesos que se están ejecutando. ¿Cuántos son? ¿Por qué?

Observar que ahora si se producen interleavings entre los 2 mensajes.Memoria compartida entre hilos y las primeras interferencias

Page 42: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 42/55

 

El ejemplo anterior sólo mostraba como crear hilos y que ambos leyeran desdela memoria compartida, pero al tener sólo accesos de lectura a la memoria, noes posible diferenciar entre memoria compartida y memoria disjunta con copiasidénticas, como ocurría en el caso del fork.El siguiente ejemplo implementa un ejercicio típico de concurrencia, donde hay

un multiprograma con 2 componentes, cada una de estas incrementa ydecrementa cíclicamente una variable compartida.P0:do true ->x:=x+1;x:=x-1od

P1:do true ->x:=x+1;x:=x-1od

Es posible demostrar de manera más o menos convincente que 0<=x<=2 si laatomicidad es línea a línea.El siguiente programa es una implementación en pthreads del multiprogramaescrito en el Lenguaje de los Comandos Guardados de Dijkstra (LCGD).

programTopology.c#include <stdio.h>#include <assert.h>#include <pthread.h>

 /* compilacion condicional */ #define __DEBUG

void *IncrDecr(void *);

 /* la variable global y compartida */ 

 

int x = 0;

int main(int argc, char *argv[]) {pthread_t t0id,t1id;

pthread_create(&t0id, NULL, IncrDecr, NULL);pthread_create(&t1id, NULL, IncrDecr, NULL);#ifdef __DEBUG

printf("Hilos Creados\n");printf("Inicio de sonda de testeo\n");

 

#endif /* sonda que comprueba el Invariante */ 

 

while(1) {assert(0<=x && x<=2); /* Inv: 0<=x<=2 */ printf("%2d", x);} /* aqui nunca llega */ }

 /* componente del multiprograma */ 

Page 43: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 43/55

 

void *IncrDecr(void *arg) {#ifdef __DEBUGprintf("Hilo IncrDecr iniciado\n");#endiffor (;;) {

x = x+1;x = x-1;}}Remarcamos 2 aspectos de este programa: la compilación condicional a travésdel uso de las directivas del preprocesador #define, #undef, #ifdef y #endif, ylas variables globales.La compilación condicional, nos permite en este caso generar 2 versionesdiferentes de nuestro código una con información de debug y la otra sin ella,cambiando #define __DEBUG por #undef __DEBUG. Luego veremos como esposible obtener un mecanismo similar mejorando la legibilidad del código.El uso de variables globales es una condición necesaria para el uso dememoria compartida entre los hilos, a menos que pasemos a las funciones queejecutan los hilos referencias a espacios de memoria pedidos en el montículo oheap.Si compilamos y ejecutamos nuestro ejemplo que podemos llamarprogramTopology, veremos que aunque la sonda muestra pocos cambios en x,esta siempre se mantiene en el rango [0,2]. La falta de cambios se debenuevamente a la poca cantidad de interleavings que se producen, donde elefecto se nota más en procesadores rápidos y poco utilizados por el SO.Para generar más interleavings podemos inundar de sched_yield() nuestro

programa. Sin embargo nunca se producen los interleavings que muestran quex:=x+1 no es atómico, pues en este caso el invariante del multiprograma0<=x<=2 ya no vale. Notar que jamás le declaramos al compilador C que estainstrucción fuese atómica, asi que puede ser compilada a una sola o a unasecuencia (interrumpible) de intrucciones.A fin de simular el caso del incremento no atómico suele ser necesario dividirexplicitamente los incrementos y decrementos en 3 instrucciones.Ejercicios¿Por qué no es posible compartir entre los hilos una variable declarada dentrodel main()?Java da soporte al concepto de Thread desde el propio lenguaje, con algunas

clases einterfaces definidas en el paquete java.lang y con métodos específicos para lamanipulaciónde Threads en la clase Object.Desde el punto de vista de las aplicaciones los hilos son útiles porque permitenque el flujo delprograma sea divido en dos o más partes, cada una ocupándose de algunatarea de formaindependiente. Por ejemplo un hilo puede encargarse de la comunicación conel usuario,mientras que otros actúan en segundo plano, realizando la transmisión de unfichero,

Page 44: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 44/55

 

accediendo a recursos del sistema (cargar sonidos, leer ficheros ...), etc. Dehecho, todos losprogramas con interface gráfico (AWT o Swing) son multihilo porque loseventos y las rutinas de dibujado de las ventanas corren en un hilo distinto alprincipio

El método main crea dos objetos de clase ThreadEjemplo y los inicia con lallamadaal método start()(el cual inicia el nuevo hilo y llama al método run()).• Obsérvese en la salida el primer mensaje de finalización del thread main. Laejecución de los hilos es asíncrona. Realizada la llamada al método start(), ésteledevuelve control y continua su ejecución, independiente de los otros hilos.• En la salida los mensajes de un hilo y otro se van mezclando. La máquinavirtual asignatiempos a cada hilo.Hay dos posibles causas para que el planificador no interrumpa x:=x+1, una

puede ser simplemente que este hecho resulte altamente improbable y lasegunda es que la compilación genere una instrucción atómica a nivel delmicroprocesador. Investigue sobre este hecho en diversas arquitecturas.(Ayuda: con gcc -S generamos el código ensamblador intermedio dondepodemos corroborar este último hecho, quienes estén interesados, se puedenpreparar más ejercicios).Cuando un proceso recibe una señal, se interrumpe la ejecución en curso y seejecuta la función manejador.Al finalizar la cual, se reanuda la ejecucióninterrumpida.El envío de señales se realiza con sigqueue en lugar de kill„ Las señales se “entregan” en orden cronológico en el que

fueron enviadas„ Podemos realizar una espera rápida de señales: no se tieneque ejecutar ningún manejador„ sigwaitinfo: espera la llegada de una señal pero no llama a ningúnmanejador, devuelve el número de la señal„ sigtimedwait: igual que la anterior, pero tiene un parámetro“timeout” que especifica un tiempo máximo de llegada de la señal Modificar programTopology.c para que los incrementos no sean atómicos ycomprobar que la aserción puede no cumplirse para ciertos interleavings.¿Por qué al activar las opciones de optimización de código -O, el programa

anterior podría dejar de producir los interleavings requeridos para que no valgael invariante, o para peor imprime constantemente 0. Intente reproducir estosresultados.Sincronizando con MutexesLa biblioteca de hilos en Linux incorpora Locks o Mutexes, para protegerregiones críticas del código, y las llamadas básicas son las siguientes:pthread_mutex_init, pthread_mutex_lock, pthread_mutex_unlock ypthread_mutex_destroy.Notamos que tenemos llamadas para destruir el objeto Mutex a fin de liberarlos recursos que este pueda consumir.

En este caso nuestro ejemplo sincroniza los accesos a un buffer compartidopor parte de dos componentes, donde una establece un valor i en todo el buffer

Page 45: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 45/55

 

y la otra un valor j distinto. La idea es que queremos que se mantenga elinvariante que todo hilo externo siempre vea el buffer con un contenidouniforme de valores, ya sea i o j.Claramente si no protegemos con regiones críticas, es posible que a la mitadde una actualizacion, que no es atómica, se produzca un cambio de contexto,

ya sea para inspeccionar los valores o para establecerlos en otro valor.

Lo que sigue es el programa setBuffer que implementa esta idea y protege alas secciones críticas utilizando Locks.

setBuffer.c

#include <stdio.h>#include <stdlib.h>#include <assert.h>#include <pthread.h>

#define CAPACIDAD 16

void *set_buffer_to(void *);void yield(void);

 

 /* las variables globales y compartidas */ int buffer[CAPACIDAD];pthread_mutex_t mutex;

int main(int argc, char *argv[]) {pthread_t hilo0,hilo1;int val[2] = {4,8};int i;

for(i=0; i<CAPACIDAD; i++)buffer[i] = 0;

pthread_mutex_init(&mutex, NULL);pthread_create(&hilo0, NULL, set_buffer_to, (void

*)&val[0]);pthread_create(&hilo1, NULL, set_buffer_to, (void*)&val[1]); /* sonda que comprueba que el Inv siempre se cumpla */ while(1) {yield();

 

pthread_mutex_lock(&mutex); /* Inicio SC */  /* Inv: { \forall i : buffer[i]=buffer[0] } */ 

 

for (i=0; i<CAPACIDAD; i++) {assert(buffer[i]==buffer[0]);yield();

}pthread_mutex_unlock(&mutex); /* Fin SC */ 

Page 46: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 46/55

 

}}

 /* pone todo el buffer en un valor */ 

void *set_buffer_to(void *arg) {int val, i;

val = *(int *)arg;

 

while(1) {yield();pthread_mutex_lock(&mutex); /* Inicio SC */ for (i=0; i<CAPACIDAD; i++) {buffer[i] = val;yield();}pthread_mutex_unlock(&mutex); /* Fin SC */ yield();

 

}}

 

 /* se entrega al planificador de manera no deterministica */ void yield(void) {if (rand()%2)

sched_yield();}

Notar el uso de la función yield(), la cual agrega un poco de no-determinismo alsistema, entregando o no el hilo actual de ejecución con probabilidad ½. Luegoveremos sistemas un poco más sofisticados, todos tendientes a producirinterleavings siempre distintos en cada ejecución del código.EjerciciosUtilizar esta forma no-determinística de entregar el control al planificador en los2 ejemplos anteriores y comprobar que efectivamente hay mayor cantidad de

interleavings.Comprobar que sin la exclusión mutua, podría suceder el buffer se encuentreinconsistente.Se vio en los ejercicios anteriores que dividiendo el incremento en instruccionesmás simples e introduciendo algunos sched_yield, el programa abortaba puesse producían ciertos interleavings que invalidaban el invariante. Solucionar losproblemas de concurrencia de este programa utilizando locks para reestablecerel grado de atomicidad.Sincronizando con SemáforosUn Lock que no permita recursión puede ser implementado sencillamente conun semáforo. La operación lock es similar a la operación P de un semáforo: siel semáforo está con valor 0, la operación se bloquea; sino, el valor esdecrementado y la ejecución sigue. La operación unlock se la puede ver como

Page 47: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 47/55

 

una V sobre el semáforo. Ahora, si el valor del semáforo es mayor a 1, variasoperaciones P van a poder ejecutarse sin bloquear, por lo que es estrictamentenecesario evitar que eso pase. Por último, es necesario que inicialmente almenos un thread pueda ejecutar lock, por lo que el valor inicial del semáforodebe ser 1.

slock.c, .h

#include <assert.h>

#include "slock.h"

#define NOT_SHARED 0

int slock_init (slock l) {int ans;

 /* must start with 1 */  /* no shared semaphores yet! (we don't need them

 

anyways)*/ ans= sem_init (&l->bin, NOT_SHARED, 1);l->n= 1;return ans;}

int slock_destroy (slock l) {

return sem_destroy (&l->bin);}

int slock_acquire (slock l) {int ans;

ans= sem_wait (&l->bin);l->n= 0;return ans;}

int slock_release (slock l) {int ans;

assert (l->n<=1);ans= sem_post (&l->bin); /* ok, this can have inconsistency problems

 

where a++ is not atomic */ l->n++;}Sincronizando con Variables de CondiciónPodemos pensar los semáforos como una primitiva de sincronización que condos operaciones, una que espera en ciertas condiciones -P(s) espera si s=0- yotra que señalizan la continuación -V(s)-. Estas condiciones de espera son

Page 48: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 48/55

 

predicados específicos a fin de mantener el invariante de semáforo 0<=s.Las variables de condición son el tercer y último mecanismo de sincronizaciónque veremos y nos permite esperar y señalizar por condiciones más generalesque las impuestas en los semáforos.Hay 2 primitivas básicas:

esperar en una variable de condición.señalizar una variable de condición para que algún proceso esperando sobreella pueda continuar.También se suele incorporar una transmisión masiva (broadcast) deseñalizaciones a fin de despertar a todos los procesos esperando por lavariable de condición.La primitiva para esperar es pthread_cond_wait. Además de tomar una variablede condición, toma un mutex. Esto es a fin de establecer exclusión mutua entretodos los hilos que pueden modificar los datos que componen la condiciónsobre la que se espera y se señaliza. El pseudocódigo de pthread_cond_waitse muestra a continuación:

pthread_cond_wait (cond, mutex)beginpthread_mutex_unlock(mutex);

 

block_on_cond (cond);pthread_mutex_lock(mutex);

 

endNormalmente la secuencia de instrucciones de espera sería la siguente:pthread_mutex_lock(mutex);

"se modifican los valores de las

variables compartidas y se decide

 

esperar"

pthread_cond_wait(cond, mutex);

"debido al mutex, estoy seguro que lascondiciones sobre las variablescompartidas que produjeron laseñalización se siguen cumpliendo"

pthread_mutex_unlock(mutex);

Si pthread_cond_wait, no liberara y readquiriera la exclusión mútua de maneraatómica con las acciones de dormir y despertar, otros hilos podrían cambiar lascondiciones sobre las variables compartidas de manera que invaliden lacondición que se asocia a cond. Exactamente esto es lo que se puntualiza enla página 269 respecto de la implementación de semáforos en "An OperatingSystems Vade Mecum" de R.Finkel.El ejemplo que se muestra a continuación se denomina sincronización debarrera y nos permite sincronizar fases de una computación que se realiza deforma paralela por un conjunto de hilos.Supongamos que tenemos una máquina con 2 procesadores y necesitamos

realizar un cómputo, pero para que éste sea correcto, tenemos que poner a 0un gran vector A de valores que están en memoria compartida. Sería deseableque existan 2 hilos de ejecución, donde uno ponga a 0 los lugares pares del

Page 49: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 49/55

 

vector y otro los impares. Si tenemos en cuenta que es muy probable que cadahilo se ejecute en un procesador por separado, entonces esta etapa ocuparía lamitad del tiempo.Lamentablemente no queremos que se comience a ejecutar el cálculo(segunda fase) hasta que se haya terminado con el proceso de reseto del

vector (primera fase), y por supuesto no podemos confiar en que ambos hilosterminarán exactamente al mismo tiempo. Es por ello que introducimos unavariable de condición llegaron y una de exclusión mútua mutex. La primera seasocia a la condición 2<=n, donde n representa la cantidad de componentes(en este caso a lo más 2) que finalizaron la primera etapa, mientras que mutex,asegura que los accesos a n sean en exclusión mutua para evitar condicionesde carrera, invalidaciones o interferencias sobre esta variable compartida.El procedimiento es simple, cada componente pone a 0 sus elementos delvector y cuando termina incrementa n indicando que "una más llegó". Luego sin<2, entonces tenemos que esperar por la condición llegaron, y en cualquiercaso que detectemos que 2<=n despertamos a todos los otros hilos que

puedan estar esperando por la condición.Veamos el código:

barrera.c#include <stdio.h>#include <stdlib.h>#include <pthread.h>

#define N 2#define TAMANIO 1024

#define _Y yield()void *trabajador(void *);void yield();void randomize()

 /* los datos compartidos */ 

 

int vector[TAMANIO];struct barrera_s {int n;pthread_mutex_t mutex;

pthread_cond_t llegaron;} barrera;

int main(int argc, char *argv[]) {pthread_t hilo_p, hilo_i;

 

int par,impar;

 /* cambio la semilla de los numerosaleatorios */ randomize();

 /* init de los datos compartidos */ 

 /* trabajador */ 

 

void *trabajador(void *arg) {int paridadint i;

paridad = *(int *)arg;

for(i=paridad; i<TAMANIO; i+=2) {vector[i] = i;}printf("Termino %d\n", paridad);pthread_mutex_lock(&barrera.mutex);

 

barrera.n++;if (N<=barrera.n) { /* todas las

 

componentes llegaron */ pthread_cond_broadcast(&barrera.llegaron);} else { /* faltan de llegar */ 

pthread_cond_wait(&barrera.llegaron,&barrera.mutex);}pthread_mutex_unlock(&barrera.mutex);printf("Paso %d\n", paridad);return 0;}

void yield(void) {if (rand()%2)sched_yield();

}

Page 50: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 50/55

 

barrera.n = 0;pthread_mutex_init(&barrera.mutex,NULL);pthread_cond_init(&barrera.llegaron,NULL);

 /* creo los hilos */ par=0; impar=1;

 

pthread_create(&hilo_p, NULL,trabajador, (void *)&par);

 

pthread_create(&hilo_i, NULL,trabajador, (void *)&impar); /* espero que terminen */ pthread_join(hilo_p, NULL);pthread_join(hilo_i, NULL); /* destruyo la estructura */ pthread_cond_destroy(&barrera.llegaron);pthread_mutex_destroy(&barrera.mutex);

return 0;}

void randomize(void) {FILE *fd;unsigned int read;unsigned int buffer;fd = fopen("/dev/random","r");

read = fread((void *)&buffer,sizeof(unsigned int), 1, fd);

 

srand(buffer);}

Notar que mejoramos la legibilidad del código definiendo la macro _Y. Tambiénaumentamos el no-determinismo en la planificación cambiando la semilla deproducción de números aleatorios (man srand), mediante el dispositivo /dev/random, el cual está produciendo constantemente valores al azar

extraídos del no-determinismo del entorno (interrupciones, contadores,memoria, etc.).EjerciciosEl efecto de espera casi no se nota porque la velocidad de ejecución de los 2trabajadores es más o menos igual, ya que el planificador es bastante justo(variante del RR). Definir para cada trabajador un retardo específico que seejecutará como busy wait luego de cada modificación a vector, de manera quese pueda ver que uno termina antes que el otro.Generalizar y probar el código para N trabajadores.¿Qué sucedería si no se incluye la mutex previo al incremento de n?¿Tiene acceso a una placa madre dual? ¡Compruebe que este código tarda la

mitad que su versión secuencial!TareasVeremos un ejemplo clásico de la multiprogramación: Productor/Consumidor.En éste tenemos un buffer circular fifo (o cola) que es accedido por dosprocesos concurrentes: un productor que va colocando elementos en el buffer,y un consumidor que va retirando elementos.La cola tiene dos operaciones:void push (int i): agrega el elemento i al final de la cola. *ojo*: si la cola estállena, un assert hará estallar todo!int pop (): quita el primer un elemento de la cola y lo devuelve. *warning*: colasvacías se inmolan desprevenidamente!Entonces, dado el TAD de dicha cola (.c,  .h), que no está pensada paraaccesos concurrentes, y un sistema productor/consumidor (.c) que utiliza uno

Page 51: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 51/55

 

de estas colas concurrentemente, modificar el código de la cola de forma quepueda ser utilizada sin problemas por el porductor/consumidor.Su tarea, Jim, si desea aceptarla, es resolver los tres problemas que tienedicha cola; los dos antes mencionados y éste otro:Condiciones de carrera: las operaciones push() y pop() no son atómicas

cuando deberían serlo. Sino, el estado de la cola puede fácilmente quedarinconsistentePara ello puede utilizar cualquier combinación de las primitivas desincronización que vimos. Notar que la solución a los dos primeros problemasno deben ser resueltos con cosas como un par de funcionesis_full()/is_empty(), sino que el productor deberá bloquearse cuando la colaesté llena y el consumidor cuando la cola esté vacía.Cómo atacar los problemasEs prerequisito fundamental haber probado y comprendido los códigos deejemplo de la sección de introducción. También resulta útil comoprecalentamiento la resolución de los ejercicios que allí se plantean.

También es importante, relacionar los éstos problemas con los que se dieronen el teórico (sección crítica, productor-consumidor, lectores-escritores,filósofos comensales, etc.), pues aunque los problemas no son exactamenteiguales, pueden ser solucionados como combinaciones de éstos.Casi toda la cáscara del problema está lista, sólo se deberá incluir suficientesincronización como para que no ocurran los problemas, pero no tanta quelleve a un comportamiento secuencial (pocos interleavings posibles).Se pueden utilizar cualquiera de los 3 mecanismos de sincronización depthreads enunciados (locks, semáforos y/o variables de condición), perosiempre dentro del TAD rbuffer definido en buffer.{c,h}.Antes de comenzar a resolver los problemas es necesario mejorar laproducción de interleavings, utilizando la función yield(), o bien otras queustedes propongan. La idea es que los problemas surjan, de otra manera en lamayoría de los casos se obtiene una ejecución serial.Notar que probablemente tengan que insertar numerosos yield() en laimplementacion del TAD, pero tengan cuidado con el problema detectado; unpar de sched_yield() bien colocados pueden ser suficientes para evidenciar elproblema.Este problema admite varias soluciones, desde las más sencillas pero con unbajo grado de paralelismo, hasta algunas más complicadas, que dan máslibertad al planificador.

Qué se debe EntregarEste laboratorio es diferente a los anteriores en cuanto a que el trabajo deprogramación es mínimo, por lo tanto la mayor carga estará en el informe.El informe deberá indicar cuales eran los problemas, y como la sincronizaciónagregada los soluciona, dando argumentos convincentes al respecto.En el informe tambíen se pueden copiar y pegar secuencias de ejecucióncorrectas e incorrectas que se produzcan, pedazos de código donde seproducen los problemas, etc.

#include <linux/init.h>#include <linux/mm.h>

#include <linux/cpu.h>#include <linux/module.h>

Page 52: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 52/55

 

#include <linux/hardirq.h>#include <linux/topology.h>

#define define_one_ro_named(_name, _func) \ static DEVICE_ATTR(_name, 0444, _func, NULL) 

#define define_one_ro(_name) \ static DEVICE_ATTR(_name, 0444, show_##_name, NULL) 

#define define_id_show_func(name) \ static ssize_t show_##name(struct device *dev, \ 

struct device_attribute *attr, char *buf) \ { \ 

unsigned int cpu = dev->id; \ return sprintf(buf, "%d\n", topology_##name(cpu)); \ 

}

#if defined(topology_thread_cpumask) || defined(topology_core_cpumask) || \ defined(topology_book_cpumask) 

static ssize_t show_cpumap(int type, const struct cpumask *mask, char *buf) {

ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf; int n = 0;

if (len > 1) {n = type? 

cpulist_scnprintf(buf, len-2, mask) :cpumask_scnprintf(buf, len-2, mask);

buf[n++] = '\n';buf[n] = '\0';

}return n; 

}#endif

#ifdef arch_provides_topology_pointers#define define_siblings_show_map(name) \ static ssize_t

show_##name(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ 

unsigned int cpu = dev->id; \ return show_cpumap(0, topology_##name(cpu), buf); \ 

}

#define define_siblings_show_list(name) \ static ssize_t show_##name##_list(struct device *dev, \ 

struct device_attribute *attr, \ char *buf) \ 

{ \ unsigned int cpu = dev->id; \ 

Page 53: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 53/55

 

return show_cpumap(1, topology_##name(cpu), buf); \ }

#else#define define_siblings_show_map(name) \ 

static ssize_t show_##name(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ 

return show_cpumap(0, topology_##name(dev->id), buf); \ }

#define define_siblings_show_list(name) \ static ssize_t show_##name##_list(struct device *dev, \ 

struct device_attribute *attr, \ char *buf) \ 

{ \ 

return show_cpumap(1, topology_##name(dev->id), buf); \ }#endif

#define define_siblings_show_func(name) \ define_siblings_show_map(name); define_siblings_show_list (name) 

define_id_show_func(physical_package_id);define_one_ro(physical_package_id);

define_id_show_func(core_id);define_one_ro(core_id);

define_siblings_show_func( thread_cpumask);define_one_ro_named(thread_siblings, show_thread_cpumask);define_one_ro_named(thread_siblings_list, show_thread_cpumask_list);

define_siblings_show_func(core_cpumask);define_one_ro_named(core_siblings, show_core_cpumask);define_one_ro_named(core_siblings_list, show_core_cpumask_list);

#ifdef CONFIG_SCHED_BOOKdefine_id_show_func(book_id);define_one_ro(book_id);define_siblings_show_func(book_cpumask);define_one_ro_named(book_siblings, show_book_cpumask);define_one_ro_named(book_siblings_list, show_book_cpumask_list);#endif

static struct attribute *default_attrs[] = {&dev_attr_physical_package_id.attr, &dev_attr_core_id.attr, 

&dev_attr_thread_siblings.attr, &dev_attr_thread_siblings_list .attr, 

Page 54: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 54/55

 

&dev_attr_core_siblings.attr, &dev_attr_core_siblings_list .attr, 

#ifdef CONFIG_SCHED_BOOK&dev_attr_book_id.attr, &dev_attr_book_siblings.attr, 

&dev_attr_book_siblings_list. attr, #endifNULL

};

static struct attribute_group topology_attr_group = {.attrs = default_attrs, .name = "topology"

};

 /* Add/Remove cpu_topology interface for CPU device */ 

static int __cpuinit topology_add_dev(unsigned int cpu) {

struct device *dev = get_cpu_device(cpu);

return sysfs_create_group(&dev->kobj, &topology_attr_group);}

static void __cpuinit topology_remove_dev(unsigned int cpu) {

struct device *dev = get_cpu_device(cpu);

sysfs_remove_group(&dev->kobj, &topology_attr_group);}

static int __cpuinit topology_cpu_callback(struct notifier_block *nfb,unsigned long action, void *hcpu)

{unsigned int cpu = (unsigned long)hcpu;int rc = 0;

switch (action) {

case CPU_UP_PREPARE: case CPU_UP_PREPARE_FROZEN: rc = topology_add_dev(cpu);break;

case CPU_UP_CANCELED: case CPU_UP_CANCELED_FROZEN: case CPU_DEAD: case CPU_DEAD_FROZEN: 

topology_remove_dev(cpu);break;

}

return notifier_from_errno(rc);}

Page 55: TAREA

5/16/2018 TAREA - slidepdf.com

http://slidepdf.com/reader/full/tarea-55ab4fe4e3d37 55/55

 

 static int __cpuinit topology_sysfs_init(void){

int cpu; int rc; 

for_each_online_cpu(cpu) {rc = topology_add_dev(cpu);if (rc) 

return rc; }hotcpu_notifier(topology_cpu_callback, 0);

return 0;}

device_initcall(topology_sysfs_init) ;