|
IPC es un medio de comunicación entre procesos.
Diferentes implementaciones de UNIX (y, por tanto, de Linux), soportan
distintos tipos de IPC. Los tipos más comunes son tuberías (pipes),
FIFOs (first in, first out), streamed pipes, named pipes, colas de
mensajes (message queues), semáforos, memoria compartida (shared memory
sockets / streams). Algunos de esto soportan full duplex, pero en caso
de que la portabilidad sea importante se debe pensar en comunicación
half duplex en general. Este tutorial se centra en el uso de semáforos
según la especificación de System V.
Identificador de una estructura IPC
Un programa puede crear o acceder a una estructura IPC (como
una cola de mensajes, un semáforo o memoria compartida) de distintas formas:
- Crear una estructura IPC utilizando la clave
(key)
IPC_PRIVATE, lo que garantiza trabajar con una
estructura nueva. Un padre puede devolver un valor de
retorno (el identificador de la estructura IPC) a un hijo cuando se efectúa
una llamada a 'fork', o con la ayuda de un archivo para almacenar el
identificador obtenido.
- En caso de que el identificador sea conocido desde el
principio, la clave se puede establecer en un archivo de cabecera común
mediante una macro, de modo que todos los procesos que utilicen la estructura
compartan esa información. En consecuencia, no sería necesario pasar la clave
del padre al hijo ni habría que buscar un identificador único en tiempo de
ejecución. No obstante, el identificador se debe establecer al compilar, y se
debe garantizar que sea único. Si se trata de crear una
estructura ya existente, la función get correspondiente devuelve un
error.
- Un identificador único se puede basar en la ruta de acceso
y el identificador del proyecto (un valor entre 0 y 255), con la ayuda de la
función ftok (ver man ftok).
La siguiente tabla presenta un resumen de las principales
operaciones. En los siguientes puntos se entra en
profundidad en el caso de los semáforos:
| |
Cola de mensajes |
Semáforos |
Memoria compartida |
| archivo de cabecera |
<sys/msg.h> |
<sys/mem.h> |
<sys/shm.h> |
| llamada al sistema
para crear o abrir |
msgget() |
semget() |
shmget() |
| llamada al sistema para
operaciones de control |
msgctl() |
semctl() |
shmctl() |
| llamada para operaciones de IPC |
msgsnd(); msgrcv() |
semop() |
shmat(); shmdt() |
Tabla 1: Operaciones
disponibles
Creación y control de semáforos
La función que permite crear o acceder a un semáforo es
semget. Los parámetros son una
clave (key),
un entero nsems con el número de
semáforos en el grupo (set), normalmente solo uno, y un entero flags
con varios bits de control. A
continuación presentamos la cabecera de la función:
#include <sys/sem.h>
int semget (key_t key, int nsems, int flags); // los flags se describen más abajo
// Devuelve el identificador del semáforo o -1 si ocurre un error (dando un valor a errno)
/* errno = EACCESS (permiso denegado)
EEXIST (La estructura existe, no se puede crear)
EIDRM (La estructura está marcada para ser borrada)
ENOENT (La estructura no existe)
ENOMEM (Memoria insuficiente para crear la estructura)
ENOSPC (Excedido el número máximo de estructuras)
*/
|
Listado: semget
Las tres funciones get
disponibles para colas de mensajes, semáforos y memoria compartida (msgget, semget, and shmget)
tienen dos parámetros similares: una
clave
(key) y un
entero flag. Se creará una nueva estructura
IPC si se da alguno de los siguientes casos:
- key es IPC_PRIVATE
- key no está asociada a una estructura IPC del tipo
correspondiente. El comportamiento en este caso depende
del valor de flag según se muestra en la tabla:
| parámetro flag |
key no existe |
key ya existe |
| ningún flag |
error, errno = ENOENT |
OK |
| IPC_CREAT |
OK, crea nueva estructura |
OK |
| IPC_CREAT | IPC_EXCL |
OK, crea nueva estructura |
error, errno = EEXIST |
Table 2: Reglas para la creación de
estructuras IPC
La función semget convierte
la clave dada en un identificador que representa un grupo de semáforos,
numerados comenzando por cero. No obstante, los semáforos no quedan listos para
ser usados tras una llamada a semget. Necesitan ser inicializados con una
llamada a semctl, una función diseñada para controlar el grupo de
semáforos. A continuación se presenta esta función, junto con sus estructuras de
datos asociadas, tal y como se definen en sem.h:
#include <sys/sem.h>
int semctl (int semid, int semnum, int cmd, union semun arg); // los flags se describen más abajo
// Devuelve el identificador del semáforo o -1 si ocurre un error (dando un valor a errno)
/* errno = EACCESS (permiso denegado)
EEXIST (La estructura existe, no se puede crear)
EIDRM (La estructura está marcada para ser borrada)
ENOENT (La estructura no existe)
ENOMEM (Memoria insuficiente para crear la estructura)
ENOSPC (Excedido el número máximo de estructuras)
*/
|
Listado: semctl
union semun {
int val; // entero
struct semid_ds *buf; // puntero a estructura
unsigned short *array; // array
};
|
Listado: union semun
struct semid_ds {
struct ipc_perm sem_perm; //
estructura con los permisos
unsigned short sem_nsems; //
numero de semáforos en el grupo
time_t sem_otime;
// fecha y hora de la última llamada a semop()
time_t sem_ctime;
// fecha y hora de la última llamada a semctl()
}; |
Listado: struct semid_ds
Los parámetros de la función son, respectivamente, el
identificador del grupo de semáforos, el número del semáforo sobre el que se
actúa, el comando que se quiere realizar y una unión que contiene información
(parámetros) para ese comando. Recuerde que una unión se diferencia de una
estructura en que contiene la información correspondiente a uno y solo uno de
sus campos. Los posibles comandos, es decir, los posibles valores de cmd
son los siguientes:
| cmd |
efecto |
| IPC_STAT |
Obtiene la estructura semid_ds
de un grupo, y la almacena en la
dirección del puntero buf
definido en la unión
semun. |
| IPC_SET |
Establece el valor de la
estructura ipc_perm
perteneciente a la estructura
semid_ds de un grupo.
Toma el valor a establecer de buf
en la unión semun. |
| IPC_RMID |
Elimina el grupo del kernel. |
| GETALL |
Devuelve los valores de todos los semáforos de un
grupo. Los valores enteros obtenidos se almacenan en un array de
unsigned short integers apuntado por
el puntero array
de la unión semun. |
|
GETNCNT |
Devuelve el número de procesos que están esperando la
asignación de recursos. |
| GETPID |
Devuelve el PID del proceso que realizó la ultima
llamada a
semop(). |
| GETVAL |
Devuelve el valor de un semáforo individual
perteneciente al grupo. |
|
GETZCNT |
Devuelve el número de procesos que están esperando a
que se utilicen el 100% de los semáforos. |
| SETALL |
Asigna valores a todos los semáforos del grupo
utilizando los valores contenidos a partir de la dirección especificada
por el puntero array definido en la unión. |
| SETVAL |
Establece el valor de un semáforo individual
perteneciente al grupo a partir del valor del entero
val definido en la unión. |
Tabla 3: Valores
de cmd
Estructura de permisos
Las colas de mensajes, los semáforos y la memoria compartida
tienen varias características comunes. En primer lugar, el identificador de la
estructura es siempre un entero positivo. Ese identificador debe ser conocido
para poder trabajar con la estructura. Aunque muy
similar, este procedimiento no es exactamente igual que el manejo de archivos,
puesto que el identificador es un valor que se incrementa cada vez que se define
una nueva estructura, hasta que alcanza el valor máximo y vuelve a un valor
"próximo" a cero. Incluso si la estructura se elimina, el valor se incrementa
para las próximas nuevas estructuras, con lo que el valor del identificador
antiguo se mantiene. La información sobre la estructura IPC (por ejemplo un
grupo de semáforos), se almacena en la estructura
ipc_perm que se muestra a continuación:
struct ipc_perm
{
key_t key; /* tipo de dato primitivo del sistema, normalmente definido como long integer en <sys/types.h> */
ushort uid; /* uid (usuario) efectiva y gid (grupo) efectiva el propietario */
ushort gid;
ushort cuid; /* uid (usuario) efectiva y gid (grupo) efectiva del creador */
ushort cgid;
ushort mode; /* modo de acceso */
ushort seq; /* slot usage sequence number */
};
|
Listado: ipc_perm
Operación de los semáforos
Ahora que somos capaces de crear, inicializar y realizar
algunas operaciones de control sobre nuestros semáforos, es el momento de operar
con ellos. Para ello se utiliza la función
semop:
#include <sys/sem.h>
int semop (int semid, struct sembuf *sops, size_t nsops);
// Devuelve 0 en caso de éxito y -1 en caso de error (dendo un valor a errno)
|
Listado: semop
struct sembuf {
unsigned short sem_num; /* numero del semáforo */
short sem_op; /* operación sobre el semáforo */
short sem_flg; /* flags de la operación */
}; |
Listado: struct sembuf
El primer parámetro de la función es
el identificador del grupo de semáforos. Puesto que esta función
actúa sobre cualquier número de semáforos, es necesario construir un
array con las operaciones a realizar aunque solo se esté utilizando
un semáforo del grupo. Este array es un array de struct
sembuf, y el segundo parámetro que recibe la
función es un puntero a su primer elemento. Finalmente, nsops
es el número de semáforos sobre los que se actúa,
normalmente solo uno.
La estructura
sembuf tiene un
primer campo para el número del semáforo afectado y un segundo para
la operación a realizar, con el siguiente significado:
| Valor
de sem_op |
Significado |
| > 0 |
El valor de sem_op
se añade al valor del semáforo (operación
'release') |
| < 0 |
El valor absoluto de sem_op
se resta del valor del semáforo, a no ser que
esa operación haga que el valor sea negativo. En ese caso, la
llamada se bloquea hasta que el valor pueda ser restado sin
producir un valor negativo (operación 'wait') |
| == 0 |
La llamada se bloquea hasta que el valor del
semáforo sea cero |
Tabla 4:
Operaciones con el seáforo
Limitaciones y comandos adicionales
Las limitaciones de las estructuras IPC
se deben principalmente al kernel del SO.
Aun así, las estructuras IPC descritas aquí (por ejemplo, las
colas de mensajes) no se eliminan automáticamente, sino que permanecen
almacenadas en el kernel hasta que son eliminadas manualmente o el sistema se
reinicia. Puede ser interesante, por tanto, acceder a las estructuras o incluso
listarlas, para lo que algunos comandos adicionales son útiles.
El comando ipcs (consulte man ipcs)
permitir obtener el status de todas las estructuras IPC System V:
| ipcs -q |
Mostrar solo semáforos |
| ipcs -m
|
Mostrar solo colas de mensajes |
| ipcs -s |
Mostrar solo memoria compartida |
| ipcs --help |
Mostrar ayuda sobre otras opciones |
Tabla 5:
comando ipcs
El comando ipcrm permite
eliminar una estructura IPC del kernel.
Aunque las estructuras se pueden eliminar mediante llamadas al
sistema en un programa en C, al desarrollar un programa puede ser necesario
hacerlo manualmente:
ipcrm <msg | sem | shm> <IPC ID>
Simplemente se ha de especificar si el elemento es una
cola de mensajes (msg),
un grupo de semáforos (sem),
o un segmento de memoria compartida (shm),
junto con el identificador
IPC ID que se puede
obtener mediante el comando ipcs.
Información adicional
Puede encontrar más información sobre semáforos y las funciones
proporcionadas por Linux para manejarlos, junto con varios ejemplos,
en este
tutorial. Revise también las
referencias bibliográficas que se
proporcionan. |