T Procesos Pipes Ango
T Procesos Pipes Ango
T Procesos Pipes Ango
PROCESOS
1. OBJETIVO
2. DESCRIPCIÓN
Identificación de procesos:
pid_t getpid(void);
pid_t getppid(void);
⇒Ejemplo:
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
void main(void)
{
pid_t id_proceso;
pid_t id_padre;
id_proceso = getpid();
id_padre = getppid();
p1_prueba1.c
1
Además de la identificación del proceso, un proceso tiene asociado un identificador de usuario
real (identificador de usuario del propietario), un identificador de usuario efectivo (que
determina los privilegios que un proceso tiene cuando se ejecuta), un identificador de grupo (al
que pertenece el usuario) y un identificador de grupo efectivo.
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);
Devuelve el identificador del grupo efectivo del proceso que realiza la llamada.
⇒ Ejemplo:
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
void main(void)
{
printf("Identificador de usuario: %d\n", getuid());
printf("Identificador de usuario efectivo: %d\n", geteuid());
printf("Identificador de grupo: %d\n", getgid());
printf("Identificador de grupo efectivo: %d\n", getegid());
}
p1_prueba2.c
Creación de procesos:
La llamada a fork lo que hace es crear una copia del proceso que ha realizado
la llamada. Se puede decir que se realiza una clonación del proceso. El
proceso que hace la llamada a fork se convierte en el proceso padre del
proceso creado. Una vez realizada la copia, tanto padre e hijo continúan de
forma independiente la ejecución en el mismo punto del programa, es decir, en
2
la siguiente instrucción al fork. Es un error pensar que el hijo comienza la
ejecución por el principio del programa. Esto es así porque el proceso hijo
hereda del padre los datos y la pila que tuviera en el momento de la
ejecución del fork, así como el valor de los registros.
⇒Ejemplo:
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
void main(void)
{
pid_t pid;
pid = fork();
switch(pid)
{
case -1: /* error del fork() */
perror("fork");
break;
case 0: /* proceso hijo */
printf("Proceso %d; padre = %d \n", getpid(), getppid());
break;
default: /* padre */
printf("Proceso %d; padre = %d \n", getpid(), getppid());
}
}
p1_prueba3.c
Ejecución de un programa:
El servicio exec tiene por objetivo cambiar el programa que está ejecutando un proceso. No es
como el servicio fork ya que exec no crea un nuevo proceso, sino que permite que un proceso
pase a ejecutar un programa diferente. Recuerde que fork crea un nuevo proceso que ejecuta el
mismo programa que el proceso padre.
Estas funciones reemplazan la imagen del proceso actual por una nue va imagen. Esta imagen se
construye a partir de un archivo ejecutable (file). Si la llamada se ejecuta con éxito, no devolverá
ningún valor puesto que la imagen del proceso habrá sido reemplazada, es decir, de una llamada
con éxito no hay retorno, en caso contrario devuelve –1.
El argumento arg es la dirección del primer elemento de una cadena de caracteres que contiene el
primer argumento que se le pasa al programa. Los puntos suspensivos indican que hay que poner
también los demás argumentos de la misma forma.
3
El argumento argv es la dirección de comienzo de un vector de cadenas de caracteres que
contiene los argumentos pasados al programa y debería acabar con un NULL.
La diferencia entre execlp y execvp es únicamente la forma de dar los argumentos del programa.
donde argc representa el número de argumentos que se pasan al programa, incluido el propio
nombre del programa, y argv es un vector de cadenas de caracteres, conteniendo cada elemento
de este vector un argumento pasado al programa. El primer componente de este vector (argv[0])
representa el nombre del programa.
⇒ Ejemplo de execlp:
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
void main(void)
{
pid_t pid;
int status;
pid = fork();
switch(pid)
{
case -1: /* error del fork() */
perror("fork");
break;
case 0: /* proceso hijo */
execlp("ls","ls","- l",NULL);
perror("exec");
break;
default: /* padre */
printf("Proceso padre\n");
}
}
p1_prueba4.c
4
⇒ Ejemplo de execvp:
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
argumentos[0] = "ls";
argumentos[1] = "- l";
argumentos[2] = NULL;
pid = fork();
switch(pid)
{
case -1: /* error del fork() */
perror("fork");
break;
case 0: /* proceso hijo */
execvp(argumentos[0], argumentos);
perror("exec");
break;
default: /* padre */
printf("Proceso padre\n");
}
}
p1_prueba5.c
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
pid = fork();
switch(pid)
{
case -1: /* error del fork() */
perror("fork");
break;
case 0: /* proceso hijo */
if (execvp(argv[1], &argv[1])< 0)
5
perror("exec");
break;
default: /* padre */
printf("Proceso padre\n");
}
}
p1_prueba6.c
El servicio wait permite a un proceso padre esperar hasta que termine la ejecución de un proceso
hijo. El proceso padre se queda bloqueado hasta que termina un proceso hijo. La forma de wait
es la siguiente:
Esta llamada espera la finalización de un proceso hijo y permite obtener información sobre el
estado de terminación del mismo. Devuelve el identificador del proceso hijo cuya ejecución ha
finalizado. Si status es distindo de NULL, entonces se almacena en esta variable información
relativa al proceso que ha terminado. Se puede usar esta variable para ver como ha terminado el
proceso utilizando macros. Algunas de estas macros son las siguientes:
⇒ Ejemplo:
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
pid = fork();
switch(pid)
{
case -1: /* error del fork() */
6
perror("fork");
break;
case 0: /* proceso hijo */
if(argc>1)
if (execvp(argv[1], &argv[1]) < 0)
perror("exec");
break;
default: /* padre */
while (wait(&valor) != pid);
if (valor == 0)
printf("El mandato se ejecuto de forma normal\n");
else
{
if (WIFEXITED(valor))
printf("El hijo termino normalmente y su valor devuelto fue %d\n",
WEXITSTATUS(valor));
if (WIFSIGNALED(valor))
printf("El hijo termino al recibir la señal %d\n", WTERMSIG(valor));
}
}
}
p1_prueba7.c
7
COMUNICACIÓN BÁSICA ENTRE PROCESOS
1. OBJETIVO
Tuberías:
Una tubería es un canal de comunicación entre procesos emparentados (padre e hijo). El acceso
se realiza como en una cola (FIFO)
Formato:
#include <unistd.h>
int pipe (int descriptores[2]);
Parámetros:
Tabla que recibirá los descriptores de entrada (0, para lectura) y de
salida (1, para escritura) de la tubería
Devuelve:
0 si se ha completado correctamente; -1 si error
#include <unistd.h>
#define LECTURA 0
#define ESCRITURA 1
int main()
{
8
}
}
p2_prueba4.c
9
PIPES CON NOMBRE
1. OBJETIVO
2. DESCRIPCIÓN
Los pipes (tuberías) con nombre son una vía de intercambio de datos. Igual que los pipes
estudiados se gestionan mediante el método FIFO, es decir, el primer byte introducido por el
emisor será el primer byte en ser extraído por el receptor. Las tuberías con nombre también
reciben el nombre de FIFOs. También son una vía de comunicación unidireccional como los
pipes.
Creación:
Hay varias formas de crear un FIFO. Se puede crear mediante mkfifo o bien mediante mknod.
Tanto mkfifo como mknod se pueden utilizar desde la línea de órdenes o bien llamando al
servicio correspondiente, como se detalla a continuación:
donde se está creando un FIFO de nombre mi mififo con permisos de acceso para el usuario y
para el grupo de lectura y escritura.
♦ Servicio mkfifo:
10
El prototipo del servicio que permite crear una tubería con nombre es el siguiente:
Los parámetros proporcionados son el nombre del FIFO y los permisos asociados. La llamada
devuelve 0 si se ejecutó con éxito o –1 en caso de error. Por ejemplo:
Crea el FIFO de nombre mififo con permisos de lectura y escritura para el usuario y el grupo.
mknod sirve para crear ficheros especiales. Un FIFO es considerado un fichero especial.
Se indica el nombre, los permisos y el tipo de fichero especial, que para la creación de un FIFO
debe ser p indicando que es un pipe. Por ejemplo:
♦ Servicio mknod:
Los parámetros proporcionados son el nombre del FIFO y el modo de acceso incluyendo los
permisos (para un FIFO se debe indicar S_IFIFO), el tercer parámetro en un FIFO es ignorado.
La llamada devuelve 0 si se ejecutó con éxito o –1 en caso de error. Por ejemplo:
Crea el FIFO de nombre mififo con permisos de lectura y escritura para el usuario y el grupo.
Una vez creada una tubería con nombre se utiliza exactamente como un fichero. Recordamos a
continuación los servicios utilizados:
Apertura:
El primer argumento indica el nombre del FIFO y el segundo la forma en la que se va a acceder.
Los posibles valores de flag son los siguientes:
11
O_RDWR: se obre para realizar operaciones de lectura y escritura.
El servicio open devuelve un descriptor de archivo que se puede utilizar para leer y escribir del
FIFO. En el caso de error devuelve –1. La llamada open bloquea al proceso que la ejecuta hasta
que haya algún otro proceso en el otro extremo del FIFO. Si no interesa este comportamiento, se
puede usar la bandera O_NONBLOCK en la llamada a open para desactivar esta opción por
defecto.
Cierre:
El argumento es el descriptor del archivo que se quiere cerrar, en este caso, el descriptor del
FIFO devuelto en la apertura. La llamada devuelve 0 si se ejecutó con éxito o –1 en caso de
error.
Lectura:
El primer argumento es el descriptor del FIFO. El segundo argumento es la dirección del buffer
de usuario donde se van a guardar los datos leídos. El último argumento es el número de bytes
que se quieren leer. La llamada devuelve el número de bytes leídos o –1 en caso de error.
Escritura:
Una tubería debe tener un lector y un escritor. Si un proceso trata de escribir en una tubería que
no tiene lectores asociados, el núcleo enviará la señal SIGPIPE.
NOTA: también se pueden utilizar las funciones fopen, fclose, fputs, fgets … que
utilizan FILE * en vez de int para indicar un fichero.
Borrado:
12
Esta llamada pospone la destrucción del FIFO hasta que todos los procesos que lo estén
utilizando lo hayan cerrado con la función close.
Para borrar una tubería con nombre también se pueden utilizar la orden correspondiente del
sistema operativo para el borrado de ficheros (rm).
⇒ Ejemplo: El siguiente programa crea un fifo de nombre mififo. En cada iteración del bucle
lee una cadena enviada por el proceso escritor.
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main(void)
{
int fp;
char buffer[TAM_BUF];
int nbytes;
mknod(NOMBREFIFO,S_IFIFO|0660,0);
while(TRUE)
{
fp=open(NOMBREFIFO,O_RDONLY);
nbytes=read(fp,buffer,TAM_BUF-1);
buffer[nbytes]='\0';
printf("Cadena recibida: %s \n",buffer);
close(fp);
}
return 0;
}
p3_prueba1.c
if(argc!=2)
printf("uso: %s cadena \n", argv[0]);
13
else if ((fp=open(NOMBREFIFO,O_WRONLY))==-1)
perror("fopen");
else
{
write(fp,argv[1],strlen(argv[1]));
close(fp);
result=0;
}
return result;
p3_prueba2.c
Ejercicio:
14