ProblemasUnix Bis
ProblemasUnix Bis
en UNIX / Linux
Javier Gutiérrez
[email protected]
Introducción
Este libro pretende ser un compendio de los problemas de programación de
sistemas UNIX / LINUX que se suelen estudiar en las prácticas de la asignatura
Sistemas Operativos.
Se ha intentado en todo momento que este libro sea una referencia útil y
práctica. Por ello, se ha incluido al principio de cada capítulo un resumen con los
conceptos teóricos necesarios para resolver los ejercicios. Además, muchos ejercicios
cuentan con notas aclaratorias que ayudan a su comprensión. Junto con este libro,
también se ofrece un archivo comprimido con todo el código fuente de las soluciones.
Actualmente, este libro aún está en desarrollo. Periódicamente se incluyen
nuevos ejercicios, se completan los capítulos e, incluso, se añaden capítulos nuevos. A
continuación se muestra una tabla de versiones para que todos los lectores puedan saber
si poseen la versión más actualizada y cuales son los cambios entre las distintas
versiones.
Ejercicios de comandos.
4
1.1. Resumen de conceptos.
Para una versión posterior.
1.2. Enunciados.
Fichero-log.año.mes.dia
5
1.2. Espacio de disco.
> df -ka
Filesystem kbytes used avail capacity Mounted on
/dev/md/dsk/d2 4129290 191573 3896425 5% /
/dev/md/dsk/d5 4129290 1129374 2958624 28% /usr
/proc 0 0 0 0% /proc
fd 0 0 0 0% /dev/fd
mnttab 0 0 0 0% /etc/mnttab
/dev/md/dsk/d8 4129290 1360464 2727534 34% /var
swap 5541176 24 5541152 1% /var/run
swap 5542488 1336 5541152 1% /tmp
/dev/md/dsk/d11 18491215 3223353 15082950 18% /opt
/dev/dsk/c0t9d0s7 35007716 5337270 29320369 16% /export/home
/dev/dsk/c0t10d0s7 35007716 15579221 19078418 45%
/export/home/alumnos
-hosts 0 0 0 0% /net
-xfn 0 0 0 0% /xfn
murillo:vold(pid287) 0 0 0 0% /vol
>_
6
a) Contar el número de directorios que aparece en el archivo ficheros.
b) Contar el número de archivos ejecutables que aparecen en el archivo ficheros.
murillo:/export/home/prof/lensis> ps -a
PID TTY TIME CMD
529 pts/3 0:02 dtsessio
512 pts/3 0:00 ksh
528 pts/3 0:00 ttsessio
1615 pts/5 3:59 nwadmin
21477 pts/22 0:00 tcsh
7204 pts/3 0:01 dtfile
7203 pts/3 0:00 sh
7206 pts/3 0:00 dtfile
22717 pts/18 0:01 tcsh
21476 pts/22 0:00 ksh
23630 pts/40 0:00 ps
22716 pts/18 0:00 ksh
21576 pts/22 0:05 emacs
7160 pts/7 0:00 ksh
7
d) Cuente el número de copias de seguridad (hechas por el editor Emacs) de
archivos con código C que aparecen en el directorio ficheros. Las copias de
seguridad de Emacs tienen el mismo nombre que el archivo original
terminado con el carácter ~.
8
1.9. Gestión de recursos IPC.
Envíe una señal de finalización al proceso que ejecuta el programa emacs (PID
4242), suponiendo que tiene los permisos adecuados. La señal podría ignorarse y el
proceso podría continuar en ejecución. . Tenga en cuenta que se desea ejecutar dicho
comando en un shell korn.
9
1.3 Soluciones
Apartado a.
wc –l $LOGS/fichero-log.*
Apartado b.
grep ‘P:25’ $LOGS/fichero-log.2005.04.* > analisis.dat
Apartado c.
¿?
Apartado d.
¿?
Apartado a.
df -ka | grep ' 0%'
Apartado b.
Df -ka | grep '[456789].%'
Apartado c.
Df -ka | grep '/export' | wc -l
Apartado a.
grep ':.*directory$' ficheros | wc -l
Apartado b.
grep ', dynamically linked,' ficheros | wc -l
10
Apartado a.
ps -a | grep 'pts/[35]'
Apartado b.
ps -a | grep -w 'pts/3' | wc -l
Apartado c.
ps –A | grep –w 'ksh$' | wc -l
Apartado a.
grep –l ‘sem\.h’ $HOME/practicas/*
Apartado b.
grep ‘^a’ fichero
Apartado c.
ls | grep ".c$" | wc -l
Apartado d.
grep '\.c~:' ./ficheros | wc -l
Apartado a.
kill -9 $APACHE_PID
Apartado b.
export RUTA=$PATH
Apartado a.
cp f01 f02 f03 $HOME/ficheros
Apartado b.
11
cp /tmp/ejemplos/* $HOME/practicas
Apartado c.
cp /tmp/profile-ejemplo $HOME/.profile
Apartado d.
rm $HOME/core
Apartado e.
mv *.c ./src
Apartado a.
chmod 751 prueba
Apartado b.
chmod ug+x prueba
Apartado a.
ipcs –q
Apartado b.
ipcs | grep -w $USER
Apartado c.
ipcs –s | grep -w $USER | wc –l
Apartado d.
ipcrm –s $ID_SEM
Apartado e.
ipcs –q | more
Apartado f.
ipcrm -s 4127
12
1.10. Envío de señales.
Apartado a.
Como es un shell Korn, no ponemos SIG delante del nombre de las señales. Si fuera un
shell Bash (el que suele por defecto en Linux, por ejemplo), sí las pondríamos.
kill –15 (o –TERM o –s TERM) 4242
13
Capítulo 3.
Ejercicios de ficheros y
directorios.
14
3.1. Resumen de conceptos.
3.2. Enunciados.
Escriba un programa que cuente todas las entradas del directorio dónde se
ejecuta (incluido directorios y entradas ocultas). Si el programa funciona correctamente,
el resultado debe ser el mismo que el de los comandos: ls –a | wc –l.
Es posible desarrollar muchas variantes de este ejercicio. Por ejemplo puede
ampliar el programa para que en ve de tomar el directorio dónde se ejecuta, se pueda
indicar un directorio por línea de comandos. También puede hacer variantes para contar
sólo los directorios o contar sólo las entradas ocultas.
Este ejercicio es una variante del ejercicio anterior. Ahora, el programa debe
contar los directorios y los archivos regulares y mostrar el total de cada uno.
Compruebe como el resultado del programa puede ser distinto al resultado
obtenido por los comandos: ls –a | wc –l . ¿Por qué sucede esto?. Plantéese como
ejercicio opcional, mejorar este programa para que también cuente otros tipos de
entradas de directorio que no sean ni archivos regulares ni directorios?.
En el apartado de soluciones, encontrará una variante de este programa que,
además de la cuenta desglosada, también cuenta cuantas entradas pertenecen al mismo
usuario que ejecuta el programa y cuantas pertenecen a otros usuarios.
Escriba un programa que reciba por la línea de comandos la ruta de una entrada
de un directorio. El programa mostrará por pantalla un mensaje indicando si dicha
entrada es un directorio o no.
Una variante de este ejercicio es ampliar las opciones de este programa. Por
ejemplo, puede extender el programa para que indique si la entrada es un directorio, un
archivo regular, un enlace, etc.
15
Escriba un programa que muestre por la consola el mensaje típico mensaje
Hola mundo. No utilice printf. Como este es el capítulo de archivos, utilice las
funciones de entrada y salida y descriptores de archivos para hacerlo.
En este ejercicio vamos a escribir una función copia con el siguiente prototipo.
¿Cuál puede ser el motivo de que esta función devuelva un valor entero?.
Como su nombre indica, esta función copiará todo el contenido del archivo
referenciado por el descriptor de archivo origen en el archivo referenciado por el
descriptor de archivo destino.
Escriba un programa que realice la copia de un fichero. Para comprobar si la
copia se ha efectuado correctamente, puede utilizar el comando para comparar archivos
cmp.
16
3.3 Soluciones
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main();
int main() {
int cuenta=0;
DIR * dir;
char directorio[255];
struct dirent* entrada;
dir = opendir(directorio);
if (dir == NULL) {
perror("Error abriendo directorio: ");
return -1;
}
entrada = readdir(dir);
while(entrada != NULL) {
cuenta++;
//free(entrada);
entrada = readdir(dir);
}
if (closedir(dir) == -1) {
perror("Error cerrando directorio: ");
return -1;
}
return 0;
}
17
3.2. Contar entradas desglosadas.
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main();
int main() {
dir = opendir(directorio);
if (dir == NULL) {
perror("Error abriendo directorio: ");
return -1;
}
entrada = readdir(dir);
while(entrada != NULL) {
nombre = entrada->d_name;
if (stat(nombre, &info) == -1) {
perror("Error leyendo información.");
}
if (S_ISDIR(info.st_mode))
c_dir++;
if (S_ISREG(info.st_mode))
c_reg++;
entrada = readdir(dir);
}
if (closedir(dir) == -1) {
perror("Error cerrando directorio: ");
return -1;
}
18
return 0;
}
Variante.
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int regulares, directorios, otros;
int mios, ajenos;
char directorio[256];
DIR *bd;
struct dirent *entrada;
struct stat finfo;
bd = opendir(directorio);
if (bd == NULL)
{
perror("Error obteniendo el bloque del directorio.\n");
return -1;
}
entrada = readdir(bd);
while( entrada != NULL)
{
if ( stat(entrada->d_name, &finfo)==-1 )
{
perror("Error obteniendo información.\n");
return -1;
}
if ( S_ISDIR(finfo.st_mode) )
directorios++;
else if ( S_ISREG(finfo.st_mode) )
regulares++;
else
otros++;
if ( getuid() == finfo.st_uid )
mios++;
else
ajenos++;
19
entrada = readdir(bd);
}
closedir(bd);
return 0;
}
Una versión más potente de este programa la puede encontrar en el comando file.
Este ejercicio es realmente sencillo, solo es necesario una línea de código. Sin
embargo, es muy valioso repasar los fundamentos. Todo proceso tiene en su tabla de
descriptores de archivo tres descriptores abiertos y listos para ser usados. Estos
descriptores referencia a la entrada estándar (descriptor 0), salida estándar (descriptor
1) y salida estándar de errores (descriptor 2). Por lo tanto sólo tenemos que escribir en
el descriptos 1 para enviar información a la consola.
#include <stdio.h>
#include <stdlib.h>
#define TAM_BUF 255
printf("Hello, world!\n");
return 0;
}
20
Lo más importante de este programa es caer en la cuenta de que no debe mostrarse por
pantalla todo el contenido del buffer. Sólo deben mostrarse el mismo número de bytes
que se han leído.
Aunque no es relevante para este ejercicio, es importante saber que read no devuelve
una cadena de texto, es decir, el buffer no termina en \0. Para que el buffer sea una
cadena de texto correcta, se debe añadir el carácter \0 al final de los bytes leidos.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
return 0;
}
El motivo por el que esta función devuelve un valor entero es para indicar si todo fue
bien o si hubo algún error, al igual que la mayoría de funciones de las bibliotecas de C.
Archivo copia.h
#ifndef __copia__
#define __copia__
#endif
Archivo copia.c
#include <unistd.h>
#include "copia.h"
21
while ( (readed=read(fd_origen, buffer, SIZE)) > 0) {
if ( write(fd_destino, buffer, readed) == -1 )
return -1;
}
return 0;
}
Archivo de prueba.
#include <unistd.h>
#include <sys/file.h>
#include "copia.h"
if (argc != 3) {
printf("\nUso: pruebaCopia <origen> <destino> \n");
return 0;
}
if ( (fd_origen == -1 ) || (fd_destino==-1)) {
perror("\nError en la apertura de ficheros\n");
return -1;
}
if ( copia(fd_origen, fd_destino) != 0 ) {
perror("Fallo en la copia\n");
return -1;
}
close(fd_origen);
close(fd_destino);
printf("Copia correcta. \n");
return 0;
}
22
Capítulo 4.
Ejercicios de Procesos.
23
4.1. Resumen de conceptos.
4.2. Enunciados.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
// fork();
printf("Uno\n");
// fork();
printf("Dos\n");
// fork();
printf("Tres\n");
return 0;
}
¿Cuál sería el resultado de este programa si quitamos cada uno de los comentarios de la
llamada a fork. Es decir, que aparecerá por la consola y en qué orden (si es que se puede
predecir)?. ¿Y si quitamos los comentarios de todas las llamadas a fork a la vez?.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
int c;
pid_t pid;
24
printf("Comienzo.:\n");
for (c = 0; c < 3 ; c++ )
{
pid = fork();
}
printf("Proceso\n");
return 0;
}
A la vista del código anterior y, sin ejecutarlo, responda: ¿Cuántas líneas con la
palabra Proceso aparecen al final de la ejecución de este programa?. El número de
líneas es el número de procesos que han estado en ejecución.
Ejecute el programa y compruebe si su respuesta es correcta, Modifique el valor
del bucle for y compruebe los nuevos resultados.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
int c;
int p=0;
pid_t pid;
return 0;
}
25
Escriba un programa que lance tres nuevos procesos hijos. Estos procesos
mostrarán su identificador de proceso (PID) y el identificador de proceso de su padre
(PPID) y, después, terminarán.
El proceso padre debe esperar a que los procesos hijos terminen antes de
finalizar. ¿Qué pasaría si el proceso padre no esperara a los hijos?. Pruebe esto varias
veces, aumentando el número de hijos y compruebe los resultados.
Asegúrese que todos los PID y PPID mostrados son correctos y que, a diferencia
de los ejercicios anteriores, ningún proceso hijo llama a fork ni crea nuevos hijos.
Este ejercicio es una variante del ejercicio anterior. Ahora, cada uno de los tres
procesos hijos va a lanzar tres nuevos procesos. Estos procesos (que llamaremos
procesos nietos), también mostrarán su PID y su PPID. Recuerde que, ahora, los
procesos hijos deben esperar a que los procesos nietos hayan terminado antes de llamar
a exit y finalizar.
Al igual que en el ejercicio anterior, compruebe que todos los PID y PPID son
correctos y que no se lanzan nuevos procesos inesperados.
26
En este ejercicio vamos a poner a prueba la función atexit. Para ello se propone
escribir un sencillo programa que muestre un mensaje de despedida cuando dicho
programa termine, independientemente del lugar dónde termine el programa.
Una vez que lo tenga escrito, inserte funciones exit aleatoriamente y compruebe
que, termine donde termine el programa, se muestra el mensaje de despedida.
4.8. Tuberías.
En este ejercicio vamos a introducir las tuberías anónimas, las cuales nos
servirán para comunicar dos procesos con un ascendente común (es decir, que tienen un
proceso ancestro común el cuál creó la tubería).
Se propone escribir un sencillo programa en el que un proceso cree una tubería y
después, lance un proceso hijo. El proceso padre enviará por la tubería una cadena de
texto. El hijo recibirá dicha cadena de texto y la mostrará por pantalla.
27
4.3 Soluciones
Recuerde que, cada proceso hijo comienza su ejecución siempre después de fork y
ejecuta el mismo código que su proceso padre, con el mismo valor para la variable
contadora del bucle for.
En la primera vuelta del bucle, el proceso padre llama a fork y se crea un proceso hio.
En este momento tenemos dos procesos.
En la segunda vuelta del bucle, tanto el proceso padre como el hijo llaman de nuevo a
fork y se crean dos nuevos procesos. En este momento tenemos cuatro procesos.
En la tercera vuelta del bucle, el padre y los hijos hacen lo mismo, es decir, ejecutan
fork. Se crean cuatro nuevos procesos. En este momento tenemos ocho nuevos
procesos.
Como el bucle ya no da más vueltas, o se llama más a fork y, cada uno de los ocho
procesos muestra su mensaje por la consola.
Cada proceso, padres e hijos, tiene su propia copia de todas las variables. Estas copias
son independientes de las demás variables de los demás procesos. Por ese motivo, los
ocho procesos tendrán una variable p con valor 0 y, al final de la ejecución, se muestra
ocho veces el valor 1.
Este ejercicio es muy sencillo, sin embargo muestra el esquema que usaremos siempre
que lancemos nuevos procesos hijos con fork. Los hijos realizarán su tarea y
terminarán con una llamada a exit. El padre hará su tarea y esperará a sus hijos con
llamadas a wait hasta que todos los hijos hayan terminado.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>
int main(void) {
int i;
int status;
pid_t pid;
28
printf("Hijo %i con padre %i\n",(i+1), (int)getppid() );
exit(0);
}
}
pid = wait(&status);
while ( (pid != -1) || ( (pid==-1) && (errno == EINTR) ) ) {
printf("Hijo %i terminado.\n", (int)pid);
pid = wait(&status);
}
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>
int main(void) {
int i;
int status;
pid_t pid;
exit(0);
}
}
pid = wait(&status);
while ( (pid != -1) || ( (pid==-1) && (errno == EINTR) ) ) {
printf("Hijo %i terminado.\n", (int)pid);
pid = wait(&status);
29
}
Lo primero que tenemos que hacer es crear el archivo errores.txt con permisos de
escritura. Como ya vimos en el boletín anterior, todos los mensajes de error se envían
al descriptor de archivo 3, que, por defecto se envía a la salida estándar que es la
consola. Lo único que hay que hacer es cambiar esto para que todo lo que se encía al
descriptor de archivo 3 termine en el archivo errores.txt. Esto lo vamos a hacer con la
función dup o dup2.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
int main()
{
int fp;
int fpdup;
fpdup=dup2(fp, 2);
printf("%i / %i\n", fp, fpdup);
close(fp);
perror("Error simulado.\n");
return 0;
}
#include <fcntl.h>
#include <stdio.h>
30
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>
int main() {
int fd;
pid_t pid;
int status;
pid = fork();
if (pid == -1) {
perror("Error creando proceso ");
close(fd);
return -1;
}
if (pid == 0) {
status = dup2(fd, STDOUT_FILENO);
close(fd);
if (status == -1) {
perror("Error duplicando descriptores ");
exit(EXIT_FAILURE);
}
status = execlp("ls", "ls", "-l", NULL);
perror("Error ejecutando ls.");
exit(EXIT_FAILURE);
}
close(fd);
while ( wait(&status) != pid) ;
return 0;
}
#include <unistd.h>
void msg_adios();
int main()
{
int status;
status=atexit(msg_adios);
if (status == -1) {
perror("Error instalando el manejador - ");
exit(-1);
}
31
printf("Me termino.\n");
exit(0);
}
//-------------------------------------------------
void msg_adios() {
printf("Adios. \n");
}
4.8. Tuberías.
#include <unistd.h>
#include <sys/types.h>
int main()
{
int tub[2];
char msg[256];
pid_t pid;
int status;
pipe(tub);
pid = fork();
if (pid !=0) {
// El padre
close(tub[0]);
write(tub[1], "Soy padre.\n", 12);
wait(&status);
return 0;
}
else {
// El hijo
close(tub[1]);
read(tub[0], msg, 256);
printf("Hijo > %s / %ld\n", msg, getpid());
exit(0);
}
32
Capítulo 5.
Ejercicios de señales.
33
5.1. Resumen de conceptos.
5.2. Enunciados.
Escriba un programa que cuente las señales USR1 y USR2 que recibe. Cuando
este programa reciba la señal TERM, mostrará el número de señales USR1 y USR2
recibidas y terminará.
Una variante de este ejercicio, si ha utilizado un manejador distinto para cada
señal, es escribir una nueva versión utilizando una única función como manejador para
ambas señales.
$ps
PID TTY TIME CMD 1. Vamos a monitorizar los procesos que ejecutan
1723 ttyp0 00:00:00 bash
1779 ttyp0 00:00:08 emacs
Emacs y KWrite (dos editores de texto). En este
4217 ttyp0 00:00:01 kwrite ejemplo, los PID de ambos procesos son 1779 y
4259 ttyp0 00:00:00 ps 4217 y se han resaltado en negrita.
$
$ pmon 2. Ejecutamos el programa de monitorización para
Uso: pmon <seg> <pro1> ... <pro10>
que compruebe si dichos procesos extán activos
$ pmon 12 1779 4217 & cada 12 segundos.
$ Ejecutamos el programa en segundo plano.
$ps
PID TTY TIME CMD 3. Si vemos de nuevo los procesos del sistema,
34
1723 ttyp0 00:00:00 bash observamos que también está activo el programa
1779 ttyp0 00:00:08 emacs de monitorización (PID 4373).
4217 ttyp0 00:00:01 kwrite
4373 ttyp0 00:00:00 pmon
4459 ttyp0 00:00:00 ps
$
$ kill 4217
[2]- Terminado kwrite 4. A las 11:50:06 del día 3 de diciembre de 2005,
$ps enviamos una señal de terminación al proceso que
PID TTY TIME CMD ejecuta el editor KWrite y este termina
1723 ttyp0 00:00:00 bash normalmente (comando kill).
1779 ttyp0 00:00:08 emacs Si volvemos a obtener los procesos, vemos que
4373 ttyp0 00:00:00 pmon sigen activos el proceso que ejecuta Emacs y
4779 ttyp0 00:00:00 ps
nuestro monitor, pero ya no está el 4217.
$
$ kill 4373 5. A las 11:50:40 del día 3 de diciembre de 2005,
[2]- Terminado pmon enviamos una señal de terminación al proceso que
$ ejecuta nuestro monitor y este termina
normalmente.
$ cat pmon.log
Sat Dec 3 11:49:38 2005
Proceso 1779 aún activo.
Proceso 4647 aún activo.
Sat Dec 3 11:49:50 2005 6. Después vemos el contenido del archivo
Proceso 1779 aún activo. pmon.log.
Proceso 4647 aún activo. Observe que cada entrada entrada se anota 12
Sat Dec 3 11:50:02 2005 segundos después de la entrada anterior porque en
Proceso 1779 aún activo.
Proceso 4647 aún activo.
el paso 2 le indicamos 12 segundos a pmon.
Sat Dec 3 11:50:14 2005 Observe que, a las 11:50:14, nuestro programa
Proceso 1779 aún activo. detecta qué el proceso 4217 (el kwrite) ya ha
Proceso 4647 terminado. terminado.
Sat Dec 3 11:50:26 2005 Observe también como en las entradas posteriores
Proceso 1779 aún activo. ese proceso ya no aparece.
Sat Dec 3 11:50:38 2005
Proceso 1779 aún activo.
Fin de la monitorización.
$
Ayuda:
- Recuerde que puede conocer si un PID existe o no enviándole la señal 0
mediante la función Hill..
- Por simplicidad suponga que el máximo número de procesos a monitorizar será
10.
- Para obtener la fecha y la hora del sistema utilice las siguientes funciones:
35
En este ejercicio proponemos escribir una sencilla máquina de estados controlada
por señales para controlar un servicio genérico. El funcionamiento de dicho programa se
describe a continuación:
1. Cuando el programa se inicia, inicializa el servicio (función
iniciar_servicio) y queda a la espera de recibir una señal USR1. Si, en este
punto recibe cualquier otra señal, el programa realiza el comportamiento por
defecto de dicha señal.
2. Cuando recibe dicha señal comienza a prestar el servicio continuamente (una
llamada continua a la función prestar_servicio).
3. Si el programa recibe un CTRL+C (recuerde, señal INT), el servicio se
interrumpe hasta que recibe una señal USR2. Cuando recibe una señal USR se
vuelve al punto 2.
4. Cuando el programa recibe un TERM, termina el servicio (función
terminar_servicio) y el programa terminará.
Utilice las siguientes funciones auxiliares para simular el servicio:
void iniciar_servicio();
void prestar_servicio();
void terminar_servicio();
5.4. La subasta
Apartado a.
36
Tenga en cuenta que la subasta no comienza a partir de una cantidad concreta,
por lo que la primera oferta que reciba el gestor será la oferta más alta.
Escriba el código en un archivo gestor.c y genere un ejecutable llamado
gestor. Esto no tiene relevancia en este apartado pero es necesario para el apartado d.
Apartado b.
Apartado c.
Apartado d.
37
3. Después, el proceso lanzador creará un hijo y lo reemplazará por el proceso
postor de apuesta múltiple (utilizando también exec).
4. Después, el proceso lanzador creará 5 hijos y, a cada uno, lo reemplazará por
el proceso postor de apuesta única (utilizando también exec).
Si todo funciona correctamente, será el proceso postor de apuesta multiple el que
termine ganando la subasta.
Escriba un programa que haga uso de las alarmas para mostrar el valor de un
contador cada 2 segundos. El contador se incrementará en uno cada vez que se muestre
su valor.
Por ejemplo, si este programa estuviera en ejecución durante 11 segundos, su
resultado sería el que se muestra a continuación.
$ ./vivo
Contador: 0
Contador: 1
Contador: 2
Contador: 3
Contador: 4
^C
$_
5.7. El banco
38
En este ejercicio vamos a implementar el código del proceso local cuya misión enviar al
recolector todas las operaciones almacenadas en su máquina. Su funcionamiento se describe a
continuación:
1. El proceso local esperará hasta recibir la señal USR1.
2. Después, el proceso local leerá el archivo de operaciones (llamado operaciones). Éste es un
archivo binario que contiene una estructura de tipo s_operacion por cada operación
almacenada en el ordenador local. Cada operación la enviará a la salida estándar y leerá otra
operación del proceso recolector. Si el identificador de la operación leída es distinto del
identificador de la operación que acaba de enviar mostrará un mensaje de error de
verificación.
3. Cuando el proceso local haya enviado todas las operaciones, terminará la transmisión
enviando una nueva operación con identificador.
struct s_operacion {
int id_operacion;
char cuenta_origen[15];
char cuenta_destino[15];
double cantidad;
};
39
5.3. Soluciones
#include <signal.h>
#include <unistd.h>
int main()
{
while(1)
sleep(5);
return 0;
}
//-------------------------
void manejadorUSR1(int sig) {
cuenta1++;
}
//-------------------------
void manejadorUSR2(int sig) {
40
if ( signal(SIGUSR2, manejadorUSR2) == SIG_ERR) {
perror("Error instalamdno manejadorUSR2 - ");
exit(0);
}
cuenta2++;
}
//---------------------------
void manejadorTERM(int sig) {
#include <signal.h>
#include <unistd.h>
int main()
{
while(1)
sleep(5);
return 0;
}
//-------------------------
void manejadorUSR(int sig) {
41
if ( signal(SIGUSR2, manejadorUSR) == SIG_ERR) {
perror("Error instalamdno manejadorUSR2 - ");
exit(0);
}
if (sig == SIGUSR1)
cuenta1++;
else
cuenta2++;
//---------------------------
void manejadorTERM(int sig) {
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
int fd;
char mensaje[255];
void man_quit(int);
pid_t procesos[10];
int num_procesos;
unsigned int segundos;
int i;
int r;
time_t hora;
ssize_t leidos;
if (argc < 3) {
printf("Uso: pmon <seg.> <proceso1> .... <proceso10>\n");
return -1;
}
42
fd = open("pmon.log", O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd == -1) {
perror("Error creando archivo de log ");
return -1;
}
while(1) {
sleep(segundos);
time(&hora);
strcpy(mensaje, ctime(&hora));
write(fd, mensaje, strlen(mensaje));
if (procesos[i] != -1) {
printf("Monitorizando a: %i \n", (int)procesos[i]);
r = kill(procesos[i], 0);
if (r == -1) {
snprintf(mensaje, 255, "Proceso %i terminado.\n",
(int)procesos[i]);
procesos[i] = -1;
} else {
snprintf(mensaje, 255, "Proceso %i aŽún activo. \n",
(int)procesos[i]);
}
printf("%s", mensaje);
leidos = write(fd, mensaje, strlen(mensaje));
if (leidos == -1)
perror("Error escribiendo en archivo de log. ");
}
}
}
//--------------------------------------
void man_quit(int sig) {
close(fd);
43
exit(0);
}
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
int main() {
iniciar_servicio();
pause();
44
}
while( 1 ) {
if (servicio_activo) {
prestar_servicio();
sleep(4);
}
else
pause();
}
return 0;
//--------------
void man_usr1(int s) {
// Esta no hace nada
}
void man_usr2(int s) {
if (signal(SIGUSR2, man_usr2) == SIG_ERR) {
perror("Error instalando USR2.");
return ;
}
servicio_activo = 1;
}
void man_int(int s) {
if (signal(SIGINT, man_int) == SIG_ERR) {
perror("Error instalando INT.");
return ;
}
servicio_activo = 0;
}
void man_term(int s) {
terminar_servicio();
exit(0);
}
void iniciar_servicio() {
printf("Servicio iniciado.\n");
}
void prestar_servicio(){
printf("Servicio prestado.\n");
}
void terminar_servicio(){
printf("Servicio terminado.\n");
}
5.4. La subasta
Apartado a.
45
Es importante caer en la cuenta de que el gestor no es el responsable de crear la
tubería, sino que escuchará de una tubería ya creada. Por ese motivo, el gestor escucha
por su entrada estándar.
Será responsabilidad del programa de apartado d crear la tubería y rediriguirla a la
entrada estándar del programa gestor.
También es importante darse cuenta que este programa tiene que responder a quien le
envíe una oferta con una señal. Para poder enviar una señal a un proceso necesitamos
conocer su PID. ¿Cómo podemos saber el PID del proceso que nos ha enviado una
oferta?. La solución más sencilla es que sea el propio proceso que hace la oferta quien
nos informe de su PID. Por eso hemos creado una estructura con dos campos, uno para
la cantidad de la oferta y otro para el PID del proceso que la envía.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#define TUB_IN 0
struct s_puja {
pid_t id;
int cantidad;
};
int main() {
ssize_t leidos;
max_puja.cantidad = -1;
while(1) {
leidos = read(TUB_IN, &puja, sizeof(struct s_puja) );
if (leidos == -1)
perror("Error leyendo de tubería. ");
46
perror("Error avisando a pujador para que mejore. ");
}
}
}
//----------------------------
void sig_alrm (int sig) {
printf("Ganador: %i, %i\n", (int)max_puja.id, max_puja.cantidad);
if ( kill(max_puja.id, SIGUSR2) == -1)
perror("Error avisando al ganador. ");
exit(0);
}
Apartado b.
Como este proceso va a escribir en una tubería que crea otro proceso, escribimos en la
salida estándar. Al igual que en el caso anterior. Será responsabilidad del
Aquí también usaremos la estructura definida en el apartado anterior
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#define TUB_OUT 1
struct s_puja {
pid_t id;
int cantidad;
};
int main() {
ssize_t escritos;
struct s_puja puja;
puja.id = getpid();
puja.cantidad = (int)getpid();
47
// Este return no se ejecutará nunca
// return 0;
}
//---------------------
void sig_usr1(int sig) {
fprintf(stderr, "%i se retira de la puja.\n", (int)getpid() );
exit(0);
}
//---------------------
void sig_usr2(int sig) {
fprintf(stderr, "%i. He ganado....", (int)getpid());
exit(0);
}
Apartado c.
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#define TUB_OUT 1
struct s_puja {
pid_t id;
int cantidad;
} puja;
int main() {
while(1) {
pujar();
pause();
}
//return;
}
//-------------------------
void pujar() {
ssize_t escritos;
48
escritos = write(TUB_OUT, &puja, sizeof(struct s_puja));
if (escritos == -1)
perror("Error escribiendo en la tubería.");
}
//------------------------
void sig_usr1(int sig) {
if (signal(SIGUSR1, sig_usr1) == SIG_ERR) {
perror("Error instalando manejador. ");
return ;
}
fprintf(stderr, "%i incrementa su puja en 4.\n", (int)getpid() );
puja.cantidad += 4;
return;
}
//-------------------------
void sig_usr2(int sig) {
fprintf(stderr, "%i. He ganado \n", (int)getpid() );
exit(0);
}
Apartado d.
Este programa es largo pero muy sencillo. De hecho esto es un ejercicio de repaso del
capítulo anterior ya que no hay que usar nada relativo a señales.
Este programa sí es el responsable de crear la tubería. Lo más importante es acordarse
de redirigir la entrada y la salida de la tubería a la entrada estándar y la salida
estándar.
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>
#define APOSTANTES 5
int main() {
pid_t pid;
int i;
int tub[2];
if (pipe(tub) == -1) {
perror("Error creando tubería. ");
return -1;
}
if (pid == 0) {
if ( (dup2(tub[0], STDIN_FILENO)) == -1) {
49
perror("Error duplicando descriptor de entrada. ");
exit(0);
}
execl("./gestor", "./gestor", NULL);
perror("Error ejecutando el gestor.");
return -1;
}
if (pid == 0) {
if (dup2(tub[1], STDOUT_FILENO) == -1) {
perror("Error duplicando descriptor de salida.");
exit(0);
}
execl("./pujador_2", "./pujadore_2", NULL);
perror("Error ejecutando el programa postor_2.");
}
if (pid == 0) {
if ( dup2(tub[1], STDOUT_FILENO) == -1) {
perror("Error duplicando descriptor de salida.");
exit(0);
}
execl("./pujador_1", "./pujador_1", NULL);
perror("Error ejecutando el programa avion.");
}
pid = wait(NULL);
while ( (pid != -1) ||
( pid == -1 && errno == EINTR) )
pid = wait(NULL);
close(tub[0]);
close(tub[1]);
return 0;
}
50
Si el proceso ya terminó, recogemos su valor de finalización y el proceso hijo termina
definitivamente. Si no le mandamos una señal para que termine (por ejemplo la señal
TERM), y recogemos su valor de finalización.
#include <unistd.h>
#include <signal.h>
#include <wait.h>
#include <sys/types.h>
int main()
{
pid_t pid, pid_hijo;
int status;
int sleep_padre = 2;
int sleep_hijo = 1;
if ( (pid_hijo=fork()) == -1) {
perror("Error en el fork - ");
return -1;
}
if (pid_hijo == 0) {
sleep(sleep_hijo);
printf("Termina hijo.\n");
exit(0);
}
sleep(sleep_padre);
pid = waitpid(pid_hijo, &status, WNOHANG);
if (kill(pid_hijo,0)==0) {
printf("Hijo aún en activo. Lo termino\n");
kill(pid_hijo, SIGTERM);
waitpid(pid_hijo, NULL, 0);
}
printf("Termina padre.\n");
return 0;
}
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
void sig_alarm(int);
int num_alarm = 0;
51
int main()
{
if ( signal(SIGALRM, sig_alarm) == SIG_ERR) {
perror("Error instalando manejador - ");
return -1;
}
alarm(2);
while(1) {
printf("Alarma %i\n", num_alarm);
pause();
}
return 0;
}
//---------------------------
alarm(2);
num_alarm++;
}
5.7. El banco
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
struct s_operacion {
int id_operacion;
char cuenta_origen[15];
char cuenta_destino[15];
double cantidad;
};
int action = 0;
int main() {
52
int r;
int fd;
ssize_t leidos, leidos_tub;
struct s_operacion operacion, op_copia, op_fin;
op_fin.id_operacion = -1;
const int TAM_OP = sizeof(struct s_operacion);
// Espera USR1
while(action == 0) {
pause();
}
do {
// lee una operación
leidos = read(fd, &operacion, TAM_OP);
if ( (leidos > 0)) {
// La envía por la tubería
write(STDOUT_FILENO, &operacion, TAM_OP);
// Espera la confirmación
leidos_tub = read(STDIN_FILENO, &op_copia, TAM_OP);
if (leidos_tub > 0) {
if (operacion.id_operacion != op_copia.id_operacion) {
fprintf(stderr, "Error, operaciones distintas\n");
}
} else {
// Escribir mensaje error leyendo confirmación
perror("Error leyendo confirmación.");
}
}
if ( leidos == -1) {
perror("Error leyendo de fichero de operaciones.");
break;
}
} while (leidos != 0 );
//----------------------------
void manUSR1(int sig) {
53
action = 1;
}
54
Capítulo 6.
Ejercicios de semáforos.
55
6.1. Resumen de conceptos.
6.2. Enunciados.
$ ./scon
Uso: ./scon [-c -r -l -d]
-c Crea el grupo de semáforos y lo inicializa
-r Reinicia el grupo de semáforos.
-l Muestra los valores de los semáforos
-r Borra el grupo de semáforos.
$ _
56
El grupo de semáforos que crea la mopción –c tendrá 3 semáforos. Los valores
iniciales de dichos semáforos serán de 0, 3 y 0 respectivamente.
Si se invoca una operación pero el grupo de semáforos no ha sido creado, se
muestra un mensaje de error y la ejecución del programa termina.
A continuación tiene algunos ejemplos por si los encuentra útiles.
$ ./scon -r
Error recuperando grupo - : No such file or directory
$ ./scon -c
$ ./scon -l
Semaforo: 0 Valor 0 |Semáforo: 1 Valor 0 |Semáforo: 2 Valor 0 |
$ ./scon -c
Error creando grupo - : File exists
$ ./scon -d
$
Escriba un programa que muestre por pantalla los números del 1 al 6 en el orden
correcto (es decir: 1, 2, 3, 4, 5, 6). Para hacerlo, se deben crear dos procesos, el primero
mostrará los números impares (1, 3, 5) y el segundo mostrará los números pares (2, 4,
6).
Lógicamente, para que el resultado sean los 6 números en orden, ambos
procesos deben estar sincronizados, es decir, cada proceso debe saber cuando tiene que
escribir el siguiente número y cuando tiene que esperar. Para ello utilice semáforos.
Antes de comenzar a resolver este ejercicio conteste a las siguientes preguntas.
¿Cuántos semáforos serán necesarios?. ¿Qué valores de inicio deben tener esos
semáforos?. ¿Cuál va a ser el valor máximo y mínimo que va a alcanzar cada
semáforo?. ¿Cuándo, y cuanto, hay que sumar o restar al semáforo?.
Una variante de este ejercicio con más dificultad es escribir un programa que
muestre en orden los números del 1 al 9. Los encargados de escribir los números van a
ser tres procesos. El primer proceso escribirá los números 1, 4 y 7. El segundo proceso
escribirá los números 2, 5 y 8. El tercer proceso escribirá los números 3, 6 y 9. Conteste
a las preguntas del párrafo anterior antes de resolver esta variante.
Como ejercicio de repaso, piense como resolver este ejercicio sin usar
semáforos, por ejemplo con tuberías o señales.
57
Después de años estudiando oposiciones y tras conseguir la certificación
correspondiente, un grupo de N_TECNICOS procesos ha conseguido plaza como
técnicos de descargas de maletas en un aeropuerto. El primer día el encargado les
explica como funciona el asunto:
Los técnicos tenéis que coger las maletas de la bodega del avión, llevarlas hasta
el carro de las maletas, dejarla allí y volver a por más. La ley de seguridad laboral de la
descarga de maletas os obliga a que cada uno coja una sola maleta cada vez y a que, en
cualquier momento, sólo haya un único técnico tanto en la bodega del avión como en el
carro de las maletas.
Vamos a un programa en C que simule el párrafo anterior. Pare ello empleamos
los siguientes consejos: Un técnico tarda un tiempo T_ACARREO desde que sale de la
bodega con una maleta hasta que llega al carro de las maletas. Puede considerar que
cada técnico va a sacar un número finito de maletas N_MALETAS de la bodega y
llevarla al carro.
Tenemos que definir las constantes N_TECNICOS, T_ACARREO y
N_MALETAS. Prueba con distintos valores para comprobar los resultados.
Recuerde que no es necesaria ninguna variable compartida.
Ayuda:
En este ejercicio, a diferencia de los anteriores, necesitamos una variable que sea
común a varios procesos diferentes. Piense un momento el por qué la necesitamos antes
de continuar leyendo.
Efectivamente, es necesario saber cuál es el número de cajas que hay en el
muelle. Sin embargo, una variable compartida no es sencilla de hacer. Una solución,
que puede implementar como ejercicio, es utilizar un archivo. La alternativa que
proponemos en nuestra solución es utilizar memoria compartida, el cuál es otro
mecanismo IPC. Para simplificar el uso de memoria compartida e proporciona una
librería de funciones. Esta libería se propone como ejercicio y se explica con detalle en
el capítulo dedicado a memoria compartida.
58
de VISITANTES procesos ha decidido visitarla, sin embargo la sala de espera del
museo solo tiene capacidad para ESPERA procesos, por lo que los procesos que no
puedan entrar deberán esperar en la calle hasta que haya sitio libre en la sala de espera.
Una vez en la sala de espera, un proceso podrá entrar en la sala de exposición si hay
sitio. La exposición puede ser visitada simultáneamente por EXPOSICION procesos.
Cada proceso estará VISITA segundos admirando la exposición antes de marcharse a
casa.
En este ejercicio vamos a escribir un programa en C que simule el párrafo
anterior. Por ejemplo, puede usar los siguientes valores para las constantes y
modificarlas para observar los resultados: VISITANTES=9, ESPERA=4,
EXPOSICION=4 y VISITA=2..
Observe que un proceso debe conseguir primero acceso a la sala de espera y después a
la sala de exposición. Cuando un proceso entra en la sala de exposición queda un hueco
libre para que pueda entrar en la sala de espera un proceso que está en la calle. Cuando
un proceso sale de la sala de exposición queda un hueco libre para que pueda entrar en
la sala de la exposición un proceso que está en la sala de espera. Además, todos los
procesos esperarán el tiempo que haga falta hasta visitar la exposición
En este ejercicio no es necesaria ninguna variable compartida.
6.7. El banco I.
59
2: ventanilla 1 ocupada.
2: atendido en ventanilla en 2.
3: solicitando acceso a las ventanillas.
4: solicitando acceso a las ventanillas.
5: solicitando acceso a las ventanillas.
6: solicitando acceso a las ventanillas.
0: liberando ventanilla.
1: liberando ventanilla.
2: liberando ventanilla.
3: buscando ventanilla libre.
3: atendido en ventanilla en 0.
4: buscando ventanilla libre.
4: ventanilla 0 ocupada.
4: atendido en ventanilla en 1.
5: buscando ventanilla libre.
5: ventanilla 0 ocupada.
5: ventanilla 1 ocupada.
5: atendido en ventanilla en 2.
3: liberando ventanilla.
4: liberando ventanilla.
5: liberando ventanilla.
6: buscando ventanilla libre.
6: atendido en ventanilla en 0.
6: liberando ventanilla.
Fin.
6.3. Soluciones.
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/types.h>
#define IPC_WAIT 0
#define CLAVE 42
union senum {
int val;
};
int numsem;
60
int semid;
int r;
// Indice del semŽáforo introducido por teclado
int semindx;
// Valor a sumar introducido por teclado
int valor;
// Operaciones con semŽáforos
struct sembuf op;
union senum extra;
int monitor = 0;
key_t clave;
} else {
if (strcmp("-m", argv[1]) !=0) {
numsem = atoi(argv[1]);
printf("Creando %i semaforos.\n", numsem);
}
else {
monitor = 1;
printf("Monitorizando.\n");
}
}
61
semindx = 0;
// Realizar operaciones
do {
printf("[Indice semáforo - Negativo terminar] >> ");
scanf("%i", &semindx);
printf("[Valor a añadir - + sube / - baja] >> ");
scanf("%i", &valor);
return 0;
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/types.h>
#define CLAVE 42
union senum {
int val;
};
void inicializar(int);
int semid;
int r;
int monitor = 0;
int c;
key_t clave;
62
// Comprobar lŽmnea de comandos
if (argc != 2) {
// Cambiar esto
printf("Uso: %s [-c -r -l -d] \n", argv[0]);
printf(" -c Crea el grupo de semaforos y lo inicializa \n", argv[0]);
printf(" -r Reinicia el grupo de semaforos. \n");
printf(" -l Muestra los valores de los semaforos \n");
printf(" -r Borra el grupo de semaforos. \n\n");
return -1;
// Crear la clave
clave = ftok("/etc/passwd", CLAVE);
if (clave == -1) {
perror("Error calculando clave del grupo de semaforos.");
return -1;
}
// Comprobamos el comando
63
r = semctl(semid, 0, IPC_RMID);
if (r == -1) {
perror("Error destruyedo grupo.");
} else {
printf("Grupo destruido.\n");
}
}
return 0;
extra.val = 3;
r = semctl(id, 1, SETVAL, extra);
if (r == -1)
perror("Error inicializando semaforo.");
union senum {
int val;
};
int main() {
pid_t pid;
int c, r;
int num;
int sem_prod, sem_cons;
struct sembuf arriba = {0,1,0};
struct sembuf abajo = {0,-1,0};
union senum arg;
arg.val = 1;
r = semctl(sem_prod, 0, SETVAL, arg);
if (r == -1) {
perror("Error incializando el semáforo del productor - ");
//Borrar los semáforos
return -1;
}
64
}
if (pid == 0) {
// Impares
if (c==0) {
for (num = 1; num < 6; num+= 2) {
// Mi turno de producir
r = semop(sem_prod, &abajo, 1);
if (r == -1) {
perror("Error bajando el semáforo del productor - ");
// Eliminar semáforos
return -1;
}
// El turno de consumir
r = semop(sem_cons, &arriba, 1);
if (r == -1) {
perror("Error subiendo el semáforo del consumidor - ");
// Eso
return -1;
}
}
exit(0);
}
// Pares
if (c==1) {
for (num = 2; num < 7; num+=) {
// Voy a consumir 5 números
r = semop(sem_cons, &abajo, 1);
if (r == -1) {
perror("Error bajando semáforo del consumidor - ");
return -1;
}
// Hora de producir
r = semop(sem_prod, &arriba, 1);
if (r == -1) {
perror("Error subiendo semáforo del productos - ");
return -1;
}
}
exit(0);
}
}
}
pid = wait(&r);
r = semctl(sem_prod, 0, IPC_RMID);
if (r==-1) {
perror("Error eliminando semáforo productor.");
}
r = semctl(sem_cons, 0, IPC_RMID);
if (r == -1) {
perror("Error eliminando semáforo consumidor. ");
}
65
printf("Fin.\n");
return 0;
}
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/types.h>
#define N_MALETAS 4
#define N_TECNICOS 3
#define T_ACARREO 1
#define SEM_BODEGA 0
#define SEM_CARRO 1
union senum {
int val;
};
int main() {
int sem;
int r;
int c, quiensoy;
pid_t pid;
union senum arg;
struct sembuf arriba = {0, 1, 0};
struct sembuf abajo = {0, -1, 0};
arg.val = 1;
r = semctl(sem, SEM_BODEGA, SETVAL, arg);
if (r == -1)
perror("Error inicializando semáforo de la bodega.");
arg.val = 1;
r = semctl(sem, SEM_CARRO, SETVAL, arg);
if (r == -1)
perror("Error inicializando semáforo del carro.");
if (pid == 0) {
quiensoy = c;
66
for (c = 0; c < N_MALETAS; c++) {
printf("%i: Accediendo a bodega por maleta %i.\n", quiensoy, c);
abajo.sem_num = SEM_BODEGA;
r = semop(sem, &abajo, 1);
if (r == -1)
perror("Error bajando semáforo de la bodega.");
arriba.sem_num = SEM_BODEGA;
r = semop(sem, &arriba, 1);
if (r == -1)
perror("Error subiendo semáforo de la bodega.");
sleep(T_ACARREO);
abajo.sem_num = SEM_CARRO;
r = semop(sem, &abajo, 1);
if (r == -1)
perror("Error bajando semáforo del carro.");
arriba.sem_num = SEM_CARRO;
r = semop(sem, &arriba, 1);
if (r == -1)
perror("Error subiendo semáforo del carro.");
if (c < (N_MALETAS-1))
printf("%i: Va a por otra maleta.\n", quiensoy);
}
exit(0);
}
}
pid = wait(&r);
while ( (pid !=-1) ||
(pid == -1 && errno == EINTR) )
pid = wait(&r);
r = semctl(sem, 0, IPC_RMID);
if (r == -1)
perror("Error eliminando semáforos.");
printf("Fin.\n");
return 1;
}
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
67
#include <sys/types.h>
#include "sv.h"
#define MAX_CAJAS 30
#define PROD 8
union senum {
int val;
};
int main() {
pid_t pid;
int c, r;
int num;
int sem_prod, sem_cons, sem_exc;
struct sembuf arriba = {0,1,0};
struct sembuf abajo = {0,-1,0};
struct sembuf espera_cero = {0, 0, 0};
struct sembuf abajo_prod = {0, 0, 0};
struct sembuf arriba_prod= {0, 15, 0};
union senum arg;
sv_init();
sem_prod=semget(IPC_PRIVATE,1,IPC_CREAT| IPC_EXCL | S_IRUSR | S_IWUSR);
sem_cons=semget(IPC_PRIVATE,1,IPC_CREAT| IPC_EXCL | S_IRUSR | S_IWUSR);
sem_exc=semget(IPC_PRIVATE,1,IPC_CREAT| IPC_EXCL | S_IRUSR | S_IWUSR);
arg.val = 0;
r = semctl(sem_cons, 0, SETVAL, arg);
if (r == -1) {
perror("Error incializando el semáforo del consumidor ");
//Borrar los semáforos
sv_finish();
return -1;
}
arg.val = 1;
r = semctl(sem_exc, 0, SETVAL, arg);
if (r == -1) {
perror("Error inicializando el semáfoto de exclusión mútua");
// Borrar lo semáforos
sv_finish();
return -1;
}
arg.val = 15;
r = semctl(sem_prod, 0, SETVAL, arg);
if (r == -1)
perror("Error inicializando el semáfoto de los productores.");
if (pid == 0) {
68
// Productores
if (c!=0) {
//if (c == 1) sleep(1);
for (num = 0; num < MAX_CAJAS; num++) {
// Mi turno de producir
r = semop(sem_prod, &abajo, 1);
if (r == -1)
perror("Error bajando sem. productor.");
sv_inc(1);
printf("Productor: %i. Cajas: %i de %i\n", c, sv_val(), num+1);
if (sv_val() == 15) {
printf("Productor: %i. llamando a consumidor.\n", c);
if (semctl(sem_prod, 0, GETVAL) != 0) {
printf("Productor: %i no es el último !!!. \n", c);
}
} // for
exit(0);
}
// Consumidor
if (c==0) {
for (num = 0; num < (MAX_CAJAS / 15)*3; num++) {
69
r = semop(sem_prod, &arriba_prod, 1);
if (r == -1)
perror("Error subiendo sem. productores.");
}
exit(0);
}
}
}
pid = wait(&r);
r = semctl(sem_exc, 0, IPC_RMID);
if (r==-1) {
perror("Error eliminando semáforo exclusion mutua.");
}
r = semctl(sem_cons, 0, IPC_RMID);
if (r == -1) {
perror("Error eliminando semáforo consumidor. ");
}
r = semctl(sem_prod, 0, IPC_RMID);
sv_finish();
printf("Fin.\n");
return 0;
}
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/types.h>
#define VISITANTES 9
#define ESPERA 4
#define EXPOSICION 2
#define VISITA 2
int quiensoy;
union semun {
70
int val;
};
int main() {
pid_t pid;
int sem_espera, sem_exposicion;
int r;
int i;
union semun arg;
struct sembuf arriba = {0, 1, 0};
struct sembuf abajo = {0, -1, 0};
sem_exposicion=semget(IPC_PRIVATE,1,IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);
if (sem_exposicion == -1) {
perror("Imposible crear semaforo de la sala de exposicion. ");
return -1;
}
arg.val = ESPERA;
r = semctl(sem_espera, 0, SETVAL, arg);
if (r == -1) {
perror("Error inicializando semaforo de la sala de espera.");
return -1;
}
arg.val = EXPOSICION;
r = semctl(sem_exposicion, 0, SETVAL, arg);
if (r == -1) {
perror("Error inicializando semaforo de la sala de exposicion.");
return -1;
}
if (pid == 0) {
quiensoy = i;
71
if (r == -1) {
perror("Error subiendo semaforo de la sala de espera.");
}
exit(0);
}
} // For
pid = wait(NULL);
while ( (pid != -1) ||
( (pid == -1) && (errno ==EINTR)))
pid = wait(NULL);
r = semctl(sem_espera, 0, IPC_RMID);
if ( r==-1) {
perror("Error al eliminar el semaforo de la sala de espera.");
}
r = semctl(sem_exposicion, 0, IPC_RMID);
if ( r==-1) {
perror("Error al eliminar el semaforo de la sala de exposicion.");
}
printf("Fin\n");
return 0;
}
6.7. El banco I.
#include <errno.h>
#include <unistd.h>
#include <wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/types.h>
#define VENTANILLAS 3
#define CLIENTES 7
#define T_ATENCION 2
72
int main() {
int r, i;
int id;
pid_t pid;
int sem_pool, sem_prts;
arg.val = VENTANILLAS;
r = semctl(sem_pool, 0, SETVAL, arg);
if (r == -1)
perror("Error inicializando semáforo de la cola.");
arg.val = 1;
for (i =0 ; i < VENTANILLAS; i++) {
r = semctl(sem_prts, i, SETVAL, arg);
if (r == -1)
perror("Error inicializando semáforos de la simpresoras.");
}
if (pid == 0) {
id = i;
printf("%i: solicitando acceso a las ventanillas.\n", id);
73
if (r == -1) {
perror("Error subiendo semáforo de cola.");
}
arriba.sem_num =0;
break;
}
}
exit(0);
}
} // For
pid = wait(&r);
while ( (pid !=-1) ||
(pid == -1 && errno ==EINTR) )
pid = wait(&r);
semctl(sem_pool, 0, IPC_RMID);
semctl(sem_prts, 0, IPC_RMID);
printf("Fin.\n");
}
74
Apéndices.
75
Apéndice A. Manejo de GDB
#include <stdio.h>
int main() {
int v = 0;
int i;
for (i = 0; i < 5; i++) {
v += i;
}
printf("Resultado: %i\n", v);
return 0;
}
76
Lo primero que hemos de hacer es compilar dicho programa con la opción "-g"
para que se incluya la información necesaria para la depuración. Si el programa está
guardado en un archivo
(gdb)
La línea "(gdb)" nos indica que el depurador está listo para empezar a procesar
nuestras órdenes. El primer comando que veremos es (l)ist. Este comando muestra el
código fuente del programa que estoy depurando.
(gdb) l
1 #include <stdio.h>
3 int main() {
5 int v = 0;
6 int i;
9 v += i;
10 }
(gdb)
77
Para ejecutar el programa, utilizamos el comando (r)un. Como aún no hemos
puesto ningún punto de ruptura, el programa se ejecutará entero.
(gdb) r
Resultado: 10
(gdb)
(gdb)
9 v += i;
(gdb)
Continuing.
9 v += i;
(gdb) c
Continuing.
9 v += i;
78
(gdb) c
Continuing.
9 v += i;
(gdb) c
Continuing.
9 v += i;
(gdb) c
Continuing.
Resultado: 10
(gdb)
9 v += i;
(gdb) n
(gdb) n
9 v += i;
(gdb)
$1 = 1
79
(gdb) c
Continuing.
9 v += i;
(gdb) c
Continuing.
9 v += i;
(gdb) p v
$2 = 6
(gdb)
(gdb)
Hay mucha más información disponible tanto en la página del manual como en
los archivos info:
$ man gdb
$ info gdb
80
Apéndice B. Referencia rápida de Emacs.
^ significa Ctrl.
Matar buffer: ^x k
Deshacer: ^_ o ^x u
Borrar línea: ^k
Ir a fin de línea: ^e
Selecionar todo: ^x h
Intercambia líneas: ^x^t
81