batracios.c
, cuya adecuada compilación producirá
el ejecutable batracios
. Respetad las
mayúsculas/minúsculas de los nombres.
libbatracios.a
)
que debéis enlazar con vuestro módulo objeto para generar el
ejecutable. Gracias a ella, algunas de las funciones necesarias
para realizar la práctica no las tendréis
que programar sino que bastará nada más con incluir
la biblioteca cuando compiléis el programa. La línea de
compilación del programa podría ser:
gcc batracios.c libbatracios.a -o batracios -lmDisponéis, además, de un fichero de cabeceras,
batracios.h
, donde se encuentran definidas, entre
otras cosas, las macros que usa la biblioteca y las cabeceras
de las funciones que ofrece.
BATR_inicio
con los parámetros adecuados.
El proceso
será responsable de crear los procesos adicionales
necesarios, salvo los procesos que representan a las ranitas,
que son hijos del proceso que maneja a la rana madre
correspondiente.
En ningún caso, puede la práctica mantener en ejecución simultánea
más de 30 procesos.
Las cuatro ranas madres procreadoras y cada una de la
pequeñas
ranitas que nazcan, serán representadas, por lo tanto,
mediante un proceso.
También es responsabilidad del primer proceso el
controlar que,
si se pulsa CTRL+C, la práctica acaba, no dejando procesos en
ejecución ni recursos IPCs sin borrar.
La práctica devolverá 0 en caso de ejecución satisfactoria
o un número mayor que cero, en caso de detectarse un error.
libbatracios.a
son las que a continuación aparecen.
De no indicarse nada, las funciones devuelven -1 en caso de
error o, si siendo funciones booleanas, el resultado es falso.
Las funciones devuelven 0 en caso contrario:
int BATR_inicio(int ret, int semAforos,
int lTroncos[],int lAguas[],int dirs[],
int tCriar, char *zona)
ret
la velocidad de presentación
y en tCriar
el
tiempo medio entre partos de una rana madre
(parámetros ambos de la línea de órdenes)
y pasando además el identificador del conjunto de
semáforos que se usará y el puntero a la zona
de memoria compartida declarada
para que la biblioteca pueda usarlos.
El significado del resto de parámetros es el siguiente:
lTroncos
: array de siete enteros
que contiene el valor de la longitud media de
los troncos para cada fila. El índice cero del
array se refiere a la fila superior de troncos. lAguas
: igual que el parámetro
anterior, pero referido a la longitud media
del espacio entre troncos. dirs
: lo mismo que los dos parámetros
anteriores, pero en esta ocasión cada elemento
puede valer DERECHA
(0) o
IZQUIERDA
(1), indicando la dirección
en que se moverán los troncos. int BATR_avance_troncos(int fila)
void BATR_descansar_criar(void)
int BATR_parto_ranas(int i,int *dx,int *dy)
dx
y
dy
, se devuelve la posición donde
nace la rana. La rana madre, debe crear un
nuevo proceso (o esperarse si ahora mismo hay
el máximo) para que se haga cargo de la nueva
rana int BATR_puedo_saltar(int x,int y,int direcciOn)
x
e y
pregunta con esta función si puede avanzar en la
dirección (DERECHA
, IZQUIERDA
o ARRIBA
). Devuelve 0 si la rana
puede saltar int BATR_explotar(int x,int y)
int BATR_avance_rana_ini(int x,int y)
,
int BATR_avance_rana(int *x,int *y,int direcciOn)
y int BATR_avance_rana_fin(int x,int y)
Una vez la rana sabe que puede avanzar, llama a estas
tres funciones. Los parámetros son de significado
evidente. No obstante, fijaos en que la segunda
función recibe la posición pasada por referencia,
de modo que, una vez realizado el avance, las nuevas
coordenadas aparecen en las variables pasadas.
Esas mismas nuevas coordenadas, se pasan a la última
función int BATR_pausa(void)
e
int BATR_pausita(void)
int BATR_comprobar_estadIsticas(int r_nacidas, int r_salvadas, int r_perdidas)
int BATR_fin(void)
BATR_avance_rana_ini
, luego a
BATR_avance_rana
, ambas con la posición donde
se encuentra la rana. Al retornar de BATR_avance_rana
,
en las variables de posición ya se encontrará la nueva posición
de la rana. Hay que llamar a la función BATR_pausa
y, finalmente, a BATR_avance_rana_fin
, con la
nueva posición de la rana. BATR_inicio
. BATR_comprobar_estadIsticas
BATR_descansar_criar
BATR_parto_ranas
y crea un nuevo proceso para que se encargue de la
rana recién nacida sleep()
, se refiere a la llamada al sistema,
no a la orden de la línea de órdenes.
libbatracios.a
libbatracios.a
y el fichero de cabeceras batracios.h
. La biblioteca
funciona con los códigos de VT100/xterm, por lo que debéis
adecuar vuestros simuladores a este terminal. También
se usa la codificación UTF-8, por lo que necesitáis
un programa de terminal que sepa interpretarlos. Los terminales
de Linux lo hacen por defecto, pero si usáis Windows,
debéis aseguraros de que el programa tiene capacidad para
interpretarlos y que esta capacidad está activada. Si no
es así notaréis caracteres basura en la salida de
modo que no se verá nada. Es, además, conveniente que
pongáis el color de fondo de la pantalla a negro y su tamaño,
al menos, a 80x25 caracteres.
libcruce.a
:
para Solaris (ver 0.3),
para el LINUX de
clase (ver 0.3), batracios.h
:
Para todos (ver 0.3). ipcs
.
Es preferible, para que no haya interferencias, que
los defináis privados. BATR_inicio
en
main
. Debe aparecer la pantalla de bienvenida
y, pasados dos segundos, dibujarse la pantalla.
Añadid también la función
BATR_fin
SEMáFOROS Y VALOR INICIAL: SC=1, SV=0. SEUDOCĂ“DIGO: C V === === Por_siempre_jamás Por _siempre_jamás { { W(SC) W(SV) escribir_consonante escribir_vocal S(SV) S(SC) } }Daos cuenta que lo que importa en el pseudocódigo es la sincronización. El resto puede ir muy esquemático. Un buen esquema os facilitará muchísimo la defensa.
BATR_explotar
para eliminar esa rana. En ese
caso, la cuenta de ranas perdidas, se incrementa.
gcc
que os genere código de 32 bits para que vaya bien
con la biblioteca. Añadid -m32
en la
línea de compilación para lograrlo. Si os da
problemas al ejecutar el código es que necesitáis
instalar las bibliotecas de compatibilidad de 32 bits.
Dependiendo de la distribución se tratará de
un paquete u otro. Por ejemplo, para la familia
de Debian, se trata de un paquete llamado
ia32-libs
. SIGINT
a los procesos que estén
en primer plano. Para probarlo, mandad el proceso
a primer plano con fg %
y pulsad
entonces CTRL+C. int semAforo=-1
. En la
manejadora de SIGINT
, sólo si
semAforo
vale distinto de -1,
elimináis el recurso con semctl
.
Esto es lógico: si vale -1 es porque no se ha
creado todavía o porque al intentar crearlo
la llamada al sistema devolvió error. En ambos casos,
no hay que eliminar el recurso. SIGINT
, podéis declarar una
estructura que los contenga a todos y así
sólo gastáis un identificador del espacio
de nombres globales. wait
.
A vosotros os pasa con semop
, pero es
lo mismo. De las dos soluciones que propone el
apartado, debéis usar la segunda. semctl
de
Solaris (con union semun
), como
se explica en la sesión de semáforos y no la de
HPUX. Invalid argument
", pero, sin embargo,
se borren bien. La razón de esto es que habéis
registrado la manejadora de SIGINT
para
todos los procesos. Al pulsar CTRL+C, la señal la
reciben todos, el padre y los otros procesos. El primero
que obtiene la CPU salta a su manejadora y borra los
recursos. Cuando saltan los demás, intentan borrarlos,
pero como ya están borrados, os da el error. Bus Error. Core dumped
si no definís el
puntero a esa variable apuntando a una dirección que
sea múltiplo de cuatro. El puntero que os devuelve
shmat
, no obstante, siempre será una
dirección múltiplo de cuatro, por lo que solo os
tenéis que preocupar con que la dirección sea
múltiplo de
cuatro respecto al origen de la memoria compartida.
La razón se escapa un poco al nivel de este curso y
tiene que ver con el alineamiento de direcciones de
memoria en las instrucciones de acceso de palabras en
el procesador RISC de encina. Segmentation fault. Core dumped
",
la línea os la dará si aplicáis
lo que aparece en la
sección Manejo del depurador.
En cualquier otro caso, no os quedará más remedio que
depurar mediante órdenes de impresión dentro del
código.
fprintf(stderr,"...",...);donde sospechéis que hay problemas. En esas líneas identificad siempre al proceso que imprime el mensaje. Comprobad todas las hipótesis, hasta las más evidentes. Cuando ejecutéis la práctica, redirigid el canal de errores a un fichero con
2>salida
.
grep "P1" salida > salida2
Resource
temporarily unavailable
en el fork
del padre. Esto ocurre cuando no exorcizáis
adecuadamente a los procesos hijos zombies del padre.
Hay dos posibilidades para solucionarlo:
SIGCLD
con un sigaction
y
SIG_IGN
. El S.O. tradicionalmente
interpreta esto como que no queréis que
los hijos se queden zombies, por lo que no
tenéis que hacer waits sobre ninguno de
ellos para que acaben de morir Interrupted system call
en el perror. Como podéis suponer,
eso no es un error y debéis interceptarlo
para que no ponga el perror y reintente el WAIT.
La clave está en la variable
errno
que valdrá
EINTR
en esos casos. sleep
s
o pausa
s) dentro de una sección
crítica. El efecto que se nota es que, aunque
la práctica no falla, parece como si solamente
un proceso se moviera o apareciera en la pantalla a la
vez. Siendo más precisos, si dormís dentro
de la sección crítica, y soltáis
el semáforo para, acto seguido, volverlo a coger,
dais muy pocas posibilidades al resto de procesos de que
puedan acceder.BATR_comprobar_estadIsticas
, puede
ser por lo siguiente: casi todas las funciones
de la biblioteca están sincronizadas
mediante el semáforo que se pasa a la
biblioteca. Ese semáforo se usa para construir
una sección crítica de modo que
la salida por la pantalla no se embarulle.
Ocurre que, cuando se recibe SIGINT, puede que
a algún proceso, sobre todo a máxima
velocidad, le pille dentro de una
función de la biblioteca con el semáforo
cogido. El proceso morirá y el semáforo
se queda a cero. Cuando el padre llama a la
función para comprobar las estadísticas,
intenta hacer un wait sobre el semáforo y se
queda bloqueado para siempre. La solución pasa
por, o bien bloquear SIGINT en los sitios conflictivos
o bien que el padre, una vez se asegure de que todos
los demás procesos están muertos,
compruebe el valor del semáforo de la biblioteca
y, si vale cero, hacerle un signal.char
para las cuentas. Un char
se desborda
al llegar a 127. Debéis usar punteros de
tipo int
y reservar cuatro bytes
(mejor sería sizeof(int)
bytes)
de memoria compartida para cada uno de ellos.