%%\input{epsf.tex}
%%\usepackage{psfig}
\input psfig.sty
\chapter{\label{scott}Comunicación entre procesos en Linux}
\author{B. Scott Burkett, {\tt scottb@intnet.net}}
\date{v1.0, 29 de Marzo de 1995}
\abstract{Una detallada, pero global, visión de los mecanismos IPC
(facilidades para la comunicación entre procesos) implementados en el sistema
operativo Linux.}

\section{Introducción}
Los medios IPC (Inter-process communication) de Linux proporcionan un
método para que múltiples procesos se comuniquen unos con otros. Hay
varios métodos de IPC disponibles para los programadores Linux en C:
\begin{itemize}
\item Pipes UNIX Half-duplex
\item FIFOs (pipes con nombre)
\item Colas de mensajes estilo SYSV
\item Semáforos estilo SYSV
\item Segmentos de memoria compartida estilo SYSV
\item Sockets (estilo Berkeley) (no contemplado por ahora)
\item Pipes Full-duplex (pipes STREAMS) (no contemplado por ahora)
\end{itemize}

Estos medios, cuando se usan de forma efectiva, proporciona una base sólida para el desarrollo de cliente/servidor en cualquier sistema
UNIX (incluido Linux).

\section{Pipes UNIX Semi-dúplex}




\subsection{Conceptos básicos}
Simplemente, una tubería ({\em pipe}) es un método de conexión de que une la
{\em salida estándar\/} de un proceso a la {\em entrada estándar\/} de
otro. Las  tuberías son la mayor de las herramientas de IPC, han
estado presentes desde los primeros orígenes del sistema operativo
UNIX. Proporcionan un método de comunicaciones en un sentido
(unidirecional, semi-duplex) entre procesos.

Este mecanismo es ampliamente usado, incluso en la línea de comandos UNIX
(en la shell):

\begin{tscreen}
\begin{verbatim}
        ls | sort | lp
\end{verbatim}


\end{tscreen}

Lo anterior es un ejemplo de 'pipeline', donde se toma la salida de un
comando {\tt ls} como entrada de un comando {\tt sort}, quien a su vez
entrega su salida a la entrada de {\tt lp}. Los datos corren
por la tubería semi-duplex, de viajando (virtualmente) de izquierda a
derecha por la tubería.

Aunque la mayor parte de nosotros usamos las tuberías casi
religiosamente en las programaciones de scripts de shell, casi nunca
nos paramos a pensar en lo que tiene lugar a nivel del núcleo.

Cuando un proceso crea una tubería, el núcleo instala dos descriptores
de ficheros para que los use la tubería. Un descriptor se usa para
permitir un camino de entrada a la tubería (write), mientras que la
otra se usa para obtener los datos de la tubería (read). A estas
alturas, la tubería tiene un pequeño uso práctico, ya que la creación
del proceso sólo usa la tubería para comunicarse consigo
mismo. Considere esta representación de un proceso y del núcleo
después de que se haya creado una tubería:

\begin {center}
%\input {pipe1}
\psfig{figure=pipe1.ps,width=12cm}
\end   {center}

Del diagrama anterior, es fácil ver cómo se conectan los
descriptores. Si el proceso envía datos por la tubería (fd0), tiene la
habilidad obtener (leer) esa información de fd1. Sin embargo, hay un
objetivo más amplio sobre el esquema anterior. Mientras una tubería
conecta inicialmente un proceso a sí mismo, los datos que viajan por
la tubería se mueven por el núcleo. Bajo Linux en particular,
las tuberías se representan realmente de forma interna con un inodo
válido. Por supuesto, este inodo reside dentro del núcleo mismo, y no
dentro de los límites de cualquier sistema de archivos físico. Este
punto particular nos abrirá algunas puertas de E/S bastante prácticas,
como veremos un poco más adelante.

A estas alturas, la tubería es bastante inútil. Después de todo ¿por
qué el problema de crear una cañería si estamos sólo hablando con
nosotros mismos? Ahora, el proceso de creación bifurca un proceso
hijo. Como un proceso hijo hereda cualquier descriptor de fichero
abierto del padre, ahora tenemos la base por comunicación
multiprocesos (entre padre e hijo). Considere éste versión actualizada
de de nuestro esquema simple:

\begin {center}
%\input {pipe2}
\psfig{figure=pipe2.ps,width=12cm}
\end   {center}

Arriba, vemos que ambos procesos ahora tienen acceso al descriptor del
fichero que constituye la tubería. Está en esa fase, que se debe tomar
una decisión crítica. ¿En que dirección queremos que viajen los datos?
¿El proceso hijo envía información al padre, o viceversa? Los dos
procesos mutuamente están de acuerdo en esta emisión, y procede
a``cerrar'' el extremo de la cañería que no le interesa. Por motivos
discusión, digamos que el hijo ejecuta unos procesos, y devuelve
información por la tubería al padre. Nuestro esquema ya revisado
aparecería como:

\begin {center}
%\input {pipe3}
\psfig{figure=pipe3.ps,width=12cm}
\end   {center}

¡Ahora la construcción de la tubería está completa! Lo único que queda
por hacer es usar la tubería. Para a acceder a una tubería
directamente, podemos usar la misma llamada al sistema que se usa para
un fichero I/O de bajo nivel. (las tuberías están representadas
internamente como un inodo válido).
 
Para enviarle datos a la tubería, usamos la llamada al sistema
write(), y para recuperar datos de la tubería, usamos la llamada al
sistema read(). ¡Recuerde las llamadas del sistema a los ficheros I/O
de bajo-nivel se hacen usando descriptores de fichero! Sin embargo,
tenga presente que ciertas llamadas al sistema, como por ejemplo
lseek(), no trabaja con descriptores a tuberías.



\subsection{Creación de tuberías en C}



Crear ``tuberías'' con el lenguaje de programación C puede ser un poco
más complejo que en un ejemplo de shell. Para crear una tubería simple
con C, hacemos uso de la llamada al sistema pipe(). Toma un argumento
solo, que es una tabla de dos enteros, y si tiene éxito, la tabla
contendrá dos nuevos descriptores de ficheros para ser usados por la
tubería. Después de crear una tubería, el proceso típicamente desdobla
a un proceso nuevo (recuerde que el hijo hereda los descriptores del
fichero).


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: pipe();                                                          

  PROTOTIPO: int pipe( int fd[2] );                                             
    RETORNA: 0  si éxito                                                       
             -1 si error: errno = EMFILE (no quedan descriptores libres)                  
                                  EMFILE (tabla de ficheros del sistema llena)            
                                  EFAULT (el vector fd no es válido)                

  NOTAS: fd[0] es para leer, fd[1] es para escribir               
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



El primer del vector fd (elemento 0) está fijado y abierto para lectura,
mientras el segundo entero (elemento 1) está fijado y abierto para
escritura. Visualmente hablando, la salida de fd1 se vuelve la entrada
para fd0. Una vez más, todo datos que se mueven por la tubería los
hacen por el núcleo.

\begin{verbatim}
        #include <stdio.h>
        #include <unistd.h>
        #include <sys/types.h>

        main()
        {
                int     fd[2];
                
                pipe(fd);
                .
                .
        }
\end{verbatim}



Recuerde que un nombre de vector en C es un puntero a su primer
miembro. Es decir, fd es equivalente a \&fd0. Una vez hemos establecido
la tubería, entonces desdoblamos (fork) nuestro nuevo proceso hijo:

\begin{verbatim}
        #include <stdio.h>
        #include <unistd.h>
        #include <sys/types.h>

        main()
        {
                int     fd[2];
                pid_t   childpid;

                pipe(fd);
        
                if((childpid = fork()) == -1)
                {
                        perror("fork");
                        exit(1);
                }
                .
                .
        }
\end{verbatim}



Si el padre quiere recibir datos del hijo, debe cerrar fd$1$, y el hijo
debe cerrar fd$0$. Si el padre quiere enviarle datos al hijo, debe
cerrar fd$0$, y el hijo debe cerrar fd$1$. Como los descriptores se
comparten entre el padre y hijo, siempre debemos estar seguros cerrar
el extremo de cañería que no nos interesa. Como nota técnica, nunca
se devolverá EOF si los extremos innecesarios de la tubería no son
explicitamente cerrados.

\begin{verbatim}
        #include <stdio.h>
        #include <unistd.h>
        #include <sys/types.h>

        main()
        {
                int     fd[2];
                pid_t   childpid;

                pipe(fd);
        
                if((childpid = fork()) == -1)
                {
                        perror("fork");
                        exit(1);
                }

                if(childpid == 0)
                {
                        /* El hijo cierra el descriptor de entrada */
                        close(fd[0]);
                }
                else
                {
                        /* El padre cierra el descriptor de salida */
                        close(fd[1]);
                }
                .
                .
        }
\end{verbatim}




Como se mencionó previamente, una vez se ha establecido la tubería,
los descriptores de fichero se tratan como descriptores a ficheros
normales.

\begin{verbatim}

/*****************************************************************************
 Parte de la "Guia Linux de Programacion - Capitulo 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULO: pipe.c
 *****************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
        int     fd[2], nbytes;
        pid_t   childpid;
        char    string[] = "Hola a todos!\n";
        char    readbuffer[80];

        pipe(fd);
        
        if((childpid = fork()) == -1)
        {
                perror("fork");
                exit(1);
        }

        if(childpid == 0)
        {
                /* Cierre del descriptor de entrada en el hijo */
                close(fd[0]);

                /* Enviar el saludo via descriptor de salida */
                write(fd[1], string, strlen(string));
                exit(0);
        }
        else
        {
                /* Cierre del descriptor de salida en el padre  */
                close(fd[1]);

                /* Leer algo de la tuberia... el saludo! */
                nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
                printf("Received string: %s", readbuffer);
        }
        
        return(0);
}
\end{verbatim}



A menudo, los descriptores del hijo son duplicados en la entrada o
salida estándares. El hijo puede entonces hacer exec() con otro 
programa, que hereda los stream estándar. Observe la llamada al sistema
dup():


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: dup();                                                           

  PROTOTIPO: int dup( int oldfd );                                              
    RETORNA: nuevo descriptor si hay éxito                                      
             -1 si error: errno = EBADF (oldfd no es un descriptor valido)      
                                  EBADF (newfd se sale del rango)               
                                  EMFILE (Hay demasiados descriptores en el proceso abiertos)

  NOTAS: ¡el antiguo descriptor no se cierra! Asi podemos intercambiarlos        
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}





Aunque el descriptor viejo y el recién creado se puede intercambiar,
normalmente cerraremos primero uno de los stream estándar. La llamada
al sistema dup() usa el número descriptor más bajo no utilizado para
el nuevo descriptor.

Considere lo siguiente:

\begin{verbatim}
        .
        .
        childpid = fork();
        
        if(childpid == 0)
        {
                /* Cerrar la entrada estandar en el hijo */
                close(0);
                
                /* Duplicar sobre esta la salida de la tuberia */
                dup(fd[0]);
                execlp("sort", "sort", NULL);
                
        }
\end{verbatim}



Como el descriptor de fichero 0 (stdin) se cerró, la llamada a dup()
duplicó el descriptor de la entrada de la tubería (fd0) hacia su
entrada estándar. Entonces hacemos una llamada a execlp() recubrir el
segmento de texto (código) del hijo con el del programa. ¡Desde no
hace mucho los programas exec heredan los stream estándares de sus
orígenes, realmente hereda el lado de la entrada de la tubería como su
entrada estándar! Ahora, cualquier cosa que el padre original procesa
lo envía a la tubería, va en la facilidad de la clase.

Hay otra llamada al sistema, dup2 (), que se puede usar también. Esta
llamada particular tiene su origen con la Versión 7 de UNIX, se
realizó por una versión de BSD y ahora es requerida por el estándar POSIX.


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: dup2();                                                          

  PROTOTIPO: int dup2( int oldfd, int newfd );                                  
    RETORNA: nuevo descriptor si hay éxito                                      
             -1 si error: errno = EBADF (oldfd no es descriptor válido)         
                                  EBADF (newfd está fuera de rango)             
                                  EMFILE (demasiados descriptores abiertos)     

  NOTAS: ¡el descriptor antiguo es cerrado con dup2()!                           
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Con esta particular llamada, tenemos la operación de cerrado, y la
duplicación del descriptor actual, relacionado con una llamada al 
sistema. Además, se garantiza el ser atómica, que esencialmente
significa que nunca se interrumpirá por la llegada de una señal. Toda
la operación transcurrirá antes de devolverle el control al núcleo
para despachar la señal. Con la llamada al sistema dup() original, los
programadores tenían que ejecutar un close() antes de llamarla. Esto
resultaba de dos llamadas del sistema, con un grado pequeño de
vulnerabilidad en el breve tiempo que transcurre entre ellas. Si llega
una señal durante ese tiempo, la duplicación del descriptor
fallaría. Por supuesto, dup2 () resuelve este problema para nosotros.

Considere:

\begin{verbatim}
        .
        .
        childpid = fork();
        
        if(childpid == 0)
        {
                /* Cerrar entrada estandar, duplicando a esta la
                   salida de datos de la tuberia */
                dup2(0, fd[0]);
                execlp("sort", "sort", NULL);
                .
                .
        }
\end{verbatim}







\subsection{Tuberías, la forma fácil de hacerlo}

Si de todo lo visto anteriormente parece enredada la forma de crear y utilizar tuberías, hay una alternativa:


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  FUNCIÓN DE LIBRERÍA: popen();                                                    

  PROTOTIPO: FILE *popen ( char *comando, char *tipo);                          
    RETORNA: si hay éxito, nuevo "stream" de fichero                          
             si no hay éxito, NULL (por fallo en llamada pipe() o fork() ).     

  NOTAS: crea una tubería, y realiza las llamadas fork/exec
         según el "comando" pasado como argumento. 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Esta función estándar de la biblioteca crea una tubería semi-duplex llamando a pipe() internamente. Entonces adesdobla un proceso hijo,
abre una shell Bourne y ejecuta el argumento ``command'' en la shell. La dirección del flujo de datos se determina por el segundo
argumento, ``type''. Puede ser ``r'' o ``w'', para ``read'' o ``write''. ¡No pueden ser ambos!. Bajo Linux la tubería se abrirá según el modo
especificado por el primer carácter del argumento ``type''. Así, si trata de pasar ``rw'', sólo lo abre en modo ``read''.

Mientras esta función de la biblioteca ejecuta realmente parte del trabajo sucio por usted, hay un inconveniente substancial. Pierde parte del
control que tenía con el uso de la llamada al sistema pipe(), y la manipulación de fork/exec por usted mismo. Sin embargo, como la shell de
Bourne se usa directamente, la expansión de metacaracteres de la shell (incluso plantillas) está permitida dentro del argumento ``comando''.

Las tuberías que se crean con popen() se debe cerrar con pclose(). Por ahora, probablemente se habrá dado cuenta de que popen/pclose
comparten un parecido llamativo con las funciones I/O stream de fichero normal fopen() y fclose(). 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  FUNCIÓN DE LIBRERÍA: pclose();                                                   

  PROTOTIPO: int pclose( FILE *stream );                                        
    RETORNA: el código de retorno de la llamada wait4()                          
             -1 si el "stream" pasado no es válido, o la llamada wait4() falla                   

  NOTAS: espera a que el proceso que escribe en la tuberia termine,
  y luego cierra el "stream".

\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


La función pclose() ejecuta un wait4() en el proceso desdoblado por popen(). Cuando vuelve, destruye la tubería y el stream de fichero de
salida. Una vez más, es sinónimo de la función fclose() para ficheros E/S normales de stream.

Considere este ejemplo, que abre una tubería al comando sort, y ordena un array de cadena de caracteres.:


\begin{verbatim}
/*****************************************************************************
 Parte de la "Guia Linux de Programacion - Capitulo 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULO: popen1.c
 *****************************************************************************/
  
#include <stdio.h>

#define MAXSTRS 5

int main(void)
{
        int  cntr;
        FILE *pipe_fp;
        char *strings[MAXSTRS] = { "eco", "bravo", "alpha",
                                  "charlie", "delta"};

        /* Crea una tuberia de un sentido llamando a popen() */
        if (( pipe_fp = popen("sort", "w")) == NULL)
        {
                perror("popen");
                exit(1);
        }

        /* Bucle de proceso */
        for(cntr=0; cntr<MAXSTRS; cntr++) {
                fputs(strings[cntr], pipe_fp);
                fputc('\n', pipe_fp);
        }

        /* Cierra la tuberia */
        pclose(pipe_fp);
        
        return(0);
}
\end{verbatim}



Como {\tt popen()} usa la shell para hacer su enlace, ¡todas las expansiones de caracteres y metacaracteres de la shell están disponibles para su
uso! Además, técnicas más avanzadas tales como redirección, e incluso la salida por tubería se puede utilizar con {\tt popen()}. Considere el
siguiente ejemplo: 

\begin{verbatim}
        popen("ls ~scottb", "r");
        popen("sort > /tmp/foo", "w");
        popen("sort | uniq | more", "w");
\end{verbatim}



Considere este pequeño programa como otro ejemplo de popen(), que abre dos tuberías (una a la orden ls, el otro a sort):

\begin{verbatim}

/*****************************************************************************
 Parte de la "Guia Linux de Programacion - Capitulo 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULO: popen2.c
 *****************************************************************************/

#include <stdio.h>

int main(void)
{
        FILE *pipein_fp, *pipeout_fp;
        char readbuf[80];

        /* Crea una tuberia de un sentido llamando a popen() */
        if (( pipein_fp = popen("ls", "r")) == NULL)
        {
                perror("popen");
                exit(1);
        }

        /* Crea una tuberia de un sentido llamando a popen() */
        if (( pipeout_fp = popen("sort", "w")) == NULL)
        {
                perror("popen");
                exit(1);
        }

        /* Bucle de proceso */
        while(fgets(readbuf, 80, pipein_fp))
                fputs(readbuf, pipeout_fp);

        /* Cierre de las tuberias */
        pclose(pipein_fp);
        pclose(pipeout_fp);

        return(0);
}
\end{verbatim}


Para nuestra demostración final de popen(), creamos un programa genérico que abre una tubería entre una orden pasada y un nombre de
fichero: 

\begin{verbatim}

/*****************************************************************************
 Parte de la "Guia Linux de Programacion - Capitulo 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULO: popen3.c
 *****************************************************************************/

#include <stdio.h>

int main(int argc, char *argv[])
{
        FILE *pipe_fp, *infile;
        char readbuf[80];

        if( argc != 3) {
                fprintf(stderr, "USO:  popen3 [comando] [archivo]\n");       
                exit(1);
        }

        /* Abrir el fichero de entrada */
        if (( infile = fopen(argv[2], "rt")) == NULL)
        {
                perror("fopen");
                exit(1);        
        }

        /* Crear una tuberia de un sentido llamando a popen() */
        if (( pipe_fp = popen(argv[1], "w")) == NULL)
        {
                perror("popen");
                exit(1);
        }

        /* Bucle de proceso */
        do { 
                fgets(readbuf, 80, infile);
                if(feof(infile)) break;

                fputs(readbuf, pipe_fp);
        } while(!feof(infile));

        fclose(infile); 
        pclose(pipe_fp);

        return(0);
}
\end{verbatim}


Pruebe este programa, con las llamadas siguientes:

\begin{verbatim}
        popen3 sort popen3.c
        popen3 cat popen3.c
        popen3 more popen3.c
        popen3 cat popen3.c | grep main
\end{verbatim}







\subsection{Operaciones atómicas con tuberías}


Para que una operación se considere ``atómica'', no se debe interrumpir de ninguna manera. Todo su funcionamiento ocurre de una vez. La norma POSIX indica en {\tt /usr/include/posix1\_lim.h} que el tamaño máximo del buffer para una operación atómica en una tubería es: 

\begin{verbatim}
        #define _POSIX_PIPE_BUF         512
\end{verbatim}




Hasta 512 bytes se pueden escribir o recuperar de una tubería
atómicamente. Cualquier cosa que sobrepase este límite se
partirá. Bajo
Linux sin embargo, se define el límite atómico operacional en
``linux/limits.h'' como:

\begin{verbatim}
        #define PIPE_BUF        4096
\end{verbatim}



Como puede ver, Linux adapta el número mínimo de bytes requerido por POSIX, y se le pueden agregar bastantes. La atomicidad del
funcionamiento de tubería se vuelve importante cuando implica más de un proceso (FIFOS). Por ejemplo, si el número de bytes escritos en
una tubería excede el límite atómico para una simple operación, y procesos múltiples están escribiendo en la tubería, los datos serán
``intercalados'' o ``chunked''. En otras palabras, un proceso insertaría datos en la tubería entre la escritura de otro. 




\subsection{Notas acerca de las tuberías semi-dúplex:}



\begin{itemize}
\item Se pueden crear tuberías de dos direcciones abriendo dos tuberías, y reasignado los descriptores de fichero al proceso hijo.
\item La llamada a pipe() debe hacerse ANTES de la llamada a fork(), o los hijos no heredarán los descriptores (igual que en popen()).
\item Con tuberías semi -duplex, cualquier proceso conectado debe compartir el ancestro indicado. Como la tubería reside en el núcleo,
cualquier proceso que no sea ancestro del creador de la tubería no tiene forma de direccionarlo. Este no es el caso de las tuberías con
nombre (FIFOS).
\end{itemize}





\section{Tuberías con Nombre (FIFO - First In First Out)}



\subsection{Conceptos básicos}

Una tubería con nombre funciona como una tubería normal, pero tiene algunas diferencias notables.

\begin{itemize}
\item Las tuberías con nombre existen en el sistema de archivos como un archivo de dispositivo especial.
\item Los procesos de diferentes padres pueden compartir datos mediante una
tubería con nombre.
\item Cuando se han realizados todas las I/O por procesos compartidos,
la tubería con nombre permanece en el sistema de archivos para un uso
posterior.
\end{itemize}



\subsection{Creación de una FIFO}

Hay varias formas de crear una tubería con nombre. Las dos primeras se
pueden hacer directamente de la shell.

\begin{verbatim}
        mknod MIFIFO p
        mkfifo a=rw MIFIFO
\end{verbatim}

Los dos comandos anteriores realizan operaciones idénticas, con una
excepción. El comando mkfifo proporciona una posibilidad de alterar
los permisos del fichero FIFO directamente tras la creación. Con mknod
será necesaria una llamada al comando chmod.

Los ficheros FIFO se pueden identificar rápidamente en un archivo
físico por el indicador "p" que aparece en la lista del directorio.


\begin{verbatim}
        $ ls -l MIFIFO
        prw-r--r--   1 root     root            0 Dec 14 22:15 MIFIFO|
\end{verbatim}


También hay que observar que la barra vertical ("símbolo pipe") está situada inmediatamente detrás del nombre de fichero. Otra gran razón para usar
Linux ¿eh?

Para crear un FIFO en C, podemos hacer uso de la llamada del sistema mknod():


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  FUNCIÓN DE LIBRERÍA: mknod();                                                    

  PROTOTIPO: int mknod( char *nombre, mode_t modo, dev_t disp);                      
    RETURNS: 0 si éxito,                                                                 
             -1 si error: errno = EFAULT (nombre no válido)                     
                                  EACCES (permiso denegado)                     
                                  ENAMETOOLONG (nombre demasiado largo)              
                                  ENOENT (nombre no válido)                     
                                  ENOTDIR (nombre no válido)                    
                                  (vea la página mknod(3) para más información) 

  NOTES: Crea un nodo del sistema de ficheros (fichero, dispositivo, o FIFO)                 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Dejaré una discusión más detallada de mknod() a la página del manual, pero lo podemos considerear un simple ejemplo de la creación de un FIFO en
C: 

\begin{verbatim}
                mknod("/tmp/MIFIFO", S_IFIFO|0666, 0);
\end{verbatim}



En este caso el fichero ``/tmp/MIFIFO'' se crea como fichero FIFO. Los
permisos requeridos son ``0666'', aunque se ven afectados por la
configuración de umask de la siguiente forma:

\begin{verbatim}
         umask_definitiva = permisos_solicitados & ~umask_inicial
\end{verbatim}

Un truco común es usar la llamada del sisterma umask() para borrar
temporalmente el valor de umask:

\begin{verbatim}
                umask(0);
                mknod("/tmp/MIFIFO", S_IFIFO|0666, 0);
\end{verbatim}


Además, el tercer argumento de mknod() se ignora salvo que estemos creando un archivo de dispositivo. En ese caso, se debería especificar los
números {\it mayor} y {\it menor} del fichero de dispositivo.



\subsection{Operaciones con FIFOs}



Las operaciones E/S sobre un FIFOson esencialmente las mismas que para las tuberías normales, con una gran excepción. Se debería usar una
llamada del sistema "open" o una función de librería para abrir físicamente un canal para la tubería. Con las tuberías semi-duplex, esto es innecesario,
ya que la tubería reside en el núcleo y no en un sistemade archivos físico. En nuestro ejemplo trataremos la tubería como un stream, abiéndolo con
fopen(), y cerrándolo con fclose().

Consideramos un proceso servidor simple:

\begin{verbatim}
/*****************************************************************************
 Parte de la "Guia Linux de Programacion - Capitulo 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULO: fifoserver.c
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

#include <linux/stat.h>

#define FIFO_FILE       "MIFIFO"

int main(void)
{
        FILE *fp;
        char readbuf[80];

        /* Crea el FIFO si no existe */
        umask(0);
        mknod(FIFO_FILE, S_IFIFO|0666, 0);

        while(1)
        {
                fp = fopen(FIFO_FILE, "r");
                fgets(readbuf, 80, fp);
                printf("Cadena recibida: %s\n", readbuf);
                fclose(fp);
        }

        return(0);
}
\end{verbatim}


Como un FIFO bloquea por defecto, ejecute el servidor en segundo plano
tras compilarlo:

\begin{verbatim}
        $ fifoserver&
\end{verbatim}



Discutiremos la acción de bloqueo de un FIFO en un momento. Primero
considrearemos el siguiente cliente simple enfrentado a nuestro
servidor:

\begin{verbatim}

/*****************************************************************************
 Parte de la "Guia Linux de Programacion - Capitulo 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULO: fifoclient.c
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>

#define FIFO_FILE       "MIFIFO"

int main(int argc, char *argv[])
{
        FILE *fp;

        if ( argc != 2 ) {
                printf("USO: fifoclient [cadena]\n");
                exit(1);
        }

        if((fp = fopen(FIFO_FILE, "w")) == NULL) {
                perror("fopen");
                exit(1);
        }

        fputs(argv[1], fp);

        fclose(fp);
        return(0);
}
\end{verbatim}


\subsection{Acciones Bloqueantes en una FIFO}


Normalmente, el bloqueo ocurre en un FIFO. En otro palabras, si se abre el FIFO para lectura, el proceso esará "bloqueado" hasta que cualquier otro
proceso lo abra para escritura. Esta acción funciona al revés también. Si este comportamiento no nos interesa, se puede usar la bandera
O\_NONBLOCK en la llamada a open() para desactivar la acción de bloqueo por defecto.

En el caso de nuestro servidor simple, lo hemos puesto en segundo plano, y permito hacer su bloqueo allí. La alternativa estarían saltar a otra consola
virtual y ejecutar el cliente, cambiando de un lado a otro para ver la acción resultante.




\subsection{La Infame Señal SIGPIPE}

En una última nota, las tuberías deberían tener a un lector y un escritor. Si un proceso trata de escribir en una tubería que no tiene lector, el núcleo
enviará la señal SIGPIPE. Esto es imperativo cuando en la tubería se ven envueltos más dos procesos.






\section{IPC en Sistema V}




\subsection{Conceptos fundamentales}


Con Unix Sistema V, AT\&T introdujo tres nuevas formas de las
facilidades IPC (colas de mensajes, semáforos y memoria
compartida). Mientras que el
comité POSIX aun no ha completado su estadarización de estas
facilidades, la mayoría de las implementaciones soportan
éstas. Además, Berkeley
(BSD) usa sockets como su forma primaria de IPC, más que los elementos
del Sistema V. Linux tiene la habilidad de usar ambas formas de IPC
(BSD y System V), aunque no se discutirán los socket hasta el último
capítulo.

La implementación para Linux del IPC System V fue escrita por {\em
Krishna Balasubramanian}, en {\tt balasub@cis.ohio-state.edu}.



\subsubsection{Identificadores IPC}

Cada {\em objeto IPC} tiene un único identificador IPC asociado con
él. Cuando decimos ``objeto IPC'', hablamos de una simple cola de mensaje, semáforo
o segmento de memoria compartida. Se usa este identificador, dentro del núcleo, para identificar de forma única un objeto IPC. Por ejemplo, para
acceder un segmento particular memoria compartida, lo único que requiere es el valor ID único que se le ha asignado a ese segmento.

La unicidad de un identificador es importante según el {\em tipo} de objeto
en cuestión. Para ilustrar esto, supondremos un identificador numérico
``12345''.
Mientras no puede haber nunca dos colas de mensajes, con este mismo identificador existe la posibilidad que existan una cola de mensajes y un
segmento de memoria compartida que poseen el mismo identificador numérico.



\subsubsection{Claves IPC}

Para obtener un identificador único, debe utilizarse una {\em
clave\/}. Ésta debe ser conocida por ambos procesos cliente y
servidor. Este es el primer paso para construir el entorno
cliente/servidor de una aplicación.



{\em Cuando usted llama por teléfono a alguien, debe conocer su
 número. Además, la compañía telefónica debe conocer cómo dirigir su
 llamada al destino. Una vez que el receptor responde a su llamada, la
 conexión tiene lugar.\/}


En el caso de los mecanismos IPC de Sistema V, el ``teléfono''
coincide con el tipo de objeto usado. La ``compañía telefónica'' o el
sistema de encaminado, se puede equiparar con la clave IPC.

La clave puede ser el mismo valor cada vez, incluyendo su código en la
propia aplicación. Esta es una desventaja pues la clave requerida
puede estar ya en usa. Por eso, la función {\tt ftok()} nos será útil
para generar claves no utilizadas para el cliente y el servidor.



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  FUNCIÓN DE LIBRERÍA: ftok();

  PROTOTIPO: key_t ftok ( char *nombre, char proj );
    RETORNA: nueva clave IPC si hay éxito
             -1 si no hubo éxito, dejando errno con el valor de la llamada stat()
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



La clave del valor devuelto de ftok () se genera por la combinación del número del inodo y del número menor de dispositivo del archivo argumento, con
el carácter identificador del proyecto del segundo argumento. Éste no garantiza la unicidad, pero una aplicación puede comprobar las colisiones y
reintentar la generación de la clave. 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
        key_t   miclave;
        miclave = ftok("/tmp/miaplic", 'a');
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



En el caso anterior el directorio {\tt /tmp/miaplic} se combina con la
letra '{\tt a}'.Otro ejemplo común es usar el directorio actual: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
        key_t   miclave;
        mykey = ftok(".", 'a');
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


El algoritmo de la generación de la clave usado está completamente a la discreción del programador de la aplicación. Mientras que tome medidas para
prevenir ls condiciones críticas, bloqueos, etc, cualquier método es viable. Para nuestros propósitos de demostración, usaremos ftok(). Si suponemos
que cada proceso cliente estará ejecutándose desde un único directorio
``home'', las claves generadas deben bastar por nuestras necesidades.

El valor clave, sin embargo, se obtiene, se usa una llamada al sistema IPC para crear u obtener acceso a los objetos IPC. 




\subsubsection{Comando {\tt ipcs}}

El comando {\tt ipcs} puede utilizarse para obtener el estado de todos
los objetos IPC Sistema V. La versión para Linux de esta utilidad
también fue preparada por {\em Krishna Balasubramanian\/}.

\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
ipcs     -q:    Mostrar solo colas de mensajes
ipcs     -s:    Mostrar solo los semáforos
ipcs     -m:    Mostrar solo la memoria compartida
ipcs --help:    Otros argumentos
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}

Por defecto, se muestran las tres categorías. Considérese el siguiente
ejemplo de salida del comando {\tt ipcs}:


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

------ Shared Memory Segments --------
shmid     owner     perms     bytes     nattch    status      

------ Semaphore Arrays --------
semid     owner     perms     nsems     status      

------ Message Queues --------
msqid     owner     perms     used-bytes  messages    
0         root      660       5           1           
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


Aquí vemos una simple cola mensaje que tiene un identificador `` 0.''
Es propiedad del {\tt root}, y tiene permisos en octal de {\tt 660}, o
{\tt -rw-rw--}. Hay un
mensaje en la cola, y ese mensaje tiene un tamaño del total de 5 bytes. 

Los comandos {\tt ipcs} son una herramienta muy potente que proporciona una leve introducción en los mecanismos de almacenamiento del núcleo para
objetos IPC. Apréndalo, úselo, reverencielo.




\subsubsection{El Comando {\tt ipcrm}}

Se puede usar el comando ipcrm para quitar un objeto IPC del núcleo. Mientras que los objetos IPC se pueden quitar mediante llamadas al sistema en
el código del usuario (veremos cómo en un momento),aparece a menudo la necesidad, sobre todo en ambientes del desarrollo, de quitar objetos IPC a
mano. Su uso es simple: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
ipcrm <msg | sem | shm>  <IPC ID>
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Simplemente especifique si el objeto a eliminar es una cola de mensaje
({\ em msg}), un semáforo ({\em sem}), o un segmento de memoria
compartida ({\em shm}). El identificador 
de IPC se puede obtenr mediante los comandos ipcs. Tiene que especificar el tipo de objeto, como los identificadores son únicos entre los del mismo
tipo (retome nuestra discusión anterior).





\subsection{Colas de Mensajes}




\subsubsection{Conceptos Básicos}
Las colas de mensaje se pueden describir mejor como una lista enlazada interior dentro del espacio de direccionamiento del núcleo.Los mensajes se
pueden enviar a la cola en orden y recuperarlos de la cola en varias maneras diferentes. Cada cola de mensaje (por supuesto) está identificada de
forma única por un identificador IPC. 




\subsubsection{Estructuras interna y de datos de usuario}
La clave para comprender totalmente tales temas complejos como el IPC Sistema V es familiarizarse con las distintas estrcturas de datos internas que
residen dentro de los confines del núcleo mismo.El acceso directo a algunas de estas estructuras es necesario incluso en las operacions más primitivas,
mientras otros residen a un nivel mucho más bajo.


\paragraph{Buffer de Mensaje}
La primera estructura que veremos es la estructura {\tt msgbuf}. Esta
particular estructura de datos puede ser interpretada como una {\em
plantilla \/} por datos del
mensaje. Mientras que un programador puede elegir si definir
estructuras de este tipo, es imperativo que entiende que {\bf hay} realmente una estructura
del tipo {\tt msgbuf}. Se declara en {\tt linux/msg.h} como sigue: 


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
/* buffer de mensaje para llamadas msgsnd y msgrcv */
struct msgbuf {
    long mtype;         /* tipo de mensaje */
    char mtext[1];      /* texto del mensaje */
};
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Hay dos miembros en la estructura {\tt msgbuf}:

\begin{description}
\item[{\tt mtype}] \mbox{}

El {\em tipo\/} de mensaje, representado por un número positivo. ¡Y
{\em debe} ser un número positivo!

\item[{\tt mtext}] \mbox{}

Los datos del mensaje en sí mismo.

\end{description}

La habilidad asignarle a un mensaje dado un {\em tipo\/},
esencialmente le da la capacidad de {\em multiplexar\/} mensajes en una cola sola. Por ejemplo, al proceso
cliente se puede asignar a un número mágico, que se puede usar como el tipo de mensaje para mensajes enviados desde un proceso servidor. El
servidor mismo podría usar algunos otros números, que los clientes podrían usar para enviarle mensajes. Por otra parte, una aplicación podría marcar
mensajes de error como tener un mensaje tipo 1, petición de mensajes podrían ser tipo 2, etc. Las posibilidades son interminables. 

En otra nota no se confunda por el nombre demasiado descriptivo
asignado al elemento dato del mensaje ({\tt mtext}). Este campo no se restringe a
contener sólo arrays de carácteres, sino cualquier tipo e dato, en cualquier forma. El campo mismo es realmente arbitrario, ya que esta estructura es
redefinida por el programador de la aplicación. Considere esta redefinición: 


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
struct my_msgbuf {
        long    mtype;          /* Tipo de mensaje */
        long    request_id;     /* Identificador de petición */
        struct  client info;    /* Estructura de información del cliente */
};
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


Aquí vemos el tipo de mensaje, como antes, pero el resto de la estructura ha sido reemplazado por otros dos elementos,
uno de los cuales es otra estructura. Ésta es la belleza de las colas de mensajes. El núcleo no hace ninguna traducción de
datos. Se puede enviar cualquier información. 

Sin embargo, existe un límite interior del máximo tamaño de un mensaje
dado. En Linux se define éste en {\tt linux/msg.h}
como sigue: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
#define MSGMAX  4056   /* <= 4056 */   /* Tama~no maximo del mensaje (bytes) */
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


El mensajes no puede ser mayor de 4,056 bytes en total, incluyendo el miembro mtype, que tiene una longitud de 4 bytes
(long). 




\paragraph{Estructura {\tt msg} del Núcleo}

El núcleo guarda cada mensaje en la cola dentro de la estructura
msg. Se define en {\tt linux/msg.h} como sigue: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
/* one msg structure for each message */
struct msg {
    struct msg *msg_next;   /* siguiente mensaje de la cola */
    long  msg_type;          
    char *msg_spot;         /* direccion del texto del mensaje */
    short msg_ts;           /* tama~no del texto del mensaje */
};
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}




\begin{description}
\item[{\tt msg\_next}] \mbox{}

Es un puntero al siguiente mensaje de la cola. Se almacenan como ouna lista simple enlazada en el espacio de direcciones
del núcleo. 

\item[{\tt msg\_type}] \mbox{}

Éste es el tipo de mensaje, como asignado en la estructura msgbuf del usuario. 

\item[{\tt msg\_spot}] \mbox{}

Un puntero al inicio del cuerpo del mensaje. 

\item[{\tt msg\_ts}] \mbox{}

La longitud del texto del mensaje o del cuerpo.

\end{description}





\paragraph{Estructura {\tt msqid\_ds} del núcleo}

Cada uno de los tres tipos de objetos IPC tienen una estructura de datos interna que se mantiene por el núcleo. Para las
colas de mensaje, es la estructura msqid\_ds. El núcleo crea, almacena, y mantiene un caso de esta estructura por cada
cola de mensaje hace cola creada en el sistema. Se define en {\tt linux/msg.h} de la siguiente forma:



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
/* una estructura msqid por cada cola del sistema */
struct msqid_ds {
    struct ipc_perm msg_perm;
    struct msg *msg_first;  /* primer mensaje de la cola */
    struct msg *msg_last;   /* ultimo mensaje */
    time_t msg_stime;       /* ultimo instante de msgsnd */
    time_t msg_rtime;       /* ultimo instante de msgrcv */
    time_t msg_ctime;       /* ultimo instante cambio */
    struct wait_queue *wwait;
    struct wait_queue *rwait;
    ushort msg_cbytes;    
    ushort msg_qnum;     
    ushort msg_qbytes;      /* numero maximo de bytes en cola */
    ushort msg_lspid;       /* pid del ultimo msgsnd */
    ushort msg_lrpid;       /* pid de la ultima recepcion */
};
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Aunque tendrá raramente que usar la mayor parte de los miembros de esta estructura, daremos una descripción breve de
está para completar nuestra visión: 

\begin{description}
\item[{\tt msg\_perm}] \mbox{}

Un caso de la estructura {\tt ipc\_perm}, que se define en {\tt linux/ipc.h}. Éste recoge la información del permiso para la cola de
mensaje, incluso los permisos del acceso, e información sobre el creador de la cola (uid, etc). 

\item[{\tt msg\_first}] \mbox{}

Enlace al primer mensaje de la cola (cabecera de la lista). 

\item[{\tt msg\_last}] \mbox{}

Enlace al último mensaje de la cola (cola de la lista). 

\item[{\tt msg\_stime}] \mbox{}

Instante ({\tt time\_t}) del último mensaje que se envió a la cola. 

\item[{\tt msg\_rtime}] \mbox{}

Instante del último mensaje recuperado de la cola. 

\item[{\tt msg\_ctime}] \mbox{}

Instantedel último cambio hecho a la cola. (hablaremos de esto más tarde). 

\item[{\tt wwait}] \mbox{}

y

\item[{\tt rwait}] \mbox{}

Punteros a la {\em cola de espera\/} del núcleo. Se usan cuando una operación sobre una cola de mensajes estima que el proceso
entra en el estado de dormido (es decir, la cola está llena y el porceso espera una apertura).

\item[{\tt msg\_cbytes}] \mbox{}

Número total number de bytes que hay en la cola (suma de los tamaños de todos los mensajes). 

\item[{\tt msg\_qnum}] \mbox{}

Número de mansajes actual en la cola. 

\item[{\tt msg\_qbytes}] \mbox{}

Máximo número de bytes en la cola.

\item[{\tt msg\_lspid}] \mbox{}

El PID del proceso que envía el último mensaje. 

\item[{\tt msg\_lrpid}] \mbox{}

El PID del proceso que recupera el último mensaje. 

\end{description}





\paragraph{Estructura {\tt ipc\_perm} del núcleo}

El núcleo guarda información de permisos para objetos IPC en una estructura de tipo {\tt ipc\_perm}. Por ejemplo, en la
estructura interna para una cola de mensaje descrita antes, el miembro de {\tt msg\_perm} es de este tipo. Se declara en
{\tt linux/ipc.h} como sigue: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

struct ipc_perm
{
  key_t  key;
  ushort uid;   /* euid y egid del propietario */
  ushort gid;
  ushort cuid;  /* euid y egid del creador */
  ushort cgid;
  ushort mode;  /* modos de acceso, veanse despues los valores */
  ushort seq;   /* numero de secuencia del slot */
};
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Todo lo anterior es bastante autoexplicativo. Guardado junto con la clave IPC del objeto hay información sobre el
creador y dueño del objeto (pueden ser diferentes).Los modos del
acceso octal como un {\tt unsigned short}. Finalmente, la
{\it secuencia del slot} se guarda al final. Cada vez qye un objeto IPC se cierra mediante una llamada al sistema llama
(destruye), este valor se incrementa por el máximo númera de objetos
IPC que pueden residir en un sistema. ¿Tendrá que usar este valor? No.


{\bf NOTA:}{\em  Hay una excelente exposición de este tema, y los
asuntos de seguridad relacionados, en el libro {\bf UNIX Network
Programming}, de Richard Stevens (página 125)}.




\subsubsection{LLAMADA AL SISTEMA: msgget()}


Para crear una nueva cola de mensajes, o acceder a una existente,
usaremos la llamada al sistema {\tt msgget()}.



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: msgget();                                                          

  PROTOTIPO: int msgget ( key_t clave, int msgflg );                                             
    RETORNA: Si hay éxito, identificador de la cola de mensajes                                        
             -1 si error: errno = EACCESS (permiso denegado)
                                  EEXIST (No puede crearse la cola pues ya existe)
                                  EIDRM (La cola esta marcada para borrarse)
                                  ENOENT (La cola no existe)   
                                  ENOMEM (No hay memoria para crear la cola)
                                  ENOSPC (Se ha superado el limite de colas)
  NOTAS: 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



El primer argumento de {\tt msgget()} es el valor clave (en nuestro
caso devuelto por una llamada a {\tt ftok()}. Este valor clave se
compara entonces con los valores clave que existen dentro del núcleo de otras colas de mensaje. En ese punto las
operaciones de apertura o acceso depende de los contenidos del
argumento {\tt msgflg}.

\begin{description}
\item[IPC\_CREAT] \mbox{}

Crea la cola si aun no existe en el núcleo.

\item[IPC\_EXCL] \mbox{}

Cuando se usa con IPC\_CREAT, falla si la cola ya existe.

\end{description}


Si usamos solo {\tt IPC\_CREAT},  {\tt msgget()} retornará el
identificador de una cola nueva, o bien el de la existente con la
misma clave. Si usamos además {\tt IPC\_EXCL}, la llamada creará una
nueva cola o fallará si la cola con esa clave ya existía. La opción
{\tt IPC\_EXCL} es poco útil si no se usa combinada con {\tt
IPC\_CREAT}.

Es posible incluir en la máscara un modo opcional octal, pues cada
objeto IPC tiene un esquema de permisos de acceso similar a cualquier
archivo del sistema Unix.

Creamos una función de envoltura rápida para abriro crear una cola de mensaje:


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int abrir_cola( key_t keyval )
{
        int     qid;
        
        if((qid = msgget( keyval, IPC_CREAT | 0660 )) == -1)
        {
                return(-1);
        }
        
        return(qid);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Nótese el uso del modo de permisos {\tt 0660}. Esta pequeña función
retornará, bien un identificador entero de la cola de mensajes, o -1
si hubo error. El valor de la clave (keyval) debe ser el único
argumento de la llamada a la función.



\subsubsection{LLAMADA AL SISTEMA: msgsnd()}


Una vez que tenemos el identificador de la cola, podemos empezar a realizar operaciones sobre ella. Para entregar un mensaje a una cola, use la llamada al
sistema {\tt msgsndl}: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: msgsnd();                                                          

  PROTOTIPO: int msgsnd ( int msqid, struct msgbuf *msgp, int msgsz, int msgflg );
    RETORNA: 0 si éxito  
             -1 si error: errno = EAGAIN (la cola está llena, y se usó IPC_NOWAIT). 
                                  EACCES (permiso denegado, no se puede escribir)
                                  EFAULT (Dirección de memoria de msgp inválida)  
                                  EIDRM  (La cola de mensajes fue borrada)
                                  EINTR  (Se recibió una señal mientras se esperaba para escribir)
                                  EINVAL (Identificador de cola inválido, tipo 
                                          no positivo o tamaño de mensaje inválido)
                                  ENOMEM (No hay memoria suficiente para copiar el buffer)
  NOTAS: 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



El primer argumento de {\tt msgsnd} es nuestro identificador de la cola, devuelto por un llamada previa a {\tt msgget}. El segundo argumento, {\tt msgp}, es un puntero a
nuestro buffer redeclarado y cargado. El argumento msgsz contiene el tamaño del mensaje en bytes, excluye la longitud del tipo de mensaje (4 byte).

El argumento {\tt msgflg} se puede poner a cero (ignorado), o: 

\begin{description}
\item[IPC\_NOWAIT] \mbox{}

Si la cola del mensaje está llena, entonces no se escribe en la cola el mensaje, y se le devuelve el control la proceso llamador. Si no se especifica, entonces el
proceso llamador se suspenderá (bloqueado) hasta que se puede escribir el mensaje. 

\end{description}

Creamos otra función de la envoltura por enviar mensajes:



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int enviar_msj( int qid, struct mymsgbuf *qbuf )
{
        int     resultado, longitud;

        /* La longitud es esencialmente el tama~no de la estructura menos sizeof(mtype) */
        longitud = sizeof(struct mymsgbuf) - sizeof(long);        

        if((resultado = msgsnd( qid, qbuf, length, 0)) == -1)
        {
                return(-1);
        }
        
        return(resultado);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Esta pequeña función intenta enviar un mensaje almacenado en la
dirección pasada ({\tt qbuf}) a la cola de mensajes identificada por
el número pasado en el argumento {\tt qid}. Aquí tenemos un programa
de ejemplo que utiliza las dos funciones que hemos desarrollado aquí:



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

#include <stdio.h>
#include <stdlib.h>
#include <linux/ipc.h>
#include <linux/msg.h>

main()
{
        int    qid;
        key_t  msgkey;
        struct mymsgbuf {
                long    mtype;          /* Tipo de mensaje */
                int     request;        /* Numero de trabajo */
                double  salary;         /* Salario del empleado */
        } msg;

        /* Generamos nuestra clave IPC */
        msgkey = ftok(".", 'm');

        /* Abrir/crear la cola */
        if(( qid = abrir_cola( msgkey)) == -1) {
                perror("abrir_cola");
                exit(1);
        }

        /* Preparar mensajes con datos arbitrarios */
        msg.mtype   = 1;        /* !El mensaje debe ser numero positivo! */   
        msg.request = 1;        /* Dato numero 1 */
        msg.salary  = 1000.00;  /* Dato numero 2 (!mi salario anual!) */
        
        /* !Bombear mensaje! */
        if((enviar_msj( qid, &msg )) == -1) {
                perror("enviar_msj");
                exit(1);
        }
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Tras crear/abrir la cola de mensajes, pasamos a preparar el buffer del
mensaje con datos de prueba ({\em note la falta de datos de tipo carácter
para ilustrar nuestro punto sobre envío de información
binaria\/}). Una simple llamada a {\tt enviar\_msj} envía nuestro mensaje a la cola.

Ahora que tenemos un mensaje en la cola, probemos en comando {\tt
ipcs} para comprobar el estado de ésta. Ahora continuaremos con la
discusión para ver cómo leer información del mensaje. Para ello, se
utiliza la llamada al sistema {\tt msgrcv()}:



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: msgrcv();                                                          
  PROTOTIPO: int msgrcv ( int msqid, struct msgbuf *msgp, int msgsz, long mtype, int msgflg );
    RETURNS: Número de bytes copiados en la cola de mensajes
             -1 si error: errno = E2BIG  (La longitud del mensaje es mayor que msgsz)          
                                  EACCES (No hay permiso de lectura)
                                  EFAULT (La dirección del buffer msgp es incorrecta)
                                  EIDRM  (La cola fue eliminada durante la lectura)
                                  EINTR  (Interrumpido por llegada de señal)
                                  EINVAL (msgqid inválida, o msgsz menor que 0)
                                  ENOMSG (IPC_NOWAIT incluido, y no hay mensajeen la 
                                          cola disponible para leer)           
  NOTAS: 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Obviamente, el primer argumento se usa para especificar la cola utilizada durante el proceso de recuperación del mensaje (se debería haber sido devuelto por
una llamada anterior a {\tt msgget}). El segundo argumento ({\tt msgp}) representa la dirección de una variable buffer de mensaje para guardar el mensaje recuperado.
El tercer argumento, ({\tt msgsz}), representa el tamaño de la
estructura del buffer del mensaje, excluye la longitud del miembro de
{\tt mtype}. Una vez más, se puede
calcular éste fácilmente como:


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
msgsz = sizeof(struct mymsgbuf) - sizeof(long);
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}

El cuarto argumento ({\tt mtype}) especifica el {\em tipo} de mensaje a recuperar de la cola. El núcleo buscará la cola por el último mensaje que cuadra con el tipo, y le
devolverá a una copia de él en la dirección apuntada a por el
argumento {\tt msgp}. Existe un caso especial. Si se pasa el argumento
{\tt mtype} con un valor de ceros,
entonces se devuelve el mensaje más viejo en la cola, independiente del tipo. 

Si se pasa como una bandera {\bf IPC\_NOWAIT}, y no hay ningún mensajes
disponibles, la llamada le devuelve {\tt ENOMSG} al proceso llamador. Por otra parte, el
proceso llamador se bloquea hasta que un mensaje llega a la cola que
satisface el parámetro {\tt msgrcv()}. Si se anula la cola mientras un cliente espera en un
mensaje, se devuelve {\tt EIDRM}. Se devuelve {\tt EINTR} si se coge una señal mientras el proceso está en medio del bloqueo, y espera la llegada de un mensaje. 

Examinamos una función de envoltura rápida para recuperar un mensaje de nuestra cola:


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int leer_msj( int qid, long type, struct mymsgbuf *qbuf )
{
        int     resultado, longitud;

        /* La longitud es esencialmente el tama~no del buffer menos sizeof(long) */
        longitud = sizeof(struct mymsgbuf) - sizeof(long);        

        if((resultado = msgrcv( qid, qbuf, length, type,  0)) == -1)
        {
                return(-1);
        }
        
        return(resultado);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Después de terminar de forma efectiva la recuperación de un mensaje en la cola, se destruye la entrada del mensaje dentro de la cola. 

El bit {\bf MSG\_NOERROR} del argumento {\tt msgflg} proporciona
algunas capacidades adicionales. Si el tamaño de los datos del mensaje
físico es mayor que {\tt msgsz}, y
{\bf MSG\_NOERROR} está indicado, entonces se trunca el mensaje, y se
devuelven sólo msgsz bytes. Normalmente, la llamada al sistema {\tt msgrcv()} devuelve -1
({\bf E2BIG}), y el mensaje quedará en la cola para una recuperación posterior. Esta conducta se puede usar para crear otra función de envoltura, que nos permitirá
``mirar'' en la cola, para ver si un mensaje ha llegado y satisface
nuestra demanda, sin sacarlo realmente de la cola:



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int mirar_msj( int qid, long type )
{
        int     resultado, longitud;

        if((resultado = msgrcv( qid, NULL, 0, type,  IPC_NOWAIT)) == -1)
        {
                if(errno == E2BIG)
                        return(TRUE);
        }
        
        return(FALSE);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Arriba, se dará cuenta de la falta de una dirección de buffer y una longitud. En este caso particular {\em queremos} que la llamada falle. Sin embargo, verificamos por
el retorno de {\bf E2BIG} que indica que existe un mensa del tipo de nuestra petición. La función de envoltura vuelve {\bf TRUE} en éxito, {\bf FALSO} en otro caso.
También observa el uso de {\bf IPC\_NOWAIT}, que previene el compoeramiento de bloque visto antes.



\subsubsection{LLAMADA AL SISTEMA: msgctl()}

Por el desarrollo de las funciones de envoltura anteriores, ahora tiene una aproximación simple y elegante para crear y utilizar las estructuras internas
asociadas con colas de mensaje en sus aplicaciones. Ahora, volveremos directamente a la discusión sobre la manipulación de las estructuras internas
asociadas con una colas de mensaje dada. 

Para realizar operaciones de control en una cola de mensaje, use la
llamada al sistema {\tt msgctl()}. 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: msgctl();
  PROTOTIPO: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
    RETORNA: 0 si éxito  
             -1 si error: errno = EACCES (No hay permiso de lectura y cmd vale IPC_STAT)
                                  EFAULT (Direccion de buf inválida con los comandos IPC_SET y
                                          IPC_STAT)
                                  EIDRM  (La cola fue eliminada durante la operación)
                                  EINVAL (msgqid inválida, o msgsz menor que 0)
                                  EPERM  (Se intentó el comando IPC_SET o IPC_RMID, pero
                                          no tiene acceso de escritura (alteración)   
                                          de la cola)         
  NOTAS: 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Ahora, el sentido común dice que la manipulación directa de las estructuras de datos internas del núcleo podría ocasionar alguna juerga nocturna.
Desgraciadamente, los deberes resultantes por parte del programador se podrían clasificar como diversión sólo si gusta desecha el subsistema IPC. Usando
msgctl() con un conjunto selectivo de órdenes, tiene la posibilidad de manipular esos elementos, que es menos probable que causen problemas. Echemos un
vistazo a estos comandos: 

\begin{description}
\item[{\bf IPC\_STAT}] \mbox{}

Recupera la estructura msqid\_ds para una cola, y, la en la dirección del argumento buff. 

\item[{\bf IPC\_SET}] \mbox{}

Pone el valor del miembro ipc\_perm de la estructura msqid\_ds para la cola. Toma los valores del argumento buf.

\item[{\bf IPC\_RMID}] \mbox{}

Borra la cola del núcleo.

\end{description}

Retomamos nuestra discusión sobre las estructuras de datos internas para colas de mensaje ({\tt msqid\_ds}). El núcleo mantiene una instancia de esta estructura
por cada cola que existe en el sistema. Usando el comando {\bf IPC\_STAT}, podemos recuperar una copia de esta estructura para examinarla. Miramos una
función de envoltura rápida que recuperará la estructura interna y la copia en una dirección pasada: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int leer_queue_ds( int qid, struct msgqid_ds *qbuf )
{
        if( msgctl( qid, IPC_STAT, qbuf) == -1)
        {
                return(-1);
        }
        
        return(0);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Si no podemos copiar el buffer interno, se devuelve -1 a la función que hizo la llamadda. Si todo fue bien, se devuelve un valor 0 (cero), y el buffer pasado debe
contener una copia de la estructura de datos interna para la cola
representada por el identificador de cola pasado ({\tt qid}). 

¿Ahora que tenemos una copia de las estructura de datos interna de una cola, qué se puede manipular, y cómo se puede alterar? El único elemento modificable
en la estructura de los datos es el miembro {\tt ipc\_perm}. Éste contiene los permisos para la cola, así como información sobre el dueño y creador. Sin embargo, los
únicos miembros de la estructura {\tt ipc\_perm} que son modificables son modo, uid, y gid. Puede cambiar el id del usuario del dueño, el id del grupo del dueño, y los
permisos del acceso para la cola. 

Creamos una función de envoltura diseñada para cambiar el modo de una
cola. Se debe pasar el modo en como un array de caracteres (por
ejemplo {\em ``660''}).


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int cambiar_modo_cola( int qid, char *modo )
{
        struct msqid_ds tmpbuf;

        /* Obtener copia de la actual estructura de datos interna */
        leer_queue_ds( qid, &tmpbuf);

        /* Cambiar los permisos usando un viejo truco */
        sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);

        /* Actualizar la estructura de datos interna */
        if( msgctl( qid, IPC_SET, &tmpbuf) == -1)
        {
                return(-1);
        }
        
        return(0);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


Recuperamos una copia de la estructura de datos interna actual
mediante una rápida llamada a nuestra función de envoltura {\tt leer\_queue\_ds}. Entonces hacemos
una llamada a {\tt sscanf()} para alterar el miembro modo de la estructura
msg\_perm asociada. Sin embargo, no se producen cambios hasta que se usa la nueva
copia para actualizar la versión interna. Esto es ejecutado mediante
una llamada a {\tt msgctl()} usando el comando el IPC\_SET. 

{\em ¡TENGA CUIDADO!} ¡Es posible alterar los permisos en una cola, y al hacerlo, puede cerrarse sin darse cuenta. Recuerde, estos objetos IPC no se van a
menos que se quiten propiamente, o el sistema se reinicie. Así, aun
cuando no pueda ver una cola con ipcs no significa que no esté allí.



\begin{quotation}
{\em Para ilustrar este punto, una anécdota algo cómica parece estar a punto. Mientras daba una clase de enseñanza sobre UNIX interno en la Universidad de
Florida Sur, tropecé con un bloque bastante penoso. Había marcado en el servidor del laboratorio la noche de antes, para compilar y probar el trabajo de la clase
de la semana. En el proceso de comprobación, me dí cuenta de que había hecho un typo en la codificación para alterar los permisos en una cola de mensaje.
Creé una cola simple de mensaje, y probé el envío y la recepción
problemas. ¡Sin embargo, cuando intenté cambiar el modo de la cola de
``660'' a ``600'', la
acción resultante fue que se cerró con llave fuera de mi propia cola! Como un resultado, no podía probar la cola de mensaje en la misma área de mi directorio
de fuente. Entonces usé la función ftok() para crear el código IPC codifica, trataba de acceder a una cola para la que no tenía permisos. Acabé y me puse en
contacto con el administrador del sistema local en la mañana siguiente, perdiendo una hora para explicarle a él lo que era una cola del mensaje, y porqué
necesitaba correr los comandos ipcrm.\/}
\end{quotation}

Después de recuperar con éxito un mensaje de la cola, se quita el mensaje. Sin embargo, como mencioné antes, los objetos IPC quedan en el sistema a menos
que se quiten explícitamente, o el sistema sea reiniciado. Por consiguiente, nuestra cola de mensaje hace cola todavía existe dentro del núcleo, disponible para
usarla después de que sólo un simple mensaje desaparezca. Para
completar el ciclo de vida de una cola de mensaje, se deben quitar con
una llamada a {\tt msgctl()},
usando el comando {\bf IPC\_RMID}: 




\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int borrar_cola( int qid )
{
        if( msgctl( qid, IPC_RMID, 0) == -1)
        {
                return(-1);
        }
        
        return(0);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Esta función devuelve 0 si la cola se borró sin problemas, o si no
devuelve un valor -1. El borrado de la cola es atómico, y cualquier
acceso posterior a ella para cualquier cosa fallará.





\subsubsection{msgtool: Un manipulador interactivo de colas de mensajes.}

Pocos pueden negar el beneficio inmediato de tener información técnica exacta rápidamente disponible. Tales materiales proporcionan un mecanismo
tremendo para el aprendizaje y la exploración de áreas nuevas. En la misma nota, teniendo ejemplos reales para acompañar cualquier información técnica,
acelerará y reforzará el proceso de aprendizaje. 

Hasta ahora, los únicos ejemplos útiles que se han presentado eran las funciones de envoltura para manipular colas de mensaje. Aunque son sumamente útiles,
no se han presentado en una manera que garantice su estudio y experimentación. Esto se solucionará con {\em msgtool}, una utilidad de la línea de comando
interactiva parar manipular colas de mensaje IPC. Mientras funciona como una herramienta adecuada para refuerzo de la educación, también se puede aplicar
directamente en asignaciones reales, para proporcionar la funcionalidad de las colas de mensaje mediante script de shell normales.



\paragraph{Vistazo rápido}

El programa {\tt msgtool} cuenta con argumentos de la línea del comando para determinar su comportamiento. Éste es lo que lo hace especialmente útil cuando es
llamado desde script de shell. Se proporcionan todas las capacidades, de crear, enviar, y recuperar, a cambiar los permisos y finalmente eliminar una cola.
Actualmente, usa un array de caracteres para los datos, permitiéndole enviar mensajes textuales. Los cambios para facilitar tipos de datos adicionales se
queda como un ejercicio para el lector. 


\paragraph{Sintaxis de la línea de comandos}




\subparagraph{Envío de mensajes }

\begin{verbatim}
msgtool e (tipo) "texto"
\end{verbatim}





\subparagraph{Recepción de Mensajes}

\begin{verbatim}
msgtool r (tipo)
\end{verbatim}





\subparagraph{Cambio de los permisos}

\begin{verbatim}
msgtool m (modo)
\end{verbatim}





\subparagraph{Borrado de una cola}

\begin{verbatim}
msgtool d 
\end{verbatim}







\paragraph{Ejemplos}

\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
msgtool  e   1 prueba
msgtool  e   5 prueba
msgtool  e   1 "Esto es una prueba"
msgtool  r   1
msgtool  b
msgtool  m   660
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}





\paragraph{Código Fuente}

Seguidamente ofrecemos el código fuente de la utilidad {\tt
msgtool}. Debe compilar sin problemas con cualquier revisión (decente)
del núcleo que soporte IPC Sistema V. ¡Asegúrese de activar el IPC en
el núcleo cuando lo recompile!

Como comentario, esta utilidad {\em creará\/} una cola de mensajes
si no existe, independientemente del tipo de acción solicitado.

\begin{quotation}
{\bf NOTA:} {\em Puesto que esta utilidad usa ftok() para generar
 claves IPC, pueden encontrarse conflictos de directorios. Si cambia
 de directorio durante la ejecución de su script, posiblemente no
 funcione bien. Otra solución sería codificar dentro del programa {\tt
 msgtool} un {\it path} completo a
 la utilidad, tal como ``/tmp/msgtool'', o bien pasarle dicho {\it
 path} mediante un nuevo argumento de la línea de comandos.}
\end{quotation}




\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

/*****************************************************************************
 Parte de la "Guia Linux de Programacion - Capitulo 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULO: msgtool.c
 *****************************************************************************
 Utilidad de manejo de las colas de mensajes del sistema IPC SYSV
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MAX_SEND_SIZE 80

struct mymsgbuf {
        long mtype;
        char mtext[MAX_SEND_SIZE];
};

void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text);
void read_message(int qid, struct mymsgbuf *qbuf, long type);
void remove_queue(int qid);
void change_queue_mode(int qid, char *mode);
void usage(void);


int main(int argc, char *argv[])
{
        key_t key;
        int   msgqueue_id;
        struct mymsgbuf qbuf;

        if(argc == 1)
                usage();

        /* Crear clave unica mediante ftok() */
        key = ftok(".", 'm');

        /* Abrir la cola -- crearla si es necesario */
        if((msgqueue_id = msgget(key, IPC_CREAT|0660)) == -1) {
                perror("msgget");
                exit(1);
        }
        
        switch(tolower(argv[1][0]))
        {
                case 'e': send_message(msgqueue_id, (struct mymsgbuf *)&qbuf,
                                       atol(argv[2]), argv[3]); 
                          break;
                case 'r': read_message(msgqueue_id, &qbuf, atol(argv[2])); 
                          break;
                case 'b': remove_queue(msgqueue_id); 
                          break;        
                case 'm': change_queue_mode(msgqueue_id, argv[2]); 
                          break;

                 default: usage();

        }
        
        return(0);
}

void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text)
{
        /* Enviar mensaje a la cola */
        printf("Enviando mensaje ...\n");
        qbuf->mtype = type;
        strcpy(qbuf->mtext, text);

        if((msgsnd(qid, (struct msgbuf *)qbuf,
                strlen(qbuf->mtext)+1, 0)) ==-1)
        {
                perror("msgsnd");
                exit(1);
        }
}

void read_message(int qid, struct mymsgbuf *qbuf, long type)
{
        /* Leer mensaje de la cola */
        printf("Leyendo mensaje ...\n");
        qbuf->mtype = type;
        msgrcv(qid, (struct msgbuf *)qbuf, MAX_SEND_SIZE, type, 0);
        
        printf("Tipo: %ld Texto: %s\n", qbuf->mtype, qbuf->mtext);
}

void remove_queue(int qid)
{
        /* Borrado de la cola */
        msgctl(qid, IPC_RMID, 0);
}

void change_queue_mode(int qid, char *mode)
{
        struct msqid_ds myqueue_ds;

        /* Obtener informacion actual */
        msgctl(qid, IPC_STAT, &myqueue_ds);

        /* Convertir y cargar el modo */
        sscanf(mode, "%ho", &myqueue_ds.msg_perm.mode);

        /* Actualizar el modo */
        msgctl(qid, IPC_SET, &myqueue_ds);
}

void usage(void)
{
        fprintf(stderr, "msgtool - Utilidad de manejo de colas de mensajes\n");
        fprintf(stderr, "\nUSO: msgtool (e)nviar <tipo> <texto>\n");
        fprintf(stderr, "               (r)ecibir <tipo>\n");
        fprintf(stderr, "               (b)orrar\n");
        fprintf(stderr, "               (m)odo <modo octal>\n");
        exit(1);
}

\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}





















\subsection{Semáforos}




\subsubsection{Conceptos Básicos}

Los semáforos se pueden describir mejor como contadores que se usan para controlar el acceso a recursos compartidos por múltiples procesos. Se usan con
más frecuencia como un mecanismo de cierre para prevenir que los procesos accedan a un recurso particular mientras otro proceso lo está utilizando. Los
semáforos son a menudo considerados como el más difícil asir de los tres tipos de objetos Sistema V IPC. Para comprender totalmente los semáforos, los
discutiremos brevemente antes de comenzar cualquier llamada al sistema y teoría operacional. 

El nombre de {\em semáforo} es realmente un término viejo del ferrocarril, que se usaba para prevenir, en las travesías el cruce en las vías de los viejos carros.
Exactamente lo mismo se puede decir sobre un semáforo. Si el semáforo está {\em abierto} (los brazos en alto), entonces un recurso está disponible (los carros
cruzarían las vías). Sin embargo, si el semáforo está {\em cerrado} (los brazos están abajo), entonces recursos no están disponible (los carros deben esperar). 

Mientras que con este este ejemplo simple nos introduce el concepto, es importante darse cuenta de que los semáforos se llevan a cabo realmente como
{\em conjuntos}, en lugar de como entidades solas. Por supuesto, un conjunto de semáforos dado puede tener sólo un semáforo, como en nuestro ejemplo del
ferrocarril. 

Quizás otra aproximación al concepto de semáforos, sería pensar en ellos como {\em contadores de recursos}. Apliquemos este concepto a otro caso del mundo real.
Considere un {\em spooler} de impresión, capaz de manipular impresoras múltiples, con cada manejo de la impresora con demandas de la impresión múltiples. Un
hipotético manejador del {\em spool} de impresión utilizará un conjunto de semáforos para supervisar el acceso a cada impresora. 

Suponemos que en nuestro cuarto de la impresora de la organización, tenemos 5 impresoreas conectadas. Nuestro manejador del spool asigna un conjunto de
5 semáforos a él, uno por cada impresora del sistema. Como cada impresora es capaz de imprimir físicamente un único trabajo en un instante, cada uno de
nuestros cinco semáforos de nuestro conjunto se inicializará a un
valor de 1 (uno), lo que significa que están todas en línea, y aceptan
trabajos.

Juan envía que una petición de impresión al spooler. El manejador de
la impresión mira los semáforos, y encuentra que el primer
semáforo que tiene un valor de uno. Ante enviar la petición de Juan al aparato físico, el manejador de impresión {\em decrementa} el semáforo de la impresora correspondiente con un valor
negativo (-1). Ahora, el valor de ese semáforo es cero. En el mundo de semáforos Sistema V, un valor de cero representa el 100% utilización del recurso de
ese semáforo. En nuestro ejemplo se no se puede enviar a esa impresora ninguna otra petición hasta que sea distinto de cero. 

Cuando el trabajo de Juan ha finalizado, el gestor de impresión {\em
incrementa\/} el varlor del semáforo correspondiente. Su valor vuelve
a ser uno (1), lo que indica que el recurso vuelve a estar
disponible. Naturalmente, si los cinco semáforos tienen valor cero,
indica que todas las impresoras están ocupadas con peticiones y no se
pueden atender más.

Aunque este es un ejemplo simple, procure no confundirse con el valor
inicial (1) dado a los semáforos. En realidad, cuando se ven como
contadores de recursos, pueden ser iniciados con {\em cualquier valor
positivo\/}, y no están limitados a valer 0 ó 1. Si las impresoras de
nuestro ejemplo fuesen capaces de aceptar 10 trabajos de impresión,
habríamos iniciado sus semáforos con el valor 10, decrementándolo en 1
cada vez que llega un trabajo nuevo y reincrementándolo al terminar
otro. Como descubriremos en este capítulo, el funcionamiento de los
semáforos tiene mucha relación con el sistema de memoria compartida,
actuando como {\em guardianes} para evitar múltiples
escrituras en la misma zona de memoria.

Antes de entrar en las llamadas al sistema relacionadas, repasemos
varias estructuras de datos internas usadas en las operaciones con semáforos.



\subsubsection{Estructuras de datos internas}

Veamos brevemente las estructuras de datos mantenidas por el núcleo para los conjuntos de semáforos. 




\paragraph{Estructura {\tt semid\_ds} del núcleo}

Como en las colas de mensajes, el núcleo mantiene unas estructuras de datos internas especiales por cada conjunto de semáforos dentro de su espacio de
direcciones. Esta estructura es de tipo {\tt semid\_ds} y se define en {\tt
linux/sem.h} como sigue: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
        /* Hay una estructura semid_ds por cada juego de semáforos */
        struct semid_ds {
                struct ipc_perm sem_perm;       /* permisos .. ver ipc.h */
                time_t          sem_otime;      /* ultimo instante semop */
                time_t          sem_ctime;      /* ultimo instante de cambio */
                struct sem      *sem_base;      /* puntero al primer
                                                   semaforo del array */
                struct wait_queue *eventn;
                struct wait_queue *eventz;
                struct sem_undo  *undo;         /* deshacer peticiones
                                                   del array*/
                ushort          sem_nsems;      /* no. de semaforos del array */
        };
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Como con las colas de mensaje, las operaciones con esta estructura son ejecutados por llamadas especiales al sistema especial, y no se deben usar
directamente. Aquí tenemos las descripciones de los campos más interesantes: 


\begin{description}
\item[{\tt sem\_perm}] \mbox{}

Este es un caso de la estructura {\tt ipc\_perm}, que se define en
{\tt linux/ipc.h}. Toma la información de los permisos para el conjunto de semáforos, incluyendo
permisos de acceso e información sobre el creador del conjunto (uid, etc). 

\item[{\tt sem\_otime}] \mbox{}

Instante de la última operación {\tt semop()} (un poco más de esto
dentro de un momento)


\item[{\tt sem\_ctime}] \mbox{}

Instante del último cambio de modo

\item[{\tt sem\_base}] \mbox{}

Puntero al primer semáforo del array (ver siguiente estructura) 

\item[{\tt sem\_undo}] \mbox{}

Número de solicitudes de deshacer en el array (un poco más dentro de
un momento)

\item[{\tt sem\_nsems}] \mbox{}

Número de semáforos en el conjunto (el array) 

\end{description}





\paragraph{Estructura {\tt sem} del núcleo}

En la estructura {\tt semid\_ds}, hay un puntero a la base del array
del semáforo. Cada miembro del array es del tipo estructura
sem. También se define en {\tt linux/sem.h}: 




\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

        /* Una estructura por cada juego de semaforos */
        struct sem {
                short   sempid;         /* pid de ultima operacion */
                ushort  semval;         /* valor actual */
                ushort  semncnt;        /* num. procesos esperando
                                           para incrementarlo */
                ushort  semzcnt;        /* num. procesos esperando
                                           que semval sea 0 */ 
        };
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}




\begin{description}
\item[{\tt sem\_pid}] \mbox{}

El PID (identificador del proceso) que realizó la última operación
 
\item[{\tt sem\_semval}] \mbox{}

Valor actual del semáforo

\item[{\tt sem\_semncnt}] \mbox{}

Número de procesos esperando la disponibilidad del recurso

\item[{\tt sem\_semzcnt}] \mbox{}

Número de procesos esperando la disponibilidad 100% del recurso

\end{description}





\subsubsection{LLAMADA AL SISTEMA: semget()}


Se usa para crear un nuevo conjunto o acceder a uno existente.



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: semget();                                                          

  PROTOTIPO: int semget ( key_t key, int nsems, int semflg );                                             
    RETORNA: Identificador IPC del conjunto, si éxito
             -1 si error: errno = EACCESS (permiso denegado) 
                                  EEXIST (no puede crearse pues existe (IPC_EXCL))
                                  EIDRM (conjunto marcado para borrarse)
                                  ENOENT (no existe el conjunto ni se
                                          indicó IPC_CREAT)
                                  ENOMEM (No hay memoria suficiente para crear)
                                  ENOSPC (Limite de conjuntos excedido)
  NOTAS: 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


El primer argumento de {\tt semget()} es el valor clave (en nuestro caso devuelto por la llamada a {\tt ftok()}). Este valor clave se compara con los valores clave
existentes en el núcleo para otros conjuntos de semáforos. Ahora, las operaciones de apertura o acceso depende del contenido del argumento {\tt semflg}. 

\begin{description}
\item[IPC\_CREAT] \mbox{}

Crea el juego de semáforos si no existe ya en el núcleo.

\item[IPC\_EXCL] \mbox{}

Al usarlo con IPC\_CREAT, falla si el conjunto de semáforos existe ya.

\end{description}

Si se usa {\tt IPC\_CREAT} solo, {\tt semget()}, bien devuelve el identificador del semáforo para un conjunto nuevo creado, o devuelve el identificador para un conjunto
que existe con el mismo valor clave. Si se usa {\tt IPC\_EXCL} junto con {\tt IPC\_CREAT}, entonces o se crea un conjunto nuevo, o si el conjunto existe, la llamada falla
con -1.{\tt IPC\_EXCL} es inútil por sí mismo, pero cuando se combina con {\tt IPC\_CREAT}, se puede usar como una facilidad garantizar que ningún semáforo
existente se abra accidentalmente para accederlo. 

Como sucede en otros puntos del IPC del Sistema V, puede aplicarse a
los parámetros anteriores, un número octal para dar la máscara de
permisos de acceso de los semáforos. Debe hacerse con una operación OR
binaria.

El argumento {\tt nsems} especifica el número de semáforos que se deben crear en un conjunto nuevo. Éste representa el número de impresores en nuestro cuarto
de impresión ficticio descrito antes. El máximo número de semáforos en un conjunto se define en``linux/sem.h'' como:

\begin{verbatim}
        #define SEMMSL  32      /* <=512 max num de semaforos por id */
\end{verbatim}


Observe que el argumento {\tt nsems} se ignora si abre explícitamente
un conjunto existente.

Creemos ahora una función de cobertura para abrir o cerrar juegos de semáforos:



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int abrir_conj_semaforos( key_t clave, int numsems )
{
        int     sid;

        if ( ! numsems )
                return(-1);
        
        if((sid = semget( clave, numsems, IPC_CREAT | 0660 )) == -1)
        {
                return(-1);
        }
        
        return(sid);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Vea que se usan explícitamente los permisos {\tt 0660}. Esta pequeña
función retornará, bien un identificador entero del conjunto de
semáforos, o bien un -1 si hubo un error. En el ejemplo del final de
esta sección, observe la utilización del flag IPC\_EXCL para
determinar si el conjunto de semáforos existe ya o no.




\subsubsection{LLAMADA AL SISTEMA: semop()}

\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: semop();                                                          
  PROTOTIPO: int semop ( int semid, struct sembuf *sops, unsigned nsops);
    RETURNS: 0 si éxito (todas las operaciones realizadas)
             -1 si error: errno = E2BIG (nsops mayor que máx. número de opers.
                                  permitidas atómicamente)
                                  EACCESS (permiso denegado)
                                  EAGAIN (IPC_NOWAIT incluido, la operacion no terminó)
                                  EFAULT (dirección no válida en el parámetro sops)
                                  EIDRM (el conj. de semáforos fue borrado)
                                  EINTR (Recibida señal durante la espera)
                                  EINVAL (el conj. no existe, o semid inválido)
                                  ENOMEM (SEM_UNDO incluido, sin memoria suficiente para    
                                          crear la estructura de retroceso necesaria)
                                  ERANGE (valor del semáforo fuera de rango)
  NOTAS: 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}




El primer argumento de {\tt semget()} es el valor clave (en nuestro
caso devuelto por una llamada a {\tt semget}). El segundo argumento
({\tt sops}) es un puntero a un array
de operaciones para que se ejecuta en el conjunto de semáforo,
mientras el tercer argumento ({\tt nsops}) es el número de operaciones en ese array. 

El argumento {\tt sops} apunta a un array del tipo {\tt sembuf}. Se
declara esta estructura en {\tt linux/sem.h} como sigue: 


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
        /* La llamada al sistema semop usa un array de este tipo */
        struct sembuf {
                ushort  sem_num;        /* posicion en el array */
                short   sem_op;         /* operacion del semaforo */
                short   sem_flg;        /* flags de la operacion */
        };
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}




\begin{description}
\item[{\tt sem\_num}] \mbox{}

Número de semáforo sobre el que desea actuar

\item[{\tt sem\_op}] \mbox{}

Operación a realizar (positiva, negativa o cero)

\item[{\tt sem\_flg}] \mbox{}

Flags (parámetros) de la operación

\end{description}


Si {\tt sem\_op} es negativo, entonces su valor se resta del valor del semáforo. Éste pone en correlación con la obtención de recursos que controla el semáforo o los
monitores de acceso. Si no se especifica {\tt IPC\_NOWAIT}, entonces proceso que efectúa la llamada duerme hasta que los recursos solicitados están disponible
en el semáforo (otro proceso ha soltado algunos). 

Si {\tt sem\_op} es positivo, entonces su valor se añade al semáforo. Éste se pone en correlación con los recursos devueltos al conjunto de semáforos de la
aplicación. ¡Siempre se deben devolver los recursos al conjunto de semáforos cuando ya no se necesiten más!

Finalmente, si {\tt sem\_op} vale cero (0), entonces el proceso que efectúa la llamada dormirá hasta que el valor del semáforo sea 0. Éste pone en correlación la espera
a un semáforo para obtener un 100% de utilización. Un ejemplo bueno de esto sería el funcionamiento de un demonio con permisos de superusuario que podían
ajusta dinámicamente el tamaño del conjunto de semáforos si obtiene
utilización plena.


Para explicar la llamada de {\tt semop}, volvamos a ver nuestro ejemplo de impresión. Supongamos una única una impresora, capaz de único un trabajo en un
instante. Creamos un conjunto de semáforos con único semáforo en él (sólo una impresora), e inicializa ese semáforo con un valor de uno (único un trabajo en
un instante). 

Cada vez que deseemos enviarle un trabajo a esta impresora, primeros necesitamos asegura que el recurso está disponible. Hacemos este para intentar
obtener {\em una unidad\/} del semáforo. Cargamos un array {\tt sembuf} para realizar la operación:


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
        struct sembuf sem_lock = { 0, -1, IPC_NOWAIT };
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



La traducción de la inicialización de la anterior estructura indica que un valor de ``-1'' se añadirá al número del semáforo 0 del conjunto de semáforos. En otras
palabras, se obtendrá una unidad de recursos del único semáforo de
nuestro conjunto (miembro 0). Se especifica {\bf IPC\_NOWAIT}, así la llamada o se produce
inmediatamente, o falla si otro trabajo de impresión está activo en
ese momento. Aquí hay un ejemplo de como usar esta inicialización de
la estructura {\tt sembuf}
con la llamada al sistema {\tt semop}: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
        if((semop(sid, &sem_lock, 1) == -1)
                perror("semop");
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



El tercer argumento ({\tt nsops}) dice que estamos sólo ejecutando una
(1) operación (hay sólo una estructura {\tt sembuf} en nuestra array de operaciones). El
argumento {\tt sid} es el identificador IPC para nuestro conjunto de semáforos. 

Cuando nuestro trabajo de impresión ha terminado, debemos {\em devolver\/} los recursos al conjunto de semáforos, de manera que otros puedan usar la impresora. 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
        struct sembuf sem_unlock = { 0, 1, IPC_NOWAIT };
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



La traducción de la estructura anteriormente inicializada indica que un valor de ``1'' se agrega a semáforo número 0 en el conjunto de semáforos. En otras
palabras, una unidad de recursos se devolverá al conjunto. 





\subsubsection{LLAMADA AL SISTEMA: semctl()}

\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: semctl();                                                          
  PROTOTIPO: int semctl ( int semid, int semnum, int cmd, union semun arg );
    RETURNS: entero positivo si éxito    
             -1 si error: errno = EACCESS (permiso denegado) 
                                  EFAULT (dirección inválida en el argumento arg)     
                                  EIDRM (el juego de semáforos fue borrado)
                                  EINVAL (el conj. no existe, o semid no es válido)
                                  EPERM (El EUID no tiene privilegios para el comando
                                  incluido en arg)
                                  ERANGE (Valor para semáforo fuera de rango)
  NOTAS: Realiza operaciones de control sobre conjuntos de semáforos
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}

La llamada al sistema {\em semctl\/} se usa para desempeñar operaciones de control sobre un conjunto de semáforo. Esta llamada es análoga a la llamada al sistema
{\em msgctl\/} que se usa para operaciones sobre las colas de mensaje. Si usted compara las listas de argumento de las dos llamadas al sistema, notará que la lista para
{\em semctl\/} varía ligeramente con la de {\em msgctl\/}. La rellamada a semáforos actualmente implementada semáforos se implementa realmente conjuntos, más a
entidades simples. Con las operaciones de semáforo operaciones, no sólo hace que se necesite pasar la clave IPC, sino que el semáforo destino dentro de el
conjunto también. 

Las llamados al sistema utilizan un argumento {\em cmd\/}, para la especificación del comando para ser realizado sobre el objeto IPC . La diferencia que resta está en
el argumento final a ambas llamadas. En {\em msgctl\/}, el argumento final representa una copia de las estructuras de datos internos usado por el núcleo. Recuerde que
nosotros usamos esta estructura para recuperar información interna sobre una cola de mensaje, así como también para colocar o cambiar permisos y
propietario de la cola. Con semáforos, se soportan los comandos operacionales adicionales, así requieren unos tipos de estructuras de datos más complejos
como el argumento final. El uso de un tipo {\em union\/} confunde muchos programadores novatos de los semáforo de forma considerable. Nosotros estudiaremos esta
estructura cuidadosamente, en un esfuerzo para impedir cualquier confusión.


El argumento {\tt cmd} representa el comando a ejecutar con el
conjunto. Como puede ver, incluye los conocidos comandos
IPC\_STAT/IPC\_SET, junto a otros específicos de conjuntos de semáforos:

\begin{description}
\item[{\bf IPC\_STAT}] \mbox{}

Obtiene la estructura semid\_ds de un conjunto y la guarda en la
dirección del argumento {\tt buf} en la unión {\tt semun}.

\item[{\bf IPC\_SET}] \mbox{}

Establece el valor del miembro {\tt ipc\_perm} de la estructura
semid\_ds de un conjunto. Obtiene los valores del argumento {\tt buf}
de la unión {\tt semun}.

\item[{\bf IPC\_RMID}] \mbox{}

Elimina el conjunto de semáforos.

\item[{\bf GETALL}] \mbox{}

Se usa para obtener los valores de todos los semáforos del
conjunto. Los valores enteros se almacenan en un array de enteros
cortos sin signo, apuntado por el miembro {\em array\/} de la unión.

\item[{\bf GETNCNT}] \mbox{}

Devuelve el número de procesos que esperan recursos.

\item[{\bf GETPID}] \mbox{}

Retorna el PID del proceso que realizó la última llamada {\em semop\/}.

\item[{\bf GETVAL}] \mbox{}

Devuelve el valor de uno de los semáforos del conjunto.

\item[{\bf GETZCNT}] \mbox{}

Devuelve el número de procesos que esperan la disponibilidad del 100\%
de recurso.

\item[{\bf SETALL}] \mbox{}

Coloca todos los valores de semáforos con una serie de valores
contenidos en elmiembro {\em array\/} de la unión. 

\item[{\bf SETVAL}] \mbox{}

Coloca el valor de un semáforo individual con el miembro {\em val\/} de la unión. 

\end{description}

El argumento {\tt arg} representa un ejemplo de tipo {\tt semun}. Esta
unión particular se declara en {\tt linux/sem.h} como se indica a
continuación: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
        /* argumento para llamadas a semctl */
        union semun {
                int val;                /* valor para SETVAL */
                struct semid_ds *buf;   /* buffer para IPC_STAT e IPC_SET */
                ushort *array;          /* array para GETALL y SETALL */
                struct seminfo *__buf;  /* buffer para IPC_INFO */
                void *__pad;
        };
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}




\begin{description}
\item[{\tt val}] \mbox{}

Se usa con el comando SETVAL, para indicar el valor a poner en el semáforo.

\item[{\tt buf}] \mbox{}

Se usa con los comandos IPC\_STAT/IPC\_SET. Es como una copia de la
estructura de datos interna que tiene el núcleo para los semáforos.

\item[{\tt array}] \mbox{}

Puntero que se usa en los comandos GETALL/SETALL. Debe apuntar a una
matriz de números enteros donde se ponen o recuperan valores de los semáforos.

\end{description}


Los demás argumentos, {\em \_\_buf\/} y {\em \_\_pad\/}, se usan
internamente en el núcleo y no son de excesiva utilidad para el
programador. Además son específicos para el sistema operativo Linux y
no se encuentran en otras versiones de UNIX.

Ya que esta llamada al sistema es de las más complicadas que hemos
visto, pondremos diversos ejemplos para su uso.

La siguiente función devuelve el valor del semáforo indicado. El
último argumento de la llamada (la unión), es ignorada con el comando
GETVAL por lo que no la incluimos:



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int obtener_sem_val( int sid, int semnum )
{
        return( semctl(sid, semnum, GETVAL, 0));
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Volviendo al ejemplo de las impresoras, así obtendremos el estado de
las cinco máquinas:


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

        #define MAX_IMPR 5

        uso_impresoras()
        {
                int x;

                for(x=0; x<MAX_IMPR; x++)
                        printf("Impresora %d: %d\n\r", x, obtener_sem_val( sid, x ));
        }
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Considérese la siguiente función, que se debe usar para iniciar un
nuevo semáforo:



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
void iniciar_semaforo( int sid, int semnum, int initval)
{
        union semun semopts;    

        semopts.val = initval;
        semctl( sid, semnum, SETVAL, semopts);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


Observe que el argumento final de {\em semctl\/} es una copia de la unión, más que un puntero a él. Mientras nosotros estamos en el tema de la unión como argumento,
me permito demostrar una equivocación más bien común cuando usa este llamado de sistema. 

Recordamos del proyecto msgtool que los comandos IPC\_STAT e IPC\_SET se usaron para alterar los permisos sobre la cola. Mientras estos comandos se
soportan, en la implementación de un semáforo implementación, su uso es un poco diferente, como las estructuras de datos internas e recuperan y copian desde
un miembro de la unión, más bien que una entidad simple. ¿ Puede
encontrar el error en este código? 


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

/* Los permisos se pasan como texto (ejemplo: "660") */

void changemode(int sid, char *mode)
{
        int rc;
        struct semid_ds mysemds;

        /* Obtener valores actuales */
        if((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1)
        {
                perror("semctl");
                exit(1);
        }
                
        printf("Antiguos permisos: %o\n", semopts.buf->sem_perm.mode);
                
        /* Cambiar los permisos del semaforo */
        sscanf(mode, "%o", &semopts.buf->sem_perm.mode);

        /* Actualizar estructura de datos interna */
        semctl(sid, 0, IPC_SET, semopts);

        printf("Actualizado...\n");
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



El código intenta de hacer una copia local de las estructuras de datos internas estructura para el conjunto, modifica los permisos, e IPC\_SET los devuelve al
núcleo. Sin embargo, la primera llamada a {\em semctl\/} devuelve EFAULT, o dirección errónea para el último argumento (¡la unión!). Además, si no hubíeramos
verificado los errores de la llamada, nosotros habríamos conseguido un fallo de memoria. ¿Por qué? 

Recuerde que los comandos IPC\_SET/IPC\_STAT usan el miembro {\em
buf\/} de la unión, que es un puntero al tipo {\em semid\_ds\/}. ¡Los punteros,
son punteros, son punteros y
son punteros! El miembro {\em buf\/} debe indicar alguna ubicación válida de almacenamiento para que nuestra función trabaje adecuadamente. Considere esta
versión: 


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

void cambiamodo(int sid, char *mode)
{
        int rc;
        struct semid_ds mysemds;

        /* Obtener valores actuales de estructura interna */

        /* !Antes de nada apuntar a nuestra copia local! */
        semopts.buf = &mysemds;

        /* !Intentemos esto de nuevo! */
        if((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1)
        {
                perror("semctl");
                exit(1);
        }
                
        printf("Antiguos permisos: %o\n", semopts.buf->sem_perm.mode);
                
        /* Cambiar permisos */
        sscanf(mode, "%o", &semopts.buf->sem_perm.mode);

        /* Actualizar estructura interna */
        semctl(sid, 0, IPC_SET, semopts);

        printf("Actualizado...\n");
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}







\subsubsection{semtool: Manipulador interactivo de semáforos}






\paragraph{Vistazo Rápido}

El programa {\tt semtool} usa la línea de comandos para determinar su
comportamiento: es especialmente útil en los guiones de {\it
shell}. Incluye todas las operaciones posibles para un conjunto de
semáforos y puede usarse para controlar recursos compartidos desde los
guiones de {\it shell}.




\paragraph{Sintaxis de la utilidad}




\subparagraph{Creación de un conjunto de semáforos}

\begin{verbatim}
semtool c (número de semáforos en el conjunto)
\end{verbatim}





\subparagraph{Bloqueo de un semáforo}

\begin{verbatim}
semtool b (número de semáforo a bloquear)
\end{verbatim}





\subparagraph{Desbloqueo de un semáforo}

\begin{verbatim}
semtool d (número de semáforo a liberar)
\end{verbatim}





\subparagraph{Cambio de los permisos (modo)}

\begin{verbatim}
semtool m (modo)
\end{verbatim}





\subparagraph{Borrado de un conjunto de semáforos}

\begin{verbatim}
semtool b
\end{verbatim}







\paragraph{Ejemplos}

\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
semtool  c 5
semtool  b
semtool  d
semtool  m 660
semtool  b
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}





\paragraph{Código fuente}

\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

/*****************************************************************************
 Parte de la "Guia Linux de Programacion - Capitulo 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULO: semtool.c
 *****************************************************************************
 Utilidad de manejo de semaforos del sistema IPC SYSV

 *****************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define SEM_RESOURCE_MAX        1       /* Valor inicial de todo semaforo */

void opensem(int *sid, key_t key);
void createsem(int *sid, key_t key, int members);
void locksem(int sid, int member);
void unlocksem(int sid, int member);
void removesem(int sid);
unsigned short get_member_count(int sid);
int getval(int sid, int member);
void dispval(int sid, int member);
void changemode(int sid, char *mode);
void usage(void);

int main(int argc, char *argv[])
{
        key_t key;
        int   semset_id;

        if(argc == 1)
                usage();

        /* Crear clave IPC unica */
        key = ftok(".", 's');

        switch(tolower(argv[1][0]))
        {
                case 'c': if(argc != 3)
                                usage();
                          createsem(&semset_id, key,  atoi(argv[2]));
                          break;
                case 'b': if(argc != 3)
                                usage();
                          opensem(&semset_id, key);
                          locksem(semset_id, atoi(argv[2]));
                          break;
                case 'd': if(argc != 3)
                                usage();
                          opensem(&semset_id, key);
                          unlocksem(semset_id, atoi(argv[2]));
                          break;
                case 'b': opensem(&semset_id, key);
                          removesem(semset_id);
                          break;        
                case 'm': opensem(&semset_id, key);
                          changemode(semset_id, argv[2]);
                          break;        
                 default: usage();

        }
        
        return(0);
}

void opensem(int *sid, key_t key)
{
        /* Abrir (no crear!) el conjunto de semaforos */

        if((*sid = semget(key, 0, 0666)) == -1) 
        {
                printf("No existe el conjunto de semaforos!\n");
                exit(1);
        }

}

void createsem(int *sid, key_t key, int members)
{
        int cntr;
        union semun semopts;

        if(members > SEMMSL) {
                printf("Lo siento: el numero maximo de semaforos es de: %d\n",
                        SEMMSL);
                exit(1);
        }

        printf("Intentando crear un conjunto de %d miembros\n",
                                members);

        if((*sid = semget(key, members, IPC_CREAT|IPC_EXCL|0666))
                        == -1) 
        {
                fprintf(stderr, "El conjunto ya existe!\n");
                exit(1);
        }

        semopts.val = SEM_RESOURCE_MAX;
        
        /* Iniciar todos los miembros (puede hacerse con SETALL) */        
        for(cntr=0; cntr<members; cntr++)
                semctl(*sid, cntr, SETVAL, semopts);
}

void locksem(int sid, int member)
{
        struct sembuf sem_lock={ 0, -1, IPC_NOWAIT};

        if( member<0 || member>(get_member_count(sid)-1))
        {
                fprintf(stderr, "miembro %d fuera de rango\n", member);
                return;
        }

        /* Intentamos bloquear el conjunto */
        if(!getval(sid, member))
        {
                fprintf(stderr, "Recursos del semaforo agotados (no bloqueo)!\n");
                exit(1);
        }
        
        sem_lock.sem_num = member;
        
        if((semop(sid, &sem_lock, 1)) == -1)
        {
                fprintf(stderr, "Fallo en bloqueo\n");
                exit(1);
        }
        else
                printf("Recursos decrementados en 1 (bloqueo)\n");

        dispval(sid, member);
}

void unlocksem(int sid, int member)
{
        struct sembuf sem_unlock={ member, 1, IPC_NOWAIT};
        int semval;

        if( member<0 || member>(get_member_count(sid)-1))
        {
                fprintf(stderr, "miembro %d fuera de rango\n", member);
                return;
        }

        /* Esta bloqueado? */
        semval = getval(sid, member);
        if(semval == SEM_RESOURCE_MAX) {
                fprintf(stderr, "Semaforo no bloqueado!\n");
                exit(1);
        }

        sem_unlock.sem_num = member;

        /* Intentamos desbloquear */
        if((semop(sid, &sem_unlock, 1)) == -1)
        {
                fprintf(stderr, "Fallo en desbloqueo\n");
                exit(1);
        }
        else
                printf("Recursos incrementados en 1 (desbloqueo)\n");

        dispval(sid, member);
}

void removesem(int sid)
{
        semctl(sid, 0, IPC_RMID, 0);
        printf("Semaforo borrado\n");
}

unsigned short get_member_count(int sid)
{
        union semun semopts;
        struct semid_ds mysemds;

        semopts.buf = &mysemds;

        /* Devolver numero de miembros */
        return(semopts.buf->sem_nsems);
}

int getval(int sid, int member)
{
        int semval;

        semval = semctl(sid, member, GETVAL, 0);
        return(semval);
}

void changemode(int sid, char *mode)
{
        int rc;
        union semun semopts;
        struct semid_ds mysemds;

        /* Obtener valores de la estructura interna */
        semopts.buf = &mysemds;

        rc = semctl(sid, 0, IPC_STAT, semopts);

        if (rc == -1) {
                perror("semctl");
                exit(1);
        }
                
        printf("Permisos antiguos: %o\n", semopts.buf->sem_perm.mode);
                
        /* Cambiar los permisos */
        sscanf(mode, "%ho", &semopts.buf->sem_perm.mode);

        /* Actualizar estructura interna */
        semctl(sid, 0, IPC_SET, semopts);

        printf("Actualizado...\n");

}

void dispval(int sid, int member)
{
        int semval;

        semval = semctl(sid, member, GETVAL, 0);
        printf("El semval del miembro %d es %d\n", member, semval);
}

void usage(void)
{
        fprintf(stderr, "semtool - Utilidad de manejo de semaforos\n");
        fprintf(stderr, "\nUSAGE:  semtool (c)rear <cuantos>\n");
        fprintf(stderr, "                 (b)loquear <sem #>\n");
        fprintf(stderr, "                 (d)esbloquear <sem #>\n");
        fprintf(stderr, "                 (b)orrar\n");
        fprintf(stderr, "                 (m)odo <modo>\n");
        exit(1);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}







\subsubsection{semstat: utilidad para semtool}

Como regalo final, incluimos el código fuente de una utilidad
adicional llamada {\tt semstat}. Este programa muestra los valores de
cada semáforo del conjunto creado con {\tt semtool}.



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

/*****************************************************************************
 Parte de la "Guia Linux de Programacion - Capitulo 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULO: semstat.c
 *****************************************************************************
 semstat muestra el estado de los semaforos manejados con semtool
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int get_sem_count(int sid);
void show_sem_usage(int sid);
int get_sem_count(int sid);
void dispval(int sid);

int main(int argc, char *argv[])
{
        key_t key;
        int   semset_id;

        /* Obtener clave IPC unica */
        key = ftok(".", 's');

        /* Abrir (no crear!) el conjunto de semaforos */
        if((semset_id = semget(key, 1, 0666)) == -1) 
        {
                printf("El conjunto no existe\n");
                exit(1);
        }

        show_sem_usage(semset_id); 
        return(0);
}

void show_sem_usage(int sid)
{
        int cntr=0, maxsems, semval;

        maxsems = get_sem_count(sid);

        while(cntr < maxsems) {
                semval = semctl(sid, cntr, GETVAL, 0);
                printf("Semaforo #%d:  --> %d\n", cntr, semval);
                cntr++;
        }
}

int get_sem_count(int sid)
{
        int rc;
        struct semid_ds mysemds;
        union semun semopts;

        /* Obtener valores de la estructura interna */
        semopts.buf = &mysemds;

        if((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1) {
                perror("semctl");
                exit(1);
        }

        /* devolver numero de semaforos del conjunto */
        return(semopts.buf->sem_nsems);
}

void dispval(int sid)
{
        int semval;

        semval = semctl(sid, 0, GETVAL, 0);
        printf("semval vale %d\n", semval);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}













\subsection{Memoria Compartida}






\subsubsection{Conceptos Básicos}

La memoria compartida se puede describir mejor como el plano (mapping) de un área (segmento) de memoria que se combinará y compartirá por más de un
de proceso. Esta es por mucho la forma más rápida de IPC, porque no hay intermediación (es decir, un tubo, una cola de mensaje, etc). En su lugar, la
información se combina directamente en un segmento de memoria, y en el espacio de direcciones del proceso llamante. Un segmento puede ser creado por un
proceso, y consecutivamente escrito a y leído por cualquier número de
procesos.



\subsubsection{Estructuras de Datos Internas y de Usuario}

Echemos un vistazo a las estructuras de datos que mantiene el núcleo
para cada segmento de memoria compartida.


\paragraph{Estructura {\tt shmid\_ds} del Núcleo}

Como con la cola de mensaje y los conjuntos de semáforos, el núcleo mantiene unas estructuras de datos internas especiales para cada segmento compartido
de memoria que existe dentro de su espacio de direcciones. Esta
estructura es de tipo {\tt shmid\_ds}, y se define en {\tt
linux/shm.h} como se indica a continuación:


\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
        /* Por cada segmento de memoria compartida, el nucleo mantiene
           una estructura como esta */
        struct shmid_ds {
                struct ipc_perm shm_perm;        /* permisos operacion */
                int     shm_segsz;               /* tamanyo segmento (bytes) */
                time_t  shm_atime;               /* instante ultimo enlace */
                time_t  shm_dtime;               /* instante ult. desenlace */
                time_t  shm_ctime;               /* instante ultimo cambio */
                unsigned short  shm_cpid;        /* pid del creador */
                unsigned short  shm_lpid;        /* pid del ultimo operador */
                short   shm_nattch;              /* num. de enlaces act. */

                                                 /* lo que sigue es privado */

                unsigned short   shm_npages;     /* tam. segmento (paginas) */
                unsigned long   *shm_pages;      /* array de ptr. a marcos -> SHMMAX */ 
                struct vm_area_struct *attaches; /* descriptor de enlaces */
        };
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


Las operaciones sobre esta estructura son realizadas por una llamada especial al sistema, y no deberían ser realizadas directamente. Aquí se describen de los
campos más importantes: 

\begin{description}
\item[{\tt shm\_perm}] \mbox{}

Este es un ejemplo de la estructura {\tt ipc\_perm}, que se define en
{\tt linux/ipc.h}. Esto tiene la información de permisos para el segmento, incluyendo los permisos de
acceso, e información sobre el creador del segmento (uid, etc). 

\item[{\tt shm\_segsz}] \mbox{}

Tamaño del segmento (en bytes).

\item[{\tt shm\_atime}] \mbox{}

Instante del último enlace al segmento por parte de algún proceso.

\item[{\tt shm\_dtime}] \mbox{}

Instante del último desenlace del segmento por parte de algún proceso.

\item[{\tt shm\_ctime}] \mbox{}

Instante del último cambio de esta estructura (cambio de modo, etc).

\item[{\tt shm\_cpid}] \mbox{}

PID del proceso creador.

\item[{\tt shm\_lpid}] \mbox{}

PID del último proceso que actuó sobre el segmento.

\item[{\tt shm\_nattch}] \mbox{}

Número de procesos actualmente enlazados con el segmento.

\end{description}





\subsubsection{LLAMADA AL SISTEMA: shmget()}

Para crear un nuevo segmento de memoria compartida, o acceder
a una existente, tenemos la llamada al sistema {\tt shmget()}.



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: shmget();                                                          

  PROTOTIPO: int shmget ( key_t key, int size, int shmflg );                                             
    RETORNA: si éxito, ident. de segmento de memoria compartida                                                          
             -1 si error: errno = EINVAL (Tam. de segmento invalido)      
                                  EEXIST (El segmento existe, no puede crearse)
                                  EIDRM (Segmento borrado o marcado para borrarse)      
                                  ENOENT (No existe el segmento) 
                                  EACCES (Permiso denegado) 
                                  ENOMEM (No hay memoria suficiente)          
  NOTAS: 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}


Esta llamada particular debería parecer casi como vieja conocida a estas alturas. Es parecido a las correspondientes para las colas de mensaje y conjuntos de
semáforos. 

El argumento primero de {\tt shmget()} es el valor clave (en nuestro
caso vuelto por una llamada a {\tt ftok()}). Este valor clave se compara entonces a valores claves
existentes que existen dentro de el núcleo para los otros segmentos compartidos de memoria. En esta situación, las operaciones de apertura o de acceso
dependen de los contenidos del argumento {\tt shmflg}.

\begin{description}
\item[IPC\_CREAT] \mbox{}

Crea un segmento si no existe ya en el núcleo.

\item[IPC\_EXCL] \mbox{}

Al usarlo con IPC\_CREAT, falla si el segmento ya existe.

\end{description}

Si se usa {\tt IPC\_CREAT} sin nada más, {\tt shmget()} retornará,
bien el identificador del segmento recién creado, o bien el de un
segmento que existía ya con la misma clave IPC. Si se añade el comando
{\tt IPC\_EXCL}, en caso de existir ya el segmento fallará, y si no se
creará.

De nuevo, puede añadirse un modo de acceso en octal, mediante la
operación OR.

Preparemos una función recubridora para crear o localizar segmentos de
memoria compartida:



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
int abrir_segmento( key_t clave, int tamanyo )
{
        int     shmid;
        
        if((shmid = shmget( clave, tamanyo, IPC_CREAT | 0660 )) == -1)
        {
                return(-1);
        }
        
        return(shmid);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Observe el uso de los permisos explícitos {\tt 0660}. Esta sencilla
función retornará un entero con el identificador del segmento, o -1 si
hay error. Los argumentos son, el valor de la clave IPC y el tamaño
deseado para el segmento (en bytes).

Una vez que un proceso obtiene un identificador de segmento válido, el
siguiente paso es {\em mapear\/} ({\tt attach}) el segmento en su
propio espacio de direcciones.




\subsubsection{LLAMADA AL SISTEMA: shmat()}



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: shmat();                                                          

  PROTOTIPO: int shmat ( int shmid, char *shmaddr, int shmflg);
    RETORNA: dirección de acceso al segmento, o                       
             -1 si error: errno = EINVAL (Identificador o dirección inválidos)          
                                  ENOMEM (No hay memoria suficiente para ligarse)
                                  EACCES (Permiso denegado)
  NOTAS: 
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}




Si el argumento {\tt addr} es nulo (0), el núcleo intenta encontrar
una zona no mapeada. Es la forma recomendada de hacerlo. Se puede
incluir una dirección, pero es algo que solo suele usarse para
facilitar el uso con hardware propietario o resolver conflictos con
otras aplicaciones. El flag SHM\_RND puede pasarse con un OR lógico en
el argumento {\tt shmflg} para forzar una dirección pasada para ser
página (se redondea al tamaño más cercano de página).

Además, si se hace OR con el flag SHM\_RDONLY y con el argumento de
banderas, entonces el segmento compartido de memoria se mapea, pero
marcado como sólo lectura.

Esta llamada es quizás la más simple de usar. Considere esta función de envoltura, que se pasa un identificador IPC válido para un segmento, y devuelve la
dirección a la que el segmento está enlazado: 



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}

char *ligar_segmento( int shmid )
{
        return(shmat(shmid, 0, 0));
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Una vez un segmento ha sido adecuadamente adjuntado, y un proceso tiene un puntero al comienzo del segmento, la lectura y la escritura en el segmento llegar
a ser tan fácil como simplemente referenciar el puntero. ¡Tenga cuidado de no perder el valor del puntero original! Si esto sucede, no habrá ninguna manera de
acceder a la base (comienzo) del segmento. 




\subsubsection{LLAMADA AL SISTEMA: shmctl()}



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: shmctl();
  PROTOTYPE: int shmctl ( int shmqid, int cmd, struct shmid_ds *buf );
    RETURNS: 0 si éxito
             -1 si error: errno = EACCES (No hay permiso de lectura y cmd es IPC_STAT)
                                  EFAULT (Se ha suministrado una dirección inválida 
                                  para los comandos IPC_SET e IPC_STAT)
                                  EIDRM  (El segmento fue borrado durante esta operación)
                                  EINVAL (shmqid inválido)
                                  EPERM  (Se ha intentado, sin permiso de escritura,
                                          el comando IPC_SET o IPC_RMID)
      NOTAS:
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Esta llamada particular se usa tras la llamada {\em msgctl\/} solicitando colas de mensaje. En vista de este hecho, no se discutirá en detalle
demasiado. Los que valores válidos de comando son: 

\begin{description}
\item[{\bf IPC\_STAT}] \mbox{}

Obtiene la estructura shmid\_ds de un segmento y la almacena en la
dirección del argumento buf.

\item[{\bf IPC\_SET}] \mbox{}

Ajusta el valor del miembro ipc\_perm de la estructura, tomando el
valor del argumento buf.

\item[{\bf IPC\_RMID}] \mbox{}

Marca un segmento para borrarse.

\end{description}

El comado IPC\_RMID no quita realmente un segmento del núcleo. Más
bien, {\em marca\/} el segmento para eliminación. La eliminación real del mismo ocurre
cuando el último proceso actualmente adjunto al segmento termina su relación con él. Por supuesto, si ningún proceso está actualmente asociado al segmento,
la eliminación es inmediata. 

Para separar adecuadamente un segmento compartido de memoria, un
proceso invoca la llamada al sistema {\tt shmdt}. 




\subsubsection{LLAMADA AL SISTEMA: shmdt()}



\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
  LLAMADA AL SISTEMA: shmdt();                                                          

  PROTOTIPO: int shmdt ( char *shmaddr );
    RETURNS: -1 si error: errno = EINVAL (Dir. de enlace inválida)        
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}



Cuando un segmento compartido de memoria no se necesita más por un proceso, se debe separar con una llamado al sistema. Como mencionamos antes, esto
no es lo mismo que eliminar un segmento desde el núcleo! Después de separar con éxito, el miembro shm\_nattch de la estructura shmid\_ds se decrementa en
uno. Cuando este valor alcanza el cero (0), el núcleo quitará físicamente el segmento. 



\subsubsection{shmtool: Manipulador de segmentos de memoria compartida}






\paragraph{Vistazo rápido}

Nuestro ejemplo final de objetos Sistema V IPC serán las {\tt shmtool}, que es una herraminenta de línea de comando para crear, leer, escribir, y borrar
segmentos compartidos de memoria. Una vez más, como en los ejemplos previos, el segmento se crea durante cualquier operación, si no existía
anterioramente. 





\paragraph{Sintaxis del comando}




\subparagraph{Escribir cadenas en el segmento}

\begin{verbatim}
shmtool e "text"
\end{verbatim}





\subparagraph{Leer cadenas del segmento}

\begin{verbatim}
shmtool l
\end{verbatim}





\subparagraph{Cambiar permisos (modo)}

\begin{verbatim}
shmtool m (mode)
\end{verbatim}





\subparagraph{Borrado del segmento}

\begin{verbatim}
shmtool b
\end{verbatim}







\paragraph{Ejemplos}

\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
shmtool  e   prueba
shmtool  e   "Esto es una prueba"
shmtool  l   
shmtool  b
shmtool  m   660
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}





\paragraph{Código Fuente}

\par
\addvspace{\medskipamount}
\nopagebreak\hrule
\begin{verbatim}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SEGSIZE 100

main(int argc, char *argv[])
{
        key_t key;
        int   shmid, cntr;
        char  *segptr;

        if(argc == 1)
                usage();

        /* Obtener clave IPC */
        key = ftok(".", 'S');

        /* Abrir (crear si es necesario) el segmento de memoria compartida */
        if((shmid = shmget(key, SEGSIZE, IPC_CREAT|IPC_EXCL|0666)) == -1) 
        {
                printf("El segmento existe - abriendo como cliente\n");

                /* El segmento existe - abrir como cliente */
                if((shmid = shmget(key, SEGSIZE, 0)) == -1) 
                {
                        perror("shmget");
                        exit(1);
                }
        }
        else
        {
                printf("Creando nuevo segmento\n");
        }

        /* Ligar el proceso actual al segmento */
        if((segptr = shmat(shmid, 0, 0)) == -1)
        {
                perror("shmat");
                exit(1);
        }
        
        switch(tolower(argv[1][0]))
        {
                case 'e': writeshm(shmid, segptr, argv[2]);
                          break;
                case 'l': readshm(shmid, segptr);
                          break;
                case 'b': removeshm(shmid);
                          break;
                case 'm': changemode(shmid, argv[2]);
                          break;
                 default: usage();

        }
}

writeshm(int shmid, char *segptr, char *text)
{
        strcpy(segptr, text);
        printf("Hecho...\n");
}

readshm(int shmid, char *segptr)
{
        printf("valor de segptr: %s\n", segptr);
}

removeshm(int shmid)
{
        shmctl(shmid, IPC_RMID, 0);
        printf("Segmento marcado para borrado\n");
}

changemode(int shmid, char *mode) 
{
        struct shmid_ds myshmds;

        /* Obtener valor actual de la estructura de datos interna */
        shmctl(shmid, IPC_STAT, &myshmds);

        /* Mostrar antiguos permisos */
        printf("Antiguos permisos: %o\n", myshmds.shm_perm.mode);

        /* Convertir y cargar el modo */ 
        sscanf(mode, "%o", &myshmds.shm_perm.mode);

        /* Actualizar el modo */
        shmctl(shmid, IPC_SET, &myshmds);

        printf("Nuevos permisos : %o\n", myshmds.shm_perm.mode);
}

usage()
{
        fprintf(stderr, "shmtool - Utilidad para usar memoria compartida\n");
        fprintf(stderr, "\nUSAGE:  shmtool (e)scribir <texto>\n");
        fprintf(stderr, "                (l)eer\n");
        fprintf(stderr, "                (b)orrar\n");
        fprintf(stderr, "                (m)odo <modo en octal>\n");
        exit(1);
}
\end{verbatim} 
\nopagebreak\hrule 
\addvspace{\medskipamount}




\endinput
