Ejemplos de sincronización:
Proceso 1 Proceso 2 ========= ========= A C punto de punto de sincronización sincronización B DTodos los procesos que queramos sincronizar tendrán un punto de sincronización. En el segundo esquema que se ve 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 Dse 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 Dpero 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 de las asignatura de sistemas operativos ya sabemos que se producen muchas acciones antes de que la operación se lleve a cabo:
ksh
, por ejemplo) interpreta
los caracteres
que hemos tecleado. Deduce que hay una tubería
(|
)
fork
s
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 open
s 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
printf
s 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,
solo 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
cmd
s 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 solo 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