I want to be able to fork a process and have the child and parent have a bi-directional link using pipes. I create 2 pipes and make parent read from the end of 1st pipe and write to the beginning of the second and vice versa but I'm running into some issues.
A short version of the code is here (error checking omitted)
void PlayGame(int in, int out, int first, int id){
FILE *inStream = fdopen(in, "r");
FILE *outStream = fdopen(out, "w");
if (first) fputc( id, outStream);
while(1){
int c = fgetc(inStream);
printf("process %d has read %d\n", id, c);
fputc( id, outStream);
}
}
int main (void){
int fd[2];
int fd1[2];
pipe(fd);
pipe(fd1);
pid_t pid = fork();
if (pid == 0){
PlayGame(fd[0], fd1[1], 0, 1);
exit(0);
}
PlayGame(fd1[0], fd[1], 1, 2);
exit(0);
}
What I want to achieve is that a parent writes a character to the pipe and the child waits until it receives a char and then writes its response and waits again for the parent. What am I doing wrong here?
Both the parent and the child get stuck at the first call to
int c = fgetc(inStream);
stdio (fputc and friends) are buffered by default, meaning that the fputc() doesn't actually write the byte to the pipe, but stores it in-memory to be written out when the buffer is later flushed.
You can either do an fflush(outStream) after the fputc, or do a setvbuf(outStream, NULL, _IONBF, 0); after the fdopen in order to turn off buffering on that file.
Related
To learn how Pipe IPC mechanism works, I wrote a simple program that creates two child processes which share data using a pipe. The first child process has to read data from a file and pass it to the pipe.
Afterwards, the second child process has to read it, convert it to uppercase and write it to another file. The read system call in the second child process returns -1 when reading from the pipe. Also when I execute the program, in some cases printf in the first child does not print anything and in other cases printf in the second child does not print, too. Could you please point the mistakes in the program which are causing the problems?
int main(int args[], char * argv[]) {
int fd[2];
long length;
char buff1[250];
char buff2[250];
FILE * fptr1;
FILE * fptr2;
pid_t A, B;
pipe(fd);
A = fork();
if (A == -1) {
printf("error in fork of A\n");
exit(1);
}
if (A == 0) {
fptr1 = fopen(argv[1], "r"); // program receives file names as argument
if (fptr1 == NULL) {
printf("Erro in file open1\n");
exit(1);
}
fseek(fptr1, 0 L, SEEK_END);
length = ftell(fptr1);
fseek(fptr1, 0 L, SEEK_SET);
close(fd[0]);
fread(buff1, length, 1, fptr1);
buff1[length] = '\0';
printf("buff1 = %s", buff1);
write(fd[1], buff1, length);
fclose(fptr1);
exit(0);
} else {
B = fork();
if (B == -1) {
printf("Error in forking child B");
exit(1);
}
if (B == 0) {
fptr2 = fopen(argv[2], "w");
if (fptr2 == NULL) {
printf("Error in file open2\n");
exit(1);
}
close(fd[1]);
int n = read(fd[0], buff2, length);
printf("n = %d\n", n);
upper_string(buff2); // converts characters to uppecase
fwrite(buff2, 1, length, fptr2);
fclose(fptr2);
}
}
return 0;
}
There are few things to take into account here. First thing i would like to point is that you do not need to use two fork() calls. In that case you have three processes working in parallel (parent process and two child process, one per each fork() call).
One important point to take into account when you work with processes working in parallel is synchronism. In your code you are creating two processes. Parent process does not wait for any of its child, so it finishes its execution, and if child processes have not finished, they will become child of init process. But appart from that, you have the typical producer consumer problem. One of your child produce something and the other consume it, but how they work in parallel, consumer need to know that the product is ready to be consumed. So, in this case, i think the easiest way to do this job is to use just one fork(), so child become the producer and the parent process (the consumer) wait until its child finish the job.
I'm trying to exec()a child process from a parent process. In this child process, I ask the user to enter a message so it can be printed out by the parent process but I can't find a way to do it...
So far, my code is :
parent.c
int main(int argc, char **argv) {
int fd[2];
char line[80];
pipe(fd);
pid_t pid = (pid_t)fork();
if(pid > 0) {
waitpid(pid, NULL, 0);
close(fd[0]);
int size = read(fd[1], line, 79);
close(fd[1]);
line[size] = '\0';
printf("[parent] Received \"%s\", size = %d\n", line, size);
}
else {
close(fd[1]);
close(stdin);
dup2(fd[0], stdin);
close(fd[0]);
exec("./child", 0, NULL);
}
return 0;
}
child.c
int main(int argc, char **argv) {
char line[80];
printf("[child] Enter message: ");
gets(line, 80);
printf("[child] line = %s\n", line);
return 0;
}
When I launch the parent process, it displays [child] Enter message: but when I try to type something, nothing appears, even if I hit the return key.
Do you know how can I make it work ?
Thank you for your help.
Besides the problems mentioned in my comments, the problem you are experiencing is a deadlock. You get that because the parent process waits for the child process to exit. But the child process is waiting for input that never arrives.
That's because in the child process you say that the input should come from the pipe.
Furthermore in the parent process you attempt to read from the write-end of the pipe.
Finally, your program will never work as long as the child process is wanting to read user-input, because all user-input will go to the parent process.
To make it all work, you need to rethink your design, and to make the parent process the one that reads input from the user, and writes to the pipe. And the child process should read from the pipe and print to (the non-piped) standard output. Or you close the (normal, non-piped) standard input in the parent, and in the child you write to the pipe (as standard output).
Your code has several problems:
you mix integer (low level) file descriptors, and (high level) FILE * constants.
you invert stdin and stdout as well as the order of the descriptors obtained through pipe
you write a message on stdout when you intend to redirect it (should use stderr)
Here is a fixed version (I changed your gets by fgets, as well as your exec with execl to have everything to compile):
parent.c:
int main(int argc, char **argv) {
int fd[2];
char line[80];
pipe(fd);
pid_t pid = (pid_t)fork();
if(pid > 0) {
waitpid(pid, NULL, 0);
close(fd[1]);
int size = read(fd[0], line, 79);
close(fd[0]);
line[size] = '\0';
printf("[parent] Received \"%s\", size = %d\n", line, size);
}
else {
close(fd[0]);
close(1);
dup2(fd[1], 1);
close(fd[1]);
execl("./child", NULL);
}
return 0;
}
child.c
int main(int argc, char **argv) {
char line[80];
fprintf(stderr, "[child] Enter message: ");
fgets(line, 80, stdin);
printf("[child] line = %s\n", line);
return 0;
}
I've found a workaround. My actual goal was to pass information between parent and child. My error was to dup2 on stdin. By doing this, I couldn't enter anything when the program asked. So I passed the value of the file descriptors in the argv and my problem was solved.
Thanks again for your help !
Iam trying to create a code with 1 parent and 2 childrens. The method recive 3 parameters:
original_file word1 word2
The parent read a file line by line:
If the line is pair, send the line to method proccess_pair and the word1.
If the line contains the word1, save the lines in the file_1.txt
If the line is odd, send the line to method proccess_odd and the word2.
If the line contains the word1, save the lines in the file_2.txt
Im beginner in c, and i trying with this:
int p_h1[2] // pipe from parent to child1
int p_h2[2];// pipe from parent to child2
int main(int argc, char **argv){
pid_t pdi1, pdi2;
FILE *fd; // for original file
FILE *p_h1f, *p_h2f; //file create for child1 and child2 respectively
char buffer[1024];//buffer
if (pid1<0){
fprintf(stderr,"Error fork \n %s \n",strerror(errno));
exit(EXIT_FAILURE);
}
else if (pid1==0){//Im the child1
//proccess for child 1
proccess_pair(arg[2]);
exit(EXIT_SUCCESS);
}
pid2 = fork();
if (pid2<0){
fprintf(stderr,"Error fork \n %s \n",strerror(errno));
exit(EXIT_FAILURE);
}
else if (pid2==0){//Im the child2
//proccess for child 2
proccess_odd(arg[2]);
exit(EXIT_SUCCESS);
}
//Parent dont read from pipe
close(p_h1[0]);
close(p_h2[0]);
fd = fopen(argv[1],"r"); //I openthe file for read it;
p_h1f = fdopen(p_h1[1],"w")
p_h2f = fdopen(p_h2[1],"w")
int i = 1;
while(fgets(buffer,1024,fd) != NULL){
if (i % 2 ==0){ //check if the lines is pairs
fputs(buffer,p_h1f);
fflush(p_h1f);
}else{
fputs(buffer,p_h2f);
fflush(p_h2f);
}
i++;
}
close(p_h1[1]);
close(p_h2[1]);
fclose(fd);
wait(NULL);
wait(NULL);
}
Both methods(for chil1 and chil2) will be the same(but closing the correct sides of pipes), for this reason i only implement one of them:
void proccess_pair(char *word1){
FILE *fd;
fd = fopen("file_1.txt","w");
//closing the not used
close(p_h1[1]);
close(p_h2[1]);
close(p_h2[0]);
int nsto = dup(1)//duplicate the stdout
dup2(fd,1);//changing stdout->file_1.txt
execlp("grep","grep",word1,NULL);//execution of grep
exit(EXIT_SUCCESS);
}
Im learning and i know that i have many many error, for this reason i need help.
Regards
How i can create many pipes in an array in c?
On a POSIX-conformant system, you can do that by calling pipe() many times on elements of a 2D array of int, as you presented.
¿I can use two different pipes(parent-child1,parent-child2)? I can use an array of pipes?
A pipe itself lives only in the kernel. There is no userspace data
structure representing a pipe, so you cannot have an array of pipes.
The file descriptors for the pipe ends, however, are just ints. The pipe() function takes as its argument a pointer to the first element of an array of at least two int, and (on success) it writes the appropriate file descriptors into the array.
From a C perspective, there is nothing special about the array in which the pipe ends are to be returned. In particular, it can be an element of a multi-dimensional array if you so desire. Or it can be a local variable. Or it can be a member of a struct or a union. Or it can be a large enough block of dynamically-allocated space. It's not special.
Something like this should work:
int new_process(char *word){ // return the writing part of a pipe to a newly created child
int p[2];
pipe( p ); // get a new pipe
if (fork()==0) { // create a new child
dup2(p[0],0); // child's in is pipe's entry
close(p[1]); // close writing part
execlp("grep","grep",word,NULL); // exec
}
close(p[0]); // parent don't need the reading part
return p[1]; // send back the writing part to caller
}
int main(int argc, char **argv) {
int f1 = new_process(argv[1]); // get the pipe to first child
int f2 = new_process(argv[1]); // ...second...
char buffer[1024];//buffer
FILE *fd = fopen(argv[1],"r"); // open the file for reading;
while(fgets(buffer,1024,fd) != NULL) { // read aline
if (i % 2 ==0) { //check if the line no is pair
fputs(buffer,f1); // send it to first child
fflush(f1);
} else{
fputs(buffer,f2); // send it to second child
fflush(f2);
}
i++;
}
close(f1);
close(f2);
fclose(fd);
wait(NULL);
wait(NULL);
}
Don't forget to add necessary controls on failures.
I am trying to write this little program, where the parent and the child communicate with each other via pipes, the code here works, unless you 'uncomment' the commented lines, than it comes to some sort of deadlock, and I cannot figure it out why? Any ideas?
int main(int argc, char **argv){
int fd[2];
int fd2[2];
pid_t pid;
pipe(fd);
pipe(fd2);
pid = fork();
if(pid==0){
close(fd[1]);
dup2(fd[0],fileno(stdin));
close(fd2[0]);
FILE *output = fdopen(fd2[1],"w");
char buffer[255];
while(fgets(buffer,255,stdin)!=NULL)
printf("child: %s",buffer);
// fprintf(output,"%s",buffer);
} else {
close(fd[0]);
close(fd2[1]);
FILE *output = fdopen(fd[1],"w");
char buffer[255];
while(fgets(buffer,255,stdin)!=NULL)
fprintf(output,"%s",buffer);
//FILE *input = fdopen(fd2[0],"r");
//while(fgets(buffer,255,input)!=NULL)
// printf("Parent: %s",buffer);
}
return 0;
}
The parent needs to close its side of the pipe to the child so that the child will detect end-of-file and terminate.
while(fgets(buffer,255,stdin)!=NULL)
fprintf(output,"%s",buffer);
fclose(output); // does close(fd[1]);
FILE *input = fdopen(fd2[0],"r");
while(fgets(buffer,255,input)!=NULL)
printf("Parent: %s",buffer);
Make sure everything gets closed. After
dup2(fd[0],fileno(stdin));
you should do:
close(fd[0]);
When you have both input and output pipes between two (single-threaded) processes, you can have some deadlock, so you need to have an event loop using a multiplexing syscall (generally poll(2)...) and you will either read or write, depending on what is possible. Of course you need to buffer! BTW, in that case, you'll better use low level syscalls(2) without using <stdio.h> (and if you still do use stdio, don't forget to fflush(3)....). See also this answer.
(of course I am supposing a POSIX or Linux system)
EDIT:
The solution is
int c1=dup2(pipes[0][1],STDOUT_FILENO);
int c2=dup2(pipes[1][0],STDIN_FILENO);
setvbuf(stdout,NULL,_IONBF,0);
It is SETVBUF to set stdout to be non-buffered. Even though I was printing the newline character if the destination is not an actual screen, I guess, it becomes buffered.
EDIT:
When I put fflush(stdout) after LINE 1 and fflush(fout) after LINE 4 it works as expected. However, it does not work without the fflush(stdout) after LINE 1. The problem is that I would not be able to put fflush in the program which I am planning to run.
I am trying to start another program from my process. I don't have access to its code but I know it uses stdin and stdout for user interaction. I am trying to start that program by creating 2 pipes, fork-ing and redirecting the child's stdin/stdout to the proper pipe ends. The points is that the parent should be able communicate with the child via file descriptors, while its stdin/stdout should be intact. The POPEN syscall only opens unidirectional pipe. The following code almost works.
There are 4 lines marked as LINE 1..4.
LINE 1 is child sending to pipe,
LINE 2 is child receiving from pipe,
LINE 3 is parent sending to pipe,
LINE 4 is parent receiving from pipe,
This is just a toy example to make sure things work. The issue is that is all 4 lines LINE1..4 are uncommented the output I see on the terminal is
PARENT1: -1
FD: 1 0 4 5 0 1
DEBUG1: 0
DEBUG2: 0
While if LINE 1 and LINE 3 are uncommented only I see a continuous stream of data. Same happens if if only LINE 2 and LINE 4 are uncommented. However, I want a full bidirectional communication. Also adding the commented SLEEP does not change the behavior.
What could be the issue here. I wonder why is there no bidirectional POPEN.
int pid;
int pipes[2][2];
pipe(pipes[0]);
pipe(pipes[1]);
pid=fork();
if(pid==0)
{
//usleep(1000000);
close(pipes[0][0]);
close(pipes[1][1]);
int c1=dup2(pipes[0][1],STDOUT_FILENO);
int c2=dup2(pipes[1][0],STDIN_FILENO);
//int c2=dup2(STDIN_FILENO,pipes[1][0]);
fprintf(stderr,"FD: %d %d %d %d %d %d\n",c1,c2,pipes[0][1],pipes[1][0],STDIN_FILENO,STDOUT_FILENO);
//FILE*fout=fdopen(pipes[0][1],"w");
//FILE*fin =fdopen(pipes[1][0],"r");
while(1)
{
static int c1=0;
fprintf(stderr,"DEBUG1: %d\n",c1);
printf("%d\n",c1); // LINE 1
fprintf(stderr,"DEBUG2: %d\n",c1);
scanf("%d",&c1); // LINE 2
fprintf(stderr,"DEBUG3: %d\n",c1);
c1++;
}
//fclose(fout);
//fclose(fin);
return 0;
}
close(pipes[0][1]);
close(pipes[1][0]);
char buffer[100];
FILE*fin=fdopen(pipes[0][0],"r");
FILE*fout=fdopen(pipes[1][1],"w");
while(1)
{
int c1=-1;
printf("PARENT1: %d\n",c1);
fscanf(fin,"%d",&c1); // LINE 3
printf("Recv: %d\n",c1);
fprintf(fout,"%d\n",c1+1); // LINE 4
printf("PARENT3: %d\n",c1+1);
}
fclose(fin);
fclose(fout);
Your code is quite long so I'm not sure that I have understand everything but why you don't use select ?
Do you want to redirect the output of the child in a tird process or use it in your parent process ?
The following exemple is with cat in the child process.
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int p[2];
pipe(p);
pid = fork();
if (pid == 0)
{
dup2(p[1], 1); // redirect the output (STDOUT to the pipe)
close(p[0]);
execlp("cat", "cat", NULL);
exit(EXIT_FAILURE);
}
else
{
close(p[1]);
fd_set rfds;
char buffer[10] = {0};
while (1)
{
FD_ZERO(&rfds);
FD_SET(p[0], &rfds);
select(p[0] + 1, &rfds, NULL, NULL, NULL); //wait for changes on p[0]
if(FD_ISSET(p[0], &rfds))
{
int ret = 0;
while ((ret = read(p[0], buffer, 10)) > 0) //read on the pipe
{
write(1, buffer, ret); //display the result
memset(buffer, 0, 10);
}
}
}
}
}