As I understand, you have two programs in C. Let's call the first "producer.c" (I'm going to assume that you write a data in the pipeline) and the second "consumer.c" (I'm going to assume that you read a data from the pipe, process it in some way, for example, add 1, and write it again in the pipeline, where the producer reads it from). I understand that you compile both to generate executables productor
and consumidor
, respectively. The productor
after starting and creating the pipe, makes fork()
and exec()
to load consumidor
. Right?
In that case, obviously you can not from consumidor
access the variable p
created in productor
, because it is a variable of a different process. It will be useless to declare it as extern
. When you make fork()
, the created child does have access (for now) to the variable p
, since it is an identical copy of the parent, including that variable that had already been initialized. But at the moment you use exec()
, the image of the child is destroyed (and with it the variable p
) to be replaced by the loaded image of the disk, consumidor
.
Possible solutions:
Use a named pipe instead of a unnamed pipe . Named pipes are created by calling mknod()
. These pipes appear as a file in the file system (in the folder of your choice). Although the variable to access it is different in the producer and the consumer, if both open the same "file" ( named pipe ) they can write and read from it to communicate.
After creating the child with fork()
and before running exec()
, redirect the standard input and output of the child to point to the pipe that you had created in the father. Then you make the exec()
to load the consumidor
. Everything that consumidor
reads from its standard input, it will actually be reading from the pipe, and everything you type in its standard output will be actually writing it in the pipeline. The descriptors of the standard input and output are 0 and 1 respectively. You do not need the variable p
.
Example of the first approach
productor.c
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#define NOMBRE_TUBERIA "/tmp/tuberia"
int main()
{
int tuberia;
char resultado[2];
// El padre comienza creando la tuberia (la borra antes por si ya existiera)
unlink(NOMBRE_TUBERIA);
tuberia = mknod(NOMBRE_TUBERIA, S_IRUSR| S_IWUSR|S_IFIFO, 0);
if (tuberia<0) {
perror("mknod");
exit(2);
}
// Ahora lanzará al hijo
if (fork()==0) {
execl("./consumidor", "consumidor", NULL);
}
// El padre abre la tubería para leer y escribir
tuberia = open(NOMBRE_TUBERIA, O_RDWR);
if (tuberia<0) {
perror("open");
exit(2);
}
// Escribe un dato en la tuberia
printf("Enviando un 5\n");
write(tuberia, "5", 1);
// Esperamos para leer la respuesta del hijo
sleep(1);
printf("Esperando respuesta\n");
read(tuberia, resultado, 1);
resultado[1]=0; // Añadir terminador
printf("Resultado: %s\n", resultado);
return 0;
}
consumidor.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#define NOMBRE_TUBERIA "/tmp/tuberia"
int main()
{
int tuberia;
char resultado[2];
int dato;
// El hijo no crea la tubería (ya la creó el padre)
// Simplemente la abre para leer y escribir
tuberia = open(NOMBRE_TUBERIA, O_RDWR);
if (tuberia<0) {
perror("open");
exit(2);
}
// Leemos un dato, y lo convertimos a entero
read(tuberia, resultado, 1);
resultado[1]=0; // Añadir terminador
dato = atoi(resultado);
dato++;
// Convertimos a cadena el resultado y la escribimos en la tubería
sprintf(resultado, "%d", dato);
write(tuberia, resultado, 1);
return 0;
}
Example of the second approach
This is about using unnamed pipes, which are the ones created with pipe()
. The problem is that the pipe is unidirectional. You can use it to send things from the producer to the consumer or vice versa, but not both at the same time. To have bidirectional communication, you need two pipes, which complicates the producer a bit:
productor.c
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
int p_escribir[2]; // Hacen falta dos tuberias, una para enviar cosas al consumidor
int p_leer[2]; // la otra para recibir resultados
char resultado[2];
pipe(p_escribir);
pipe(p_leer);
// Ahora lanzará al hijo
if (fork()==0) {
// el hijo, antes de cambiarse por el consumidor, redirige su
// entrada y salida estándar
dup2(p_escribir[0],0 ); // La entrada estándar será el pipe donde escriba el padre
dup2(p_leer[1], 1); // La salida estándar será el pipe de donde lea el padre
// Despues deben cerrarse todos los pipes
close(p_escribir[0]);
close(p_escribir[1]);
close(p_leer[0]);
close(p_leer[1]);
// Ahora cambiamos el código del hijo, cargando el consumidor
execl("./consumidor", "consumidor", NULL);
}
// El padre sigue por aqui
// Ha de cerrar los extremos del pipe que usa el hijo
close(p_escribir[0]);
close(p_leer[1]);
// Escribe un dato en la tuberia
printf("Enviando un 5\n");
write(p_escribir[1], "5", 1);
// Esperamos para leer la respuesta del hijo
sleep(1);
printf("Esperando respuesta\n");
read(p_leer[0], resultado, 1);
resultado[1]=0; // Añadir terminador
printf("Resultado: %s\n", resultado);
return 0;
}
consumidor.c
The consumer, on the other hand, is very simple, since he does not know anything about the father's machinations. It is limited to reading from its standard input and writing to its standard output.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
char resultado[2];
int dato;
// El hijo simplemente usa su entrada/salida estándar,
// pues ya las tiene conectadas a la tuberia
// Leemos un dato de la entrada estándar (0), y lo convertimos a entero
read(0, resultado, 1);
resultado[1]=0; // Añadir terminador
dato = atoi(resultado);
dato++;
// Convertimos a cadena el resultado y la escribimos en la salida estándar (1)
sprintf(resultado, "%d", dato);
write(1, resultado, 1);
return 0;
}
Result
Either approach produces the same result. The father, after throwing the consumer, writes a 5 in the pipe, waits a second and reads the pipeline showing the result (which has been put there by the consumer). This would be seen in the shell :
$ ./productor
Enviando un 5
Esperando respuesta
Resultado: 6