Para conseguir la documentaón de las practicas de esta asignatura,
hay que escribir una o varias páginas en html. Algunos de vosotros
quereis insertar algún gráfico, y dibujos. Para ello, yo suelo
utilizar alguna herramienta gratuita sobre Linux o Windows.
El segundo problema no es tal, pues ya manejamos punteros con soltura.
En cuanto al primero, hay que recurrir al interface de aplicación
de memoria compartida de un S.O. concreto.
El que utiliza UNIX es muy fácil de manejar (véase
un ejemplo).
Para ocultar algunos detalles
de implementación se utilizarán ciertas funciones que
facilitan la memoria compartida. El resultado viene a ser:
#include "rshmem.h"
int main() {
/* crear zona de memoria compartida */
if (!crearMemoria())
fprintf(stderr, "error de crearMemoria\n");
/* proceso padre */
if (0!=fork()) {
while (*memoria != 'x') ;
printf("he comprobado 'x' en memoria compartida\npulse una tecla");
getchar(); /* comprobar el semaforo con 'ipcs' */
/* eliminar memoria compartida */
if (!eliminarMemoria())
fprintf(stderr, "error de eliminarMemoria\n");
exit(0);
/* proceso hijo */
} else {
*memoria = 'x';
exit(0);
}
}
(cargar fuente)
Para compilar el programa debemos escribir:
cc -o ejemplo.c rshmem.c
Mediante estos programas de ejemplo se pueden ilustrar diversos problemas de acceso concurrente, e implantar alguna de las soluciones existentes, de las que se ilustra la primera:
A tal efecto hay que recordar que el comando "kill" viene muy bien al caso.
Sin embargo en el caso de utilizar memoria compartida, semaforos y colas de mensajes (ipc's todos ellos) la cosa es un poco diferente. Estos recursos, en manos del sistema, se registran en el nucleo cada vez que los creamos: Si el programa no se comporta bien y sale por peteneras, no se "des-registran", de modo que debemos hacerlo manualmente.
Para resolver este ligero problema incluyo un programita que permite deshacerse de la memoria compartida no deseada. Una vez compilado el programa hay que seguir los siguientes pasos:
Referencias: W. Richard Stevens "Advanced Programming in the Unix Environment" Addison-Wesley 1992
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #define ARRAY_SIZE 4000 #define MALLOC_SIZE 10000 #define SHM_SIZE 10000 #define SHM_MODE (SHM_R | SHM_W) /* read/write */ char array[ARRAY_SIZE]; /* datos sin inicializar = bss */ int main() { int shmid; char *ptr, *shmptr; printf("array[] desde %x hasta %x\n", &array[0], &array[ARRAY_SIZE]); printf("stack sobre %x\n", &;shmid); if ((ptr=malloc(MALLOC_SIZE)) == NULL) fprintf(stderr, "error de malloc()\n"); printf("malloc desde %x hasta %x\n", ptr, ptr+MALLOC_SIZE); if ((shmid=shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE))<0) fprintf(stderr, "error de shmget()\n"); if ((shmptr=shmat(shmid, 0, 0)) == (void *) -1) fprintf(stderr, "error de shmat()\n"); printf("shared memory desde %x hasta %x\n", shmptr, shmptr+SHM_SIZE); /* proceso padre */ if (0!=fork()) { while (*shmptr != 'x') ; printf("he comprobado 'x' en memoria compartida\npulse una tecla"); getchar(); /* comprobar el semaforo con 'ipcs' */ /* eliminar memoria compartida */ if (shmctl(shmid, IPC_RMID, 0) < 0) fprintf(stderr,"error de shmctl()\n"); exit(0); /* proceso hijo */ } else { *shmptr = 'x'; exit(0); } }(cargar fuente)
y el resultado de su ejecución será algo asi como:
array[] desde 40001220 hasta 400021c0 stack sobre 7b03a5e8 malloc desde 400041c8 hasta 400068d8 memoria compartida desde c06d4000 hasta c06d6710 he comprobado 'x' en memoria compartidavolver
Fichero de cabecera con definiciones
y declaraciones para usar memoria compartida.
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define ARRAY_SIZE 4000
#define MALLOC_SIZE 10000
#define SHM_SIZE 10000
#define SHM_MODE (SHM_R | SHM_W) /* read/write */
#define TRUE 1
#define FALSE 0
#ifdef RUTINAS_SHMEM
static int shmid; /* handler de memoria compartida */
char *memoria; /* puntero a zona de memoria compartida */
#else
extern char *memoria;
#endif
/* Prototipos de funciones de memoria compartida */
void origenTiempo();
void tiempoPasa();
int crearMemoria() ;
int eliminarMemoria() ;
#define TP tiempoPasa();
(cargar fuente)
volver
Fichero con funciones de creación
de memoria compartida y varias de utilidad.
#define RUTINAS_SHMEM
#include "rshmem.h"
/* Crea memoria compartida.
* - el manejador de memoria es interno
* - manda mensajes de error por salida de error estándar.
*/
int crearMemoria() {
char *funcName = "crearMemoria";
if ((shmid=shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE))<0) {
fprintf(stderr, "%s: error de shmget()\n", funcName);
} else if ((memoria=shmat(shmid, 0, 0)) == (void *) -1) {
fprintf(stderr, "%s: error de shmat()\n", funcName);
} else {
return TRUE;
}
return FALSE;
}
/* Destruye la memoria compartida creada por crearMemoria()
*/
int eliminarMemoria() {
char *funcName = "eliminarMemoria";
if (shmctl(shmid, IPC_RMID, 0) < 0) {
fprintf(stderr,"%s: error de shmctl()\n", funcName);
return FALSE ;
} else
return TRUE ;
}
/* Coloca una semilla en el temporizador del bucle de
* tiempoPasa()
*/
void origenTiempo(){
srand((unsigned int) time(NULL)) ;
}
/* Rutina que hace pasar un poco de tiempo con un bucle
* sencillo
*/
void tiempoPasa() {
unsigned int i;
int a=3;
/* Los parametros "50" y "2" dependen mucho de la velocidad
* de la computadora y de la configuracion del SO. Espero que
* funcionen bien
*/
for (i=rand()/50; i>0; i--)
a = a%3 + i;
}
(cargar fuente)
volver
#include "rshmem.h" void incrementa(int *mem, int k) { int i; i=*mem; TP i=i+k; TP *mem=i; } int main() { int *recurso; char *marcaFin; /* crear zona de memoria compartida */ if (!crearMemoria()) fprintf(stderr, "error de crearMemoria\n"); recurso = (int *) memoria ; marcaFin = memoria + sizeof(int) ; *recurso = 0 ; *marcaFin = 'p' ; if (0!=fork()) { /* proceso padre */ int i; for (i=0; i< 1000; i++) incrementa(recurso, -5); while (*marcaFin != 'x') ; /* espera al hijo */ printf("El recurso valia 0 y ahora vale %d\n", *recurso); if (!eliminarMemoria()) /* eliminar memoria compartida */ fprintf(stderr, "error de eliminarMemoria\n"); exit(0); } else { /* proceso hijo */ int i; for (i=0; i< 1000; i++) incrementa(recurso, 5); /* termina */ *marcaFin = 'x'; exit(0); } }(cargar fuente)
La función "incrementa()" implementa el acceso al recurso compartido. Se ha incluido ciertas llamadas al macro "TP" que implementan un retardo aleatorio con el fin de dar cierto tiempo de que ocurran interrupciones que puedan detener la ejecución del proceso en su "cuanto" de tiempo asignado por el SO. Esto da mayor realismo al procedimiento "incrementa()", que en el caso real puede ser más complejo y largo que en el caso actual.
Al compilar el programa y ejecutarlo varias veces nos encontramos con que el resultado de su ejecución varía, no siendo siempre 0, e ilustrando que las respectivas funciones "incrementa()", del proceso padre y del proceso hijo, entrelazan sus instrucciones, dando lugar a un resultado erróneo. volver
#include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/shm.h> #define ARRAY_SIZE 4000 #define MALLOC_SIZE 10000 #define SHM_SIZE 10000 #define SHM_MODE (SHM_R | SHM_W) /* read/write */ int main() { int shmid; char shmidStr[128]; printf("Que zona de memoria desea destruir? "); shmid = atoi(gets(shmidStr)); if (shmctl(shmid, IPC_RMID, 0))<0) fprintf(stderr, "error de shmctl()\n"); exit(0); }(cargar fuente)