[...] int n_hijo=1; [...] switch (fork()) {case -1: /* Error */ [...] case 0: /* Código del hijo */ printf("Soy el hijo %d.\n", n_hijo); n_hijo=n_hijo+1; /* Para el próximo que tenga mi padre... */ [...] exit(0); } /* El padre sigue por aquí y tiene otro hijo... */ switch (fork()) {case -1: /* Error */ [...] case 0: /* Código del hijo */ printf("Soy el hijo %d.\n", n_hijo); n_hijo=n_hijo+1; /* Para el próximo que tenga mi padre... */ [...] exit(0); }La variable que modifica el primer hijo,
n_hijo
,
ya pertenece a su propio espacio de direcciones, es decir,
la modificación del hijo no afecta a la variable del padre,
pues pertenecen a procesos diferentes.
Tuberías (pipes):
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/TUBOS$ /sbin/mknod tubo1 p <T>/usuarios/gyermo/PRIVADO/SO/PRACTS/TUBOS$ ls -l total 0 prw-r--r-- 1 gyermo profes 0 Mar 20 13:23 tubo1Hay que especificar la ruta completa de la orden
mknod
porque se encuentra en un directorio
de herramientas de administración del sistema
que no está
en la variable de entorno PATH
de los usuarios
normales.<ENCINA>/home/gyermo/PRIVADO/TUBOS$ mknod tubo1 p <ENCINA>/home/gyermo/PRIVADO/TUBOS$ ls -l total 0 prw-r--r-- 1 gyermo profes 0 mar 19 23:34 tubo1En este caso,
mknod
sí se encuentra en el
PATH
.mknod
para crear
la tubería. Su página de manual
se encuentra en la sección
1m (donde están las órdenes de
administración del sistema).
Depués de crear la tubería
con mknod
, aparece en el directorio como
un fichero más
sólo que ls -l
indica mediante
una p
que
se trata de una tubería. Se llama tubería con
nombre por eso, porque precisamente tenemos que darle un
nombre para que aparezca en el directorio.
cat < tubo1En la otra:
cat > tubo1A partir de esos momentos, todo lo que tecleéis en la segunda ventana aparecerá en la primera. Para acabar, podéis teclear en la segunda ventana el carácter de fin de fichero (
CTRL+D
).talk
.
Si desesperáis
y no lo conseguís, podéis ver cómo hacerlo
aquí.
mknod
que viene en el resumen.
open
.
La manera de crearlas y acceder a ellas es mediante la llamada
al sistema pipe
:
int pipe(int fildes[2]);Para usarla, se ha de declarar previamente una array de dos enteros. En dicho array depositará
pipe
dos
descriptores de fichero. El primero servirá para leer de
la tubería con read
. El segundo, para escribir
en la tubería con write
.
En el caso que nos resulte
más cómodo trabajar con las funciones de flujo
(stream)
como fprintf
o fscanf
, acudid a la
función de biblioteca fdopen
. Ejemplo del uso de
pipe
:
int tubos[2]; [...] if (pipe(tubos)==-1) {/* Error */ [...] } write(tubo[1],"Hola\n",5);
6 PASO DE MENSAJES.
El paso de mensajes es la herramienta básica de comunicación entre procesos. Un proceso puede mandar cualquier información a otro mediante este procedimiento.
El paso de mensajes se puede usar para la sincronización de procesos.
Ventajas del paso de mensajes:
Funciones (primitivas) relacionadas con el paso de mensajes:
Cómo concretar estas primitivas en un sistema dado son las cuestiones de diseño.
Direccionamiento:
msgget
:
int msgget(key_t clave, int msgflg);Los parámetros y el valor devuelto son equivalentes a los ya vistos para semáforos, con la excepción de que aquí solo se reserva un buzón y no un array de buzones. Podéis ver que el buzón se ha creado con éxito con la orden de la shell
ipcs -q
y
podéis eliminar un buzón creado por
vosotros con ipcrm -q número
, de modo
muy parecido a como hacíamos
con los semáforos.
El valor que nos devuelve msgget
será el identificador del buzón creado
y servirá para
hacer referencia a este buzón
desde otras llamadas al sistema.
Formato de los mensajes:
Normalmente, los datos del mensaje es información bruta.
struct tipo_mensaje {long tipo; /* Este campo es obligatorio */ char nombre[20]; int edad;};Mandar un mensaje al buzón cuyo identificador sea
buzOn
es ahora muy fácil:
struct tipo_mensaje m; [...] m.tipo=3; /* De tipo 3, por ejemplo */ sprintf(m.nombre,"Pascual"); m.edad=33; msgsnd(buzOn,&m,sizeof(m)-sizeof(long),0);La función
msgsnd
tiene el prototipo,
que podéis
consultar en el resumen, siguiente:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);Observad cómo, como pasa en otras llamadas al sistema, al tener un parámetro que es un puntero, a continuación aparece la longitud de la zona de memoria que se desea pasar. En este caso, curiosamente, no hay que incluir el tamaño del entero largo que especifica el tipo de mensaje.
msgrcv
, cuyo prototipo es:
int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);El primer parámetro es el identificador del buzón, justo como en
msgrcv
.
El segundo parámetro es un parámetro
de retorno de la función.
Ahí nos almacenará lo que se lea del
buzón.
El tercer parámetro es la longitud máxima del mensaje
que vamos a leer. En cuanto al cuarto parámetro, se trata del
tipo de mensaje que queremos leer. Si ponemos el tipo 0,
recibiremos el primer mensaje que haya en el buzón.
Si ponemos
un tipo negativo recibiremos el mensaje cuyo tipo sea menor
que el valor absoluto especificado y, a la vez, de todos los
que cumplan esa condición, el menor tipo posible.
Esta última
opción nos permitiría hacer
un esquema por prioridades en
la recepción de mensajes.
Respecto al último parámetro,
msgflg
será un
campo de bits que nos
permitirá especificar alguna opción.
En concreto, es interesante
la opción MSG_NOERROR
con la que, en el caso
de que el mensaje que haya en el buzón
no quepa en el búfer
que hemos reservado, no nos dé error
y meta en el búfer lo que quepa.
Sincronización:
IPC_NOWAIT
en el campo de opciones
de msgrcv
, lo que hará que se vuelva no
bloqueante en el sentido que vimos arriba. En el caso
de que no hay mensajes, devuelve un error.
msgsnd
es no bloqueante en el sentido
visto arriba, curiosamente msgsnd
se puede
bloquear. La causa del bloqueo, no obstante, no será
que el receptor no haya leído el mensaje, sino otra.
Una causa bastante común
es que el buzón esté lleno.
msgsnd
no se bloquee
nunca, especificaremos la macro IPC_NOWAIT
en las opciones de la llamada al sistema.
Como ocurría
con msgrcv
, si se fuera a quedar bloqueada,
la llamada regresa inmediatamente, dando un error.
msgctl
) para poder actuar o pedir
información de un buzón del sistema. El prototipo es:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);Su funcionamiento es muy parecido a semctl con la salvedad de que no hay que especificar el número de semáforo porque los buzones no se reservan por lotes como los semáforos. También hay menos opciones. Con esta llamada se puede consultar/modificar información acerca del buzón y se puede eliminar el buzón del sistema. Consultad el resumen y/o las páginas de manual para ver más detalles.
correo
.
A este programa se le puede pasar tres tipos de opciones por
la línea de órdenes:
-e tipo "mensaje"
: el
programa enviará a un buzón
el mensaje mensaje con el tipo tipo.
-r tipo
: el programa recibirá
un mensaje
del tipo tipo. Si no lo hay, se quedará
bloqueado.
/bin/ls
y la letra 'q'. Los mensajes que se
envíen y reciban del buzón tendrán
la siguiente estructura:
struct tipo_mensaje {long tipo; char remite[12]; char mensaje[80];};En el remite se escribirá el login del que lo envía. Como todos compartiréis el mismo buzón, hay que ajustarse a la especificación de los mensajes. En la recepción se imprimirá algo así como:
inf6970: "Hola, caracola."NOTA: para conseguir vuestro login, podéis usar la llamada al sistema
getuid
junto con una función
debiblioteca de la familia de getpwent
.
/sbin/mknod
ipcs
ipcrm
fdopen
getpwent
/etc/passwd
.