[LinuxFocus-icon]
Hogar  |  Mapa  |  Indice  |  Busqueda

Noticias | Arca | Enlaces | Sobre LF
Este documento está disponible en los siguientes idiomas: English  Castellano  Deutsch  Francais  Nederlands  Russian  Turkce  

convert to palmConvert to GutenPalm
or to PalmDoc

[Leonardo]
por Leonardo Giordani
<leo.giordani(at)libero.it>

Sobre el autor:

Acaba de recibir el diploma de la Facultad de Ingeniería de Telecomunicaciones en el Politécnico de Milán. Interesado en programación (principalmente en Ensamblador y C/C++). Desde 1999 trabaja casi exclusivamente con Linux/Unix.



Taducido al español por:
Rafa Pereira <rptv2003(at)yahoo.com>

Contenidos:

 

Programación concurrente - Colas de mensajes (2)

[run in paralell]

Resumen:

Esta serie de artículos tiene el propósito de introducir al lector al concepto de multitarea y a su implementación en el sistema operativo Linux. Comenzando por los conceptos teóricos base de la multitarea, acabaremos escribiendo una aplicación completa mostrando la comunicación entre procesos, mediante un protocolo de comunicaciones sencillo pero eficiente. Los requisitos previos para entender este artículo son:

  • Conocimiento mínimo del shell.
  • Conocimiento básico del lenguaje C (sintaxis, bucles, librerías).
Todas las referencias a páginas de manual figuran entre paréntesis a continuación del nombre del comando. Todas las funciones de glibc están documentadas en las páginas info gnu (info Libc, o, en konqueror, info:/libc/Top).

También podría ser buena idea leer antes alguno de los artículos previos de esta serie:

 

Introducción

En el anterior artículo de esta pequeña serie aprendimos cómo hacer que dos (o más) procesos se sincronicen y colaboren a través del uso de colas de mensajes. En este artículo iremos más allá y empezaremos a crear un protocolo sencillo para nuestro intercambio de mensajes.

Ya dijimos que un protocolo es un conjunto de reglas que permite a las personas o a las máquinas hablar, incluso siendo diferentes. Por ejemplo, el uso de la lengua inglesa es un protocolo, porque me permite hablarles a mis lectores hindúes (que siempre están muy interesados en lo que escribo). Hablando en términos más relacionados con Linux, si recompilas el kernel (no temas, no es tan difícil), seguramente observarás la sección Networking, en la cual puedes hacer que tu kernel entienda varios protocolos de red, como TCP/IP.

A la hora de crear un protocolo tenemos que decidir qué clase de aplicación vamos a desarrollar. En esta ocasión vamos a escribir un simulador de conmutador telefónico sencillo. El proceso principal será el conmutador telefónico, y los procesos hijos actuarán como usuarios: permitiremos a los usuarios enviarse mensajes entre ellos a través del conmutador.

El protocolo va a cubrir tres situaciones distintas: el nacimiento de un usuario (esto es, el usuario existe y está conectado), el trabajo normal del usuario, y la muerte de un usuario (ya no está conectado). Hablemos de estas tres situaciones.

Cuando un usuario se conecta al sistema crea su propia cola de mensajes (no olvides que estamos hablando de procesos), sus identificadores tienen que ser enviados al conmutador para que éste sepa cómo llegar a cada usuario. En este punto, el usuario puede inicializar las estructuras o los datos que necesite. Por último, recibe del conmutador el identificador de una cola en la que puede escribir los mensajes que quiere enviar a otros usuarios a través del conmutador.

El usuario puede enviar y recibir mensajes. Cuando envía un mensaje a otro usuario se pueden dar dos situaciones: que el receptor esté conectado o que no lo esté. En ambos casos se enviará un mensaje de confirmación al emisor, para informarle de lo que ha ocurrido con su mensaje. Esto no supone ninguna acción por parte del receptor, es el conmutador quien hace el trabajo.

Cuando un usuario se desconecta del sistema debería notificárselo al conmutador, pero no es necesaria ninguna otra acción. El metacódigo que describe este modo de trabajo es el siguiente:

/* Nacimiento */
create_queue
init
send_alive
send_queue_id
get_switch_queue_id

/* Trabajo */
while(!leaving){
 receive_all
 if(<send condition>){
  send_message
 }
 if(<leave condition>){
  leaving = 1
 }
}

/* Muerte */
send_dead

Ahora tenemos que definir el comportamiento de nuestro conmutador telefónico: cuando un usuario se conecta nos envía un mensaje que contiene el identificador de su cola de mensajes. A continuación, debemos almacenar el identificador, para poder enviar los mensajes dirigidos a este usuario, y responder enviándole el identificador de una cola en la que pueda escribir los mensajes que tengamos que enviar a otros usuarios. Además, tenemos que analizar todos los mensajes recibidos y comprobar si los receptores están conectados: si el receptor está conectado le enviaremos el mensaje, si el receptor no está conectado desecharemos el mensaje; en ambos casos enviaremos una confirmación al emisor. Cuando un usuario se desconecta, simplemente eliminaremos el identificador de su cola, de forma que no pueda recibir más mensajes.

La implementación en metacódigo es:

while(1){
 /* Usuario nuevo */
 if (<nacimiento de un usuario>){
  get_queue_id
  send switch_queue_id
 }

 /* El usuario muere */
 if (<muerte de un usuario>){
  remove_user
 }

 /* Envío de mensajes */
 check_message
 if (<usuario vivo>){
  send_message
  ack_sender_ok
 }
 else{
  ack_sender_error
 }
}
 

Tratamiento de errores

El tratamiento de las situaciones de error es una de las cosas más difíciles e importantes de hacer en un proyecto. Más aún, un buen y completo subsistema de comprobación de errores ocupa hasta el 50% del código. No voy a explicar en este artículo cómo desarrollar buenas rutinas de comprobación de errores, porque la materia es demasiado compleja, pero a partir de ahora siempre comprobaré y trataré las situaciones de error. La lectura del manual de glibc (www.gnu.org) proporciona una buena introducción a la comprobación de errores pero, si estás interesado, escribiré un artículo sobre el tema.  

Implementación del protocolo - Capa 1

Nuestro pequeño protocolo tiene dos capas: la primera (la inferior) consiste en funciones para gestionar colas y para preparar y enviar mensajes, mientras que la capa superior implementa el protocolo como funciones similares al metacódigo utilizado para describir el comportamiento del conmutador y de los usuarios.

Lo primero es definir una estructura para nuestro mensaje utilizando el prototipo de msgbuf del kernel.

typedef struct
{
  int service;
  int sender;
  int receiver;
  int data;
} messg_t;

typedef struct
{
  long mtype; /* Tipo de mensaje */
  messg_t messaggio;
} mymsgbuf_t;

Esto es algo general que podemos extender después: los campos sender y receiver contienen un identificador de usuario y el campo data contiene datos en general, mientras que el campo service se utiliza para solicitar un determinado servicio al conmutador. Por ejemplo, imaginemos que tenemos dos servicios: uno para entrega inmediata y otro para entrega con retraso, en cuyo caso el campo data podría transportar el número de segundos de retraso. Este es tan sólo un ejemplo, lo importante es que entendamos que el campo service nos ofrece muchas posibilidades.

Ahora podemos implementar algunas funciones para gestionar nuestras estructuras de datos, en particular para asignar y consultar el valor de los campos de los mensajes. Estas funciones son más o menos todas iguales, así que sólo mostraré dos de ellas, puedes encontrar el resto en los ficheros .h

void set_sender(mymsgbuf_t * buf, int sender)
{
  buf->message.sender = sender;
}

int get_sender(mymsgbuf_t * buf)
{
  return(buf->message.sender);
}

El objetivo de estas funciones no es el de reducir el código (consisten solamente en una línea de código): están ahí simplemente para recordar su significado y hacer que la codificación del protocolo se acerque más al lenguaje humano y, por lo tanto, sea más fácil de usar.

Ahora tenemos que escribir funciones para generar claves IPC, crear y eliminar colas de mensajes y enviar y recibir mensajes. Construir una clave IPC es sencillo:

key_t build_key(char c)
{
  key_t key;
  key = ftok(".", c);
  return(key);
}

Entonces, la función para crear una cola es:

int create_queue(key_t key)
{
  int qid;

  if((qid = msgget(key, IPC_CREAT | 0660)) == -1){
    perror("msgget");
    exit(1);
  }

  return(qid);
}

como puedes ver, el tratamiento de errores es muy sencillo en este caso. La siguiente función destruye una cola:

int remove_queue(int qid)
{
  if(msgctl(qid, IPC_RMID, 0) == -1)
  {
    perror("msgctl");
    exit(1);
  }
  return(0);
}

Y, por último, las funciones para recibir y enviar mensajes: para nosotros enviar un mensaje significa escribirlo en una determinada cola, en la especificada por el conmutador.

int send_message(int qid, mymsgbuf_t *qbuf)
{
  int result, lenght;
  lenght = sizeof(mymsgbuf_t) - sizeof(long);
  if ((result = msgsnd(qid, qbuf, lenght, 0)) == -1){
    perror("msgsnd");
    exit(1);
  }

  return(result);
}

int receive_message(int qid, long type, mymsgbuf_t
*qbuf)
{
  int result, length;
  length = sizeof(mymsgbuf_t) - sizeof(long);

  if((result = msgrcv(qid, (struct msgbuf *)qbuf,
length, type, IPC_NOWAIT)) == -1){
    if(errno == ENOMSG){
      return(0);
    }
    else{
      perror("msgrcv");
      exit(1);
    }
  }

  return(result);
}

Y esto es todo. Encontrarás las funciones en el fichero layer1.h: intenta escribir algún programa (por ejemplo el del artículo anterior) utilizándolas. En el próximo artículo hablaremos sobre la capa 2 del protocolo y la implementaremos.

 

Lecturas recomendadas

__________________________________ Do you Yahoo!? The New Yahoo! Search - Faster. Easier. Bingo. http://search.yahoo.com  

Formulario de "talkback" para este artículo

Cada artículo tiene su propia página de "talkback". A través de esa página puedes enviar un comentario o consultar los comentarios de otros lectores
 Ir a la página de "talkback" 

Contactar con el equipo de LinuFocus
© Leonardo Giordani, FDL
LinuxFocus.org
Información sobre la traducción:
en --> -- : Leonardo Giordani <leo.giordani(at)libero.it>
en --> es: Rafa Pereira <rptv2003(at)yahoo.com>

2003-05-06, generated by lfparser version 2.34

mirror server hosted at Truenetwork, Russian Federation.