Ejemplos de sincronización:
Proceso 1 Proceso 2
========= =========
A C
punto de punto de
sincronización sincronización
B D
Todos los procesos que queramos sincronizar tendrán un punto de
sincronización. En el segundo esquema que vimos en teoría
(cliente-servidor), el
proceso 2 no puede ejecutar D sin que el proceso 1 haya ejecutado
completamente A. Si el proceso 2 acaba antes de hacer C, se debe
quedar bloqueado esperando.
Proceso 1 Proceso 2
========= =========
A C
punto de punto de
sincronización sincronización
B D
se puede solucionar haciendo que A y D formen una zona
de exclusión mutua accesible por un solo proceso y
regulada por un semáforo.
Proceso 1 Proceso 2
========= =========
A C
punto de pause();
sincronización
B D
pero entonces puede ocurrir que el proceso 1 llegue al
punto de sincronización mientras el proceso 2 esté
ejecutando C y el proceso 2 luego se quedaría bloqueado
para siempre, pues la señal ya fue enviada. Es, por lo
tanto, un método desaconsejable.
ls -l | morepara listar el contenido de un directorio página a página, a estas alturas del Laboratorio de Sistemas Operativos ya sabemos que se producen muchas acciones antes de que la operación se lleve a cabo:
ksh) interpreta los caracteres
que hemos tecleado. Deduce que hay una tubería
(|).
forks.
dup2.
exec para ejecutar
un ls y un more,
respectivamente.
Proceso 1 Proceso 2
========= =========
fd=open("tubo",O_WRONLY); fd=open("tubo",O_RDONLY);
write(fd,"Hola",5); read(fd,buffer,5);
[...] [...]
Si el primer proceso llega, por la circunstancia del reparto
de la CPU a ejecutar el write antes de que al
segundo proceso le haya dado tiempo a abrir la tubería, el
primer proceso recibe una señal SIGPIPE
fulminante. Es por ello, que los opens de
tuberías con nombre están sincronizados. Ningún proceso continúa
hasta que haya un proceso para leer de la tubería y
otro para escribir en ella.
O_RDWR) no se bloqueará nunca.
1) en la pantalla. El
proceso hijo imprime cien ceros (0) por la
pantalla. Sincronizar mediante semáforo(s) los dos procesos
para que en la pantalla aparezca:
101010101010101010101010101010101010...NOTA: Tened precaución en hacer una llamada a la función de biblioteca
fflush depués de hacer los
printfs porque si no, los caracteres no salen
inmediatamente por pantalla sino que quedan almacenados en
un búfer intermedio. Alternativamente, podéis usar
write.
int variable;
[...]
variable=7;
swich (fork())
{case -1: /* Error */
[...]
case 0: /* Hijo */
variable=8;
exit(0);}
/* Aquí sigue el padre */
printf("El valor de la variable es %d.\n", variable);
[...]
jamás aparecerá por la pantalla un valor
de 8 para la variable, siempre 7, incluso si al hijo le diera
tiempo a cambiar su valor antes de que el padre ejecutara
el printf. Las zonas de memoria del padre y
el hijo se separaron después del fork
(aunque esto no es completamente cierto si se usa
copy on write) y cada uno tiene sus propias variables.
Si deseamos que el padre y el hijo compartan una variable, tenemos
que usar memoria compartida.
int *pvariable;
[...]
pvariable=compartir_malloc(sizeof(int));
[...]
*pvariable=7;
swich (fork())
{case -1: /* Error */
[...]
case 0: /* Hijo */
*pvariable=8;
exit(0);}
/* Aquí sigue el padre */
printf("El valor de la variable es %d.\n", *pvariable);
[...]
int shmget(key_t clave, size_t tamaNo, int shmflg);
Los parámetros y valor devuelto son como en
semget. La excepción es el
parámetro tamaNo, donde se especifica el tamaño
de la zona de memoria que queremos reservar. Sería el equivalente
al parámetro de la función de biblioteca malloc
para memoria dinámica, sólo que ahora se trata de memoria
compartida.
shmctl podemos realizar operaciones sobre el
segmento de memoria compartida que hemos definido:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
Las operaciones que podemos realizar van desde
consultar/modificar las propiedades del segmento hasta eliminarlo.
Además de estas, se puede bloquear un segmento de memoria
compartida (impedir que se realice intercambio con memoria
secundaria sobre el) y luego desbloquearlo con
los cmds SHM_LOCK y
SHM_UNLOCK. Ver llamada parecida:
semctl.
ipcs -m e ipcrm -m número.
void *shmat(int shmid, void *shmaddr, int shmflg);
Hay que pasarle el identificador del segmento de memoria
compartida como primer parámetro (nos lo dio
shmget). En el segundo parámetro basta con
poner un cero. En el tercer parámetro puede especificarse
la macro SHM_RDONLY si queremos que el segmento
sea de sólo lectura.
int shmdt(void *shmaddr);
En el parámetro se le pasa el puntero que nos devolvió
shmat.
nanosleep para controlar
esto (mirad la página de manual)).
El hijo, por su parte consumirá los caracteres según le
vayan llegando, con un gasto de CPU mínimo. Para consumir un
carácter, lo que hace es leerlo. A los caracteres que le llegan
en una posición par (segundo, cuarto, etc.) los imprime por
la pantalla tal cual. A los que le llegan en una posición
impar, los transforma en mayúsculas y los imprime por pantalla.
Para trasformar en mayúsculas usad la función de biblioteca
toupper.
Haced un único programa fuente (.c).

nanosleep para dormir durante una fracción
de segundo. Se llama usleep. Podéis usarla
en esta práctica.
ipcs
ipcrm
fflush
toupper