Colas de Mensajes
en C para Linux
Las colas de mensajes, junto con los semáforos
y la memoria compartida son los recursos
compartidos que pone unix a disposición de los programas para
que puedan intercambiarse información.
En C para unix es posible hacer
que dos procesos (dos programas)
distintos sean capaces de enviarse mensajes (estructuras de datos) y de
esta forma pueden intercambiar información. El mecanismo para conseguirlo
es el de una cola de mensajes. Los procesos introducen mensajes
en la cola y se van almacenando en ella. Cuando un proceso extrae un mensaje
de la cola, extrae el primer mensaje que se introdujo y dicho mensaje se
borra de la cola.
También es posible hacer "tipos" de mensajes distintos, de forma
que cada tipo de mensaje contiene una información distinta y va identificado
por un entero. Por ejemplo, los mensajes de tipo 1 pueden contener el saldo
de una cuenta de banco y el número de dicha cuenta, los de tipo 2
puden contener el nombre de una sucursal bancaria y su calle, etc. Los procesos
luego pueden retirar mensajes de la cola selectivamente por su tipo. Si
un proceso sólo está intersado en saldos de cuentas, extraería
únicamente mensajes de tipo 1, etc.
La forma de conseguir una cola de mensajes en un programa es la
siguiente:
- En primer lugar necesitamos conseguir una clave, de tipo key_t,
que sea común para todos los programas que quieran compartir la
cola de mensajes. Para ello existe la función key_t
ftok (char *, int). A dicha función se le pasa un fichero
que exista y sea accesible y un entero. Con ellos construye una clave
que nos devuelve. Si todos los programas utilizan el mismo fichero y el
mismo entero, obtendrán la misma clave.
Es habitual como primer parámetro pasar algún fichero del
sistema que sepamos seguro de su existencia, como por ejemplo "/bin/ls",
que es el "ls" del unix.
Para el entero, bastaría con poner un #define en algún fichero.h
de forma que todos los programas que vayan a utilizar la misma cola incluyan
dicho fichero y utilicen como entero el del #define.
- Una vez obtenida la clave, se crea la cola de mensajes. Para ello está
la función int msgget (key_t, int). Con dicha
función creamos la cola y nos devuelve un identificador para la
misma.
Si la cola correspondiente a la Clave key_t ya estuviera creada, simplemente
nos daría el identificdor de la misma (siempre y cuando los
parámetros no indiquen lo contrario).
El primer parámetro es la clave key_t obtenida anteriormente y
que debería ser la misma para todos los programas.
El segundo parámetro son unos flags. Aunque hay más posibilidades,
lo imprescindible es:
- 9 bits menos significativos, son permisos de lectura/escritura/ejecución
para propietario/grupo/otros, al igual que los ficheros. Para obtener
una cola con todos los permisos para todo el mundo, debemos poner
como parte de los flags el número 0777. Es
importante el cero delante, para que el número se interprete
en octal y queden los bits en su sitio (En C, cualquier número
que empiece por cero, se considera octal). El de ejecución
no tiene sentido y se ignora.
- IPC_CREAT. Junto con los bits anteriores, este
bit indica si se debe crear la cola en caso de que no exista. Si está
puesto, la cola se creará si no lo está ya y se devolverá
el identificador.
Si no está puesto, se intentará obtener el identificador
y se obtendrá un error si no está ya creada.
En resumen, los flags deberían ser algo así como 0777 |
IPC_CREAT
- Ya estamos en condiciones de utilizar la cola de mensajes. Para meter
un mensaje en la cola, se utiliza la función msgsnd (int,
struct msgbuf *, int, int)
- El primer parámetro entero es el identificador de la cola
obtenido con msgget().
- El segundo parámetro es el mensaje en sí. El mensaje
debe ser una estructura cuyo primer campo sea un long.
En dicho long se almacena el tipo de mensaje. El resto de los campos
pueden ser cualquier cosa que se desee enviar (otra estructura, campos
sueltos, etc). Al pasar el mensaje como parámetro, se pasa
un puntero al mensaje y se le hace un "cast" a struct msgbuf
*. No hay ningún problema en este "cast" siempre y
cuando el primer campo del mensaje sea un long.
- El tercer parámetro es el tamaño en bytes del mensaje
exceptuando el long, es decir, el tamaño en
bytes de los campos con la información.
- El cuarto parámetro son flags. Aunque hay varias opciones,
la más habitual es poner un 0 o bien IPC_NOWAIT.
En el primer caso la llamada a la función queda bloqueada hasta
que se pueda enviar el mensaje. En el segundo caso, si el mensaje
no se puede enviar, se vuelve inmediatamente con un error. El motivo
habitual para que el mensaje no se pueda enviar es que la cola de
mensajes esté llena.
- Para recoger un mensaje de la cola se utiliza la función msgrcv
(int, struct msgbuf *, int, int, int).
- El primer parámetro es el identificador de la cola obtenido
con msgget().
- El segundo parámetro es un puntero a la estructura donde
se desea recoger el mensaje. Puede ser, como en la función
anterior, cualquier estructura cuyo primer campo sea un long para
el tipo de mensaje.
- El tercer parémtro entero es el tamaño de la estructura
exceptuando el long.
- El cuarto parámetro entero es el tipo de mensaje que se quiere
retirar. Se puede indicar un entero positivo para un tipo concreto
o un 0 para cualquier tipo de mensaje.
- El quinto parámetro son flags, que habitualmente puede ser
0 o bien IPC_NOWAIT. En el primer caso, la llamada
a la función se queda bloqueada hasta que haya un mensaje del
tipo indicado. En el segundo caso, se vuelve inmediatamente con un
error si no hay mensaje de dicho tipo en la cola.
- Una vez terminada de usar la cola, se debe liberar. Para ello se utiliza
la función msgctl (int, int, struct msqid_ds *).
Es una función genérica para control de la cola de mensajes
con posibilidad de varios comandos. Aquí sólo se explica
como utilizarla para destruir la cola.
- El primer parámetro es el identificador de la cola de mensajes,
obtenido con msgget().
- El segundo parámetro es el comando que se desea ejecutar
sobre la cola, en este caso IPC_RMID.
- El tercer parémtro son datos necesarios para el comando que
se quiera ejecutar. En este caso no se necesitan datos y se pasará
un NULL.
Hay una serie de
comandos útiles
para ver desde una shell las colas que están abiertas, cuántos
mensajes contienen e incluso destruirlas.
El Ejemplo
Aquí hay dos fuentes de ejemplo en C sobre UNIX de colas de mensajes
cola1.c y cola2.c. cola1 abre la cola de mensajes, envía un mensaje
de tipo 1 para que lo lea cola2 y espera un mensaje de tipo 2 . Cuando llega
el mensaje de tipo 2, cola1 destruye la cola de mensajes.
Por su parte, cola2 abre la cola mensajes y espera un mensaje tipo 1.
Cuando lo recibe, envía un mensaje de tipo 2 y se sale. Deja la destrucción
de la cola para cola1.
Para ejecutarlos, compilarlos en primer lugar con make cola1 cola2 (no
es necesario fichero Makefile) o bien con g++ cola1.c -o cola1
y g++ cola2.c -o cola2. Luego en dos shell
distintas, ejecutar en la primera cola1 y en la segunda
cola2. Cada uno de ellos escribirá el mensaje recibido
del otro.
Estadísticas y comentarios
Numero de visitas desde el 4 Feb 2007:
- Esta pagina este mes: 30
- Total de esta pagina: 74935
- Total del sitio: 43318207