filosofar.c
, cuya adecuada compilación producirá
el ejecutable filosofar
. Respetad las
mayúsculas/minúsculas de los nombres.
libfilosofar.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 filosofar.c libfilosofar.a -o filosofar -lmDisponéis, además, de un fichero de cabeceras,
filosofar.h
, donde se encuentran definidas, entre
otras cosas, las macros que usa la biblioteca.
MAXFILOSOFOS
de filosofar.h
), el número de
vueltas que debe dar cada filósofo antes de morir y la lentitud de
ejecución, un número mayor o igual que cero. Este último
valor se ha de pasar a la biblioteca para que ella gestione la velocidad
de la simulación.
FI_getNSemAforos
acerca de cuántos
semáforos necesita para su funcionamiento interno. Se
reservarán los primeros semáforos del conjunto
para la bibloteca, pudiendo el programador solicitar y usar
más para realizar la correcta sincronización.
FI_getTamaNoMemoriaCompartida
, la biblioteca
declara cuánta memoria necesita para su uso. Se puede
reservar una cantidad mayor, pero se debe dejar los primeros
bytes para uso exclusivo de la biblioteca.FI_inicio
.
Se debe usar la clave: 41211294392005
.FI_fin
.FI_inicioFilOsofo
.
Cada proceso debe llamar a la función con un identificador
diferente (un número entre 0 y el número de
filósofos menos 1), aunque no es necesario que los
filósofos llamen a esta función en orden.CAMPO
.PUENTE
.ANTESALA
.ANTESALA
.FI_entrarAlComedor
,
indicando el sitio libre escogido.ENTRADACOMEDOR
.FI_cogerTenedor
dos veces.
Solo se puede llamar a estas funciones cuando el tenedor a que
se refieran esté libre.FI_comer
mientras
devuelva SILLACOMEDOR
.FI_dejarTenedor
por cada uno de ellos.PUENTE
.TEMPLO
.FI_entrarAlTemplo
señalando el sitio
elegido.TEMPLO
.FI_meditar
mientras
esta devuelva SITIOTEMPLO
.FI_finFilOsofo
.FI_pausaAndar
.
Esta llamada se debe hacer fuera de cualquier sección
crítica u observaremos un enlentecimiento artificial de
la práctica a baja velocidad que aumenta con el número
de procesos, llegando incluso a bloquearse intermitentemente.FI_puedoAndar
.
Esta función devuelve el valor 100 si la vía está
libre. Si no lo está, devuelve el identificador del
filósofo que está bloqueando nuestro camino.FI_andar
.top
.libfilosofar.a
son las que a continuación aparecen.
De no indicarse nada, las funciones devuelven -1 en caso de
error:
int FI_inicio(int ret, unsigned long long clave,
struct DatosSimulaciOn *ddssp, int semAforos, int memoCompartida,
int const *dE)
ret
: lentitud de ejecución. Es el valor
que se ha pasado como primer argumento al programa.clave
: en esta clave van codificados los
datos concretos de la simulación. Más
arriba se especificó cuál es el valor
que se debe introducir.ddssp
: se debe pasar por aquí una
variable de tipo struct DatosSimulaciOn
donde la función devuelve los datos concretos
asociados a la clave introducida. La definición de
struct DatosSimulaciOn
se encuentra en
filosofar.h
.semAforos
: identificador del
array de semáforos devuelto por
semget
.zona
: idem para la zona de
memoria compartida (no es el puntero, sino lo
que devuelve shmget
).dE
: puede ser NULL
, en cuyo caso
no se desea que la biblioteca produzca información de
depuración por el canal de errores estándar.
Si se pasa un array, los filósofos indicarán
en qué dirección se desplazan y, adicionalmente,
cada elemento del array señala si se desea
información o no de cada uno de los subestados por los
que pasa un filósofo:
// Sustituir por 1, si se desea depurar sin detallar cada avance del // filOsofo o con // -1, si no se desea depurar pero sin este detalle int dE[]={0, // Cigoto, 0, // PasilloSalidaTemplo, 0, // CaminoTemploPuente, 0, // PuenteHaciaComedor, 0, // CaminoPuenteComedor, 0, // PasilloEntradaComedor, 0, // Antesala, 0, // EntradaComedor, 0, // SillaComedor, 0, // SalidaComedor, 0, // PasilloSalidaComedor, 0, // ExplanadaComedorPuente, 0, // PuenteHaciaTemplo, 0, // CaminoPuenteTemplo, 0, // PasilloEntradaTemplo, 0, // EntradaTemplo, 0, // SitioTemplo, 0, // SalidaTemplo, 0 // Fin };
int FI_inicioFilOsofo(int nFilOsofo)
int FI_pausaAndar(void)
int FI_puedoAndar(void)
100
si es seguro andar o el identificador
del filósofo que nos impide el paso. Nótese que,
si no se toman medidas, puede ocurrir que entre que se llama a
esta función y se llama a la siguiente, la situación
puede haber cambiado.int FI_andar(void)
filosofar.h
int FI_entrarAlComedor(int puesto)
int FI_cogerTenedor(int t)
TENEDORDERECHO
o TENEDORIZQUIERDO
,
macros definidas en filosofar.h
.int FI_comer(void)
SILLACOMEDOR
, macro de
filosofar.h
.int FI_dejarTenedor(int t)
FI_cogerTenedor
y
con el mismo parámetro. Se invoca para dejar cada tenedor,
una vez el filósofo ha comido.int FI_entrarAlTemplo(int puesto)
int FI_meditar(void)
SITIOTEMPLO
, macro de filosofar.h
.int FI_finFilOsofo(void)
int FI_fin(void)
int FI_getTamaNoMemoriaCompartida()
int FI_getNSemAforos()
void pon_error(char *mensaje)
sleep()
, se refiere a la llamada al sistema,
no a la orden de la línea de órdenes.
libfilosofar.a
libfilosofar.a
y el fichero de cabecera filosofar.h
. La biblioteca
funciona con los códigos de VT100/xterm, por lo que debéis adecuar
la emulación de vuestro terminal.
libfilosofar.a
:
para Solaris (ver 2.0),
para el LINUX de
clase (ver 2.0), filosofar.h
:
Para todos (ver 1.2).ipcs
.
Es preferible, para que no haya interferencias, que
los defináis privados.FI_inicio
en
main
. Debe aparecer la pantalla de bienvenida
y, pasados dos segundos, dibujarse la pantalla. Para
la clave, usad el número proporcionado más arriba.
Recoged los datos de simulación y mostradlos por el canal de
errores. Incluid la plantilla del array de depuración y
pasádselo a la función.
Ejecutad la práctica, a partir de ahora, redirigiendo la
salida de errores a un fichero para que no se os emborrone la
pantalla.FI_andar
para ir resolviendo el ciclo de
vida del filósofo. Como el filósofo está solo,
no hace falta que os preocupéis por los condicionantes del
puente, podéis elegir siempre el puesto 0 en el comedor y el
templo, etc.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 de 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.
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.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,"%d:...",getpid(),...);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