Writing and reading messages continuously in ipc with pipes in C - c

I have written a program where the parent process creates two child processes.
The parent process writes to either the first or the second child and the child reads the message.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>
#define MSGSIZE 64
char msgbuf[MSGSIZE];
int main(){
int p1[2];
int p2[2];
int nread;
int choice = 0;
pid_t child_a,child_b;
if(pipe(p1) == -1){
printf("error in creating pipe\n");
exit(-1);
}
if(pipe(p2) == -1){
printf("error in creating pipe\n");
exit(-1);
}
child_a = fork();
if (child_a == 0) {
dup2(p1[0], STDIN_FILENO);
read(STDIN_FILENO,msgbuf,MSGSIZE);
printf("%d receives message: %s\n",getpid(),msgbuf);
close(p1[0]);
close(p1[1]);
} else {
child_b = fork();
if (child_b == 0) {
dup2(p2[0], STDIN_FILENO);
read(STDIN_FILENO,msgbuf,MSGSIZE);
printf("%d receives message: %s\n",getpid(),msgbuf);
close(p2[0]);
close(p2[1]);
} else {
/* Parent Code */
// Write something to child A
while(1){
printf("<child_to_receive_msg> <message>\n");
scanf("%d %s",&choice,msgbuf);
switch(choice){
case 1:
usleep(250);
write(p1[1], msgbuf, MSGSIZE);
break;
// Write something to child B
case 2:
usleep(250);
write(p2[1], msgbuf, MSGSIZE);
break;
case -1:
usleep(250);
printf("parent waiting");
wait(NULL);
exit(-1);
break;
}
}
}
}
return 0;
}
My issue is that I want the parent to keep writing to the child process. With the above code, once it writes to child or child 2 it wont write again or at least the child process wont read it again. I don't know if it is possible to do this.
I tried putting the while loop at the beginning of the program but this causes another child process to be created every time.

Here my solution, followed by some explanation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <assert.h>
#define MSGSIZE 64
void read_process(int fh) {
assert(-1 < fh);
char msgbuf[MSGSIZE];
ssize_t retval = 0;
while (-1 < retval) {
retval = read(fh, msgbuf, MSGSIZE);
printf("%d receives message: %s\n", getpid(), msgbuf);
close(fh);
}
}
void write_process(int fh_child_1, int fh_child_2) {
assert(-1 < fh_child_1);
assert(-1 < fh_child_2);
char msgbuf[MSGSIZE];
int choice = -1;
while (1) {
printf("<child_to_receive_msg> <message>\n");
scanf("%d %64s", &choice, msgbuf);
switch (choice) {
case 1:
write(fh_child_1, msgbuf, MSGSIZE);
break;
// Write something to child B
case 2:
write(fh_child_2, msgbuf, MSGSIZE);
break;
case -1:
printf("parent waiting");
wait(NULL);
exit(-1);
break;
}
}
}
int main() {
/* 0 will be for reading, 1 for writing */
int p1[2];
int p2[2];
pid_t pid;
if (pipe(p1) == -1) {
printf("error in creating pipe\n");
exit(-1);
}
/* Don't create 2nd pipe yet, we don't require it here and save us to
* tear it down in child 1 */
pid = fork();
if (pid == 0) {
close(p1[1]); /* not needed here */
p1[1] = -1;
read_process(p1[0]);
exit(EXIT_SUCCESS);
} else {
/* Nobody is going to read from pipe 1 here again */
close(p1[0]);
p1[0] = -1; /* Mark fh as invalid */
if (pipe(p2) == -1) {
printf("error in creating pipe\n");
exit(-1);
}
pid = fork();
if (pid == 0) {
close(p1[1]);
p1[1] = -1;
close(p2[1]);
p2[1] = -1;
/* Ensure we did not forget an fh */
assert(-1 == p1[0]);
assert(-1 == p1[1]);
assert(-1 == p2[1]);
read_process(p2[0]);
exit(EXIT_SUCCESS);
} else {
/* Parent Code */
close(p2[0]);
p2[0] = -1;
assert(-1 == p1[0]);
assert(-1 == p2[0]);
write_process(p1[1], p2[1]);
exit(EXIT_SUCCESS);
}
}
return 0;
}
As noted before, your main problem was that your children processes don't loop but read just once.
There are a few notes, I will first talk about the general ones, and then the more system programming specific ones afterwards.
Your main problem, the missing loops, was hidden below your code being a bit spaghetti.
C provides means for structuring your code, like functions.
Functions are not only there for re-using code, but can also be used to summarize your code: Instead of pasting the code for your child processes directly where you require it, shift the code to a dedicated function and just call the function. This aids greatly in understanding the basic structure of the code:
close(p1[1]); /* not needed here */
p1[1] = -1;
read_process(p1[0]);
exit(EXIT_SUCCESS);
is pretty obvious what is the basic idea, isn't it? And if you want the gory details of the read process, inspect the read_process function.
Be careful about your resources.
Allocate as few as possible, only when you need them. Free them as soon as possible and mark them as freed - see the pipe file handles.
Lastly, if you fork, the process is basically copied. You create a second process, and it is given copies/ clones of all (well, most) of the parent processes resources.
The file handles in your case, are cloned.
E.g. closing p1[0] in your child does not affect p1[0] in your parent, because they are not the same.
That also means, that immediately after forking, you should consider all the resources available and get rid of every resource you wont require immediately, like
pid = fork();
if(0 == pid) {
close(p1[1]); /* not needed here */
p1[1] = -1;
read_process(p1[0]);
exit(EXIT_SUCCESS);
}
Your first child does not require p1[1], thus close it and mark it as closed and invalid.
There is probably much more to say, but these are the points that come to my mind immediately.
Some of it might not seem clumsy, but as you get more and more experienced, and your code bases grow, you will appreciate these things, at least I do more and more every day.
As for the code, there are certainly many bugs still hiding in there, you get the basic idea though I hope ;)

Related

Communication between parent and child results in system lockup

I am trying to create some communication between two programs by forking in the child program within the parent program.
When I execute the child program separately, it works. The purpose of it is that if someone types 1, 2, or 3 followed by enter, that program prints that number as a word. But if one presses 0 and enter, the program exits.
Now I am trying to make the parent program execute the child program in a way where all it does is exit the program while showing the progress of action.
When I execute my program, I see:
Child to start
Parent running OK
Which suggest the child program is running, otherwise I would see:
Exec failed
So instead of me seeing any actual useful output, the system decides to gradually slow down to the point where at first the mouse cursor doesn't move smoothly when I move the mouse, then It got to the point where it wouldn't respond to the keyboard, so I literally had to hold the power button to reset my computer.
How do I fix this so that it can work with any program (that I use as a child) that can exit when I press 0 and enter from within it?
This is my code for the parent:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
//Setup child read and write file handle named parr and parw respectively
//and parent read and write file handle named to parr and parw respectively
#define kidr wrp[0]
#define kidw rdp[1]
#define parw wrp[1]
#define parr rdp[0]
int main(){
int cmd=0;
//setup and start pipes
int wrp[2],rdp[2];
if (pipe(wrp) == -1 || pipe(rdp) == -1){printf("ERROR: cant run pipes.\n");return -1;}
//Start fork
pid_t f=fork();
if (f > 0){
int wstat; //wait state data
char buff[100]; //our data buffer
close(kidr); //we are parent so close child handles
close(kidw);
struct timeval tv;
fd_set readfds;
tv.tv_sec = 1;
tv.tv_usec = 0;
printf("Parent running OK\n");
while(1){
//process other async events here
pid_t wpid=waitpid(-1,&wstat,WNOHANG);
if (wpid==-1){printf("Wait PID error\n");break;}
if (wpid > 0){printf("Children closed OK\n");break;}
//Process data only when child data is readable via pipe
FD_ZERO(&readfds);FD_SET(parr, &readfds);
select(parr+1, &readfds, NULL, NULL, &tv);
if(FD_ISSET(parr, &readfds)){
memset(buff,0,99);
int rd=read(parr, buff, 50);
//doesnt seem to reach this point...
if (rd > 0){
printf("Got: %s\n", buff);
}else{
if (cmd==0){
printf("sending data...\n");
char*dat="0\n"; //parent sends 0 and the enter button.
cmd++; //so this doesn't get called again
write(parw,dat,strlen(dat));
}
}
}
}
//close everything and exit
close(parr);
close(parw);
return 0;
}
if (f==0){
printf("Child to start\n");
//Child mode.
//Close parents
close(parr);close(parw);
//make stdio as child handles
dup2(kidr,STDIN_FILENO);dup2(kidw,STDOUT_FILENO);
//close old child handles
close(kidw);close(kidr);
execlp("/path/to/forkt","forkt",NULL);
//We shouldn't get here unless 'ls' command isnt found
printf("Exec failed\n");
_exit(-1);
}
if (f==-1){
//If fork() doesnt work...
printf("Fork error\n");
}
return 0;
}
This is my code for the child and I compiled it so its named forkt.
#include <stdio.h>
#include <stdlib.h>
int main(){
printf("The child has started\n\n");
char c[100];
while (1){
printf("Enter number or 0 to exit: \n>");
scanf("%s",c);
if (c[0]=='1'){printf("one\n");}
if (c[0]=='2'){printf("two\n");}
if (c[0]=='3'){printf("three\n");}
if (c[0]=='0'){return 0;}
}
}
Update
I took a suggestion of running my parent code through the gdb debugger.
I compiled my code using the gcc -g switch then executed it with gdb a.out
Then in gdb, I set a break point to first line of code then used the "run" command then i kept using the "step" command until I found the crashing point which is here:
pid_t f=fork();
if (f > 0){ // <- right here
This suggests that somehow the child is creating the lockup(?) even though the child runs fine if it is run by itself without a parent?
AFAICS, the parent won't write anything to the child until the child sends something back, but the child won't send anything until it gets something from the parent. That's a deadlock. There's also a problem with buffering. The pipes are not 'interactive devices' so the output streams are not flushed until the buffer is full, the stream is closed, or you call fflush().
Here's some alternative but very similar code to yours:
forkt.c
#include <stdio.h>
int main(void)
{
printf("The child has started\n\n");
fflush(stdout);
char c[100];
while (1)
{
printf("Enter number or 0 to exit:\n>");
fflush(stdout);
if (scanf("%s", c) != 1)
return 0;
fprintf(stderr, "Child received: [%s]\n", c);
if (c[0] == '1')
{
printf("one\n");
}
if (c[0] == '2')
{
printf("two\n");
}
if (c[0] == '3')
{
printf("three\n");
}
if (c[0] == '0')
{
return 0;
}
fflush(stdout);
}
}
This is mostly noticeable for a collection of calls fflush(stdout).
parent.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define kidr wrp[0]
#define kidw rdp[1]
#define parr rdp[0]
#define parw wrp[1]
int main(void)
{
int cmd = 0;
int wrp[2], rdp[2];
fprintf(stderr, "Parent process: PID %d\n", getpid());
if (pipe(wrp) == -1 || pipe(rdp) == -1)
{
fprintf(stderr, "ERROR: cant run pipes.\n");
exit(1);
}
pid_t f = fork();
if (f == -1)
{
fprintf(stderr, "Fork error\n");
exit(1);
}
if (f > 0)
{
int wstat;
char buff[100];
close(kidr);
close(kidw);
fprintf(stderr, "Parent running OK - child %d\n", f);
while (1)
{
pid_t wpid = waitpid(-1, &wstat, WNOHANG);
if (wpid == -1)
{
fprintf(stderr, "Wait PID error\n");
break;
}
if (wpid > 0)
{
fprintf(stderr, "Child %d exited\n", wpid);
break;
}
for (cmd = 3; cmd >= 0; cmd--)
{
char buffer[30];
int nb = snprintf(buffer, sizeof(buffer), "%d\n", cmd);
int wr = write(parw, buffer, strlen(buffer));
if (wr != nb)
{
fprintf(stderr, "Parent failed to write: %d\n", wr);
exit(1);
}
fprintf(stderr, "Parent sent: %s", buffer);
memset(buff, 0, 99);
int rd = read(parr, buff, 50);
if (rd > 0)
{
fprintf(stderr, "Got: [[%.*s]]\n", rd, buff);
}
else
{
fprintf(stderr, "Parent read failed\n");
exit(1);
}
}
}
close(parr);
close(parw);
return 0;
}
if (f == 0)
{
fprintf(stderr, "Child %d to start\n", getpid());
close(parr);
close(parw);
dup2(kidr, STDIN_FILENO);
dup2(kidw, STDOUT_FILENO);
close(kidw);
close(kidr);
execlp("forkt", "forkt", NULL);
fprintf(stderr, "Exec failed\n");
_exit(-1);
}
return 0;
}
The surgery here is more extensive.
When I ran the code, one time I got the output:
Parent process: PID 94693
Parent running OK - child 94694
Parent sent: 3
Child 94694 to start
Got: [[The child has started
]]
Parent sent: 2
Child received: [3]
Got: [[Enter number or 0 to exit:
>]]
Parent sent: 1
Child received: [2]
Got: [[three
Enter number or 0 to exit:
>]]
Parent sent: 0
Child received: [1]
Got: [[two
Enter number or 0 to exit:
>]]
Child received: [0]
Parent sent: 3
Got: [[one
Enter number or 0 to exit:
>]]
Parent sent: 2
Parent read failed
Note that the prompts from the child are mixed up with the output.

How to distinguish one child process from other child processes

I have an assignment for class and I am confused on this part of the requirements. So we need to make a multi process word counter with n number of processes and n will be an input argument for the program. Each process needs to do their own mini word count of a select portion of the inputted file. So essentially the inputted file will be divided into 1/n parts and split between n processes.
I understand how to fork the processes through a for loop and how to use pipes to send the mini word count from the children processes to the parent process, but I unsure of how to tell a certain process to do a select part of the input file.
Would you use their PID values to check which process they are then assign them their task?
This is my code so far.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MSGLEN 64
#define MESSES 3
int main(){
int fd[2];
pid_t pid;
int result;
//Creating a pipe
result = pipe (fd);
if (result < 0) {
//failure in creating a pipe
perror("pipe error\n");
exit (1);
}
//Creating a child process
for(int i = 0; i < MESSES; i++){
if ((pid = fork()) < 0) {
//failure in creating a child
perror ("fork error\n");
exit(2);
}
if(pid == 0)
break;
}
if (pid == 0) {
// ACTUALLY CHILD PROCESS
char message[MSGLEN];
//Clearing the message
memset (message, 0, sizeof(message));
printf ("Enter a message: ");
//scanf ("%s",message);
fgets (message, 1024, stdin);
close(fd[0]);
//Writing message to the pipe
write(fd[1], message, strlen(message));
close(fd[1]);
close(fd[0]);
exit (0);
}
else {
//Parent Process
char message[MSGLEN];
char *ptr;
long wc;
close(fd[1]);
while (1) {
//Clearing the message buffer
memset (message, 0, sizeof(message));
//Reading message from the pipe
if(read(fd[0], message, sizeof(message)) == 0)
exit(0);
printf("Message entered %s\n",message);
/*
Message entered needs to be in the format of number first space then string for it to work
*/
wc = 0;
wc = strtol(message, &ptr, 10);
printf("The number(unsigned long integer) is %ld\n", wc);
printf("String part is %s", ptr);
}
close(fd[0]);
wait(NULL);
// exit(0);
}
return 0;
}
The key thing to remember when using fork is that the parent and child share the same memory and a copy of everything the parent has is passed to the child. At which point the child has now forked the parents data.
In the code below we're counting how many processes we've created. You could if you wanted use this as an argument in the child ie the nth child gets value n.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define PROCESS_COUNT 50
int main(void) {
pid_t pid;
size_t pid_count = 0;
//pid_t pid_array[PROCESS_COUNT];
for(int i = 0; i < PROCESS_COUNT; i++) {
if ((pid = fork()) < 0) {
perror ("fork error\n");
exit(2);
}
if (pid == 0) {//child
size_t n = 0;
size_t p = getpid();
while(n++ < 2) {
//Next line is illustration purposes only ie I'm taking liberties by
//printing a pid_t value
printf("child %zu has pid_count == %zu\n", p, pid_count);
sleep(1);
}
exit (0);
}
else {
//Count how many process we've created.
pid_count++;
int status;
waitpid( -1, &status, WNOHANG);
}
}
wait(NULL);
return 0;
}
If you want to get really fancy you can use IPC using pipes or shared memory. There are lots of ways to get data from one process to another, sometimes something as simple as temporary files is more than sufficient. For your problem I'd use mmap but it does not need to be that complicated

write operation on pipe is always failing

I'm a bit new to pipes and concurrency, and have been frustrated with this problem for hours. I am struggling to understand why this write operation is constantly failing on my pipe. I am trying to have the child process write data through a pipe that will be received by the parent process. My current code is this:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 4096
int main() {
pid_t status;
int fd[2]; //The array of file descriptors
if (pipe(fd) == -1) {
printf("Error piping");
}
status = fork(); //Begin the fork process
switch (status) {
case -1:
perror("Error forking");
break;
case 0:
//Child process
close(fd[0]); //Only send data
char some_string[15] = "hi there";
if (write(fd[1], some_string, MAXSIZE) == -1) {
printf("Error writing to the pipe");
}
close(fd[1]); //Close write end
exit(1);
default:
close(fd[1]); //Only receive data
char readed[500] = "";
while(read(fd[0], readed, MAXSIZE) != 0) {
printf("read this %s\n", readed);
}
printf("Done reading");
close(fd[0]);
break;
}
return 1;
}
However, I constantly get the message "Error writing to pipe", meaning that the write operation has failed in the child process. Another interesting thing is that if I change some_string to a string literal instead, this code works fine with the exception that it never terminates and instead, the read operation in the parent process reads from STDIN! I don't understand why this could be happening, is it possible that we have a zombie child when parent executes so the pipe is "dead"? Or perhaps that the parent process terminates and we have an orphaned child? How can I avoid this and how does this explain the weird behaviour from the string literal instead? Any insights?
You told write() to read the data from out-of-range of the array and allowed read() to write the data read to out-of-range of the array. That is very bad.
Write only valid data and limit the length to read not to cause out-of-range access.
Try this:
#include <unistd.h>
#include <sys/types.h> /* add this to use pid_t */
#include <sys/wait.h> /* add this to use wait() */
#include <stdio.h>
#include <stdlib.h>
/* remove unused MAXSIZE */
int main() {
pid_t status;
int fd[2]; //The array of file descriptors
int st; /* variable for receiving the status */
if (pipe(fd) == -1) {
printf("Error piping");
return 1; /* return 1 when the execution failed */
}
status = fork(); //Begin the fork process
switch (status) {
case -1:
perror("Error forking");
return 1; /* return 1 when the execution failed */
break;
case 0:
//Child process
close(fd[0]); //Only send data
char some_string[15] = "hi there";
if (write(fd[1], some_string, sizeof(some_string)) == -1) {
printf("Error writing to the pipe");
}
close(fd[1]); //Close write end
exit(0); /* return 0 if the execution finished successfully */
default:
close(fd[1]); //Only receive data
char readed[500] = "";
while(read(fd[0], readed, sizeof(readed) - 1) != 0) { /* -1 for reserving space for terminating null-character */
printf("read this %s\n", readed);
}
printf("Done reading");
close(fd[0]);
wait(&st); /* wait for the child process to exit and release the data of the process */
break;
}
return 0; /* return 0 if the execution finished successfully */
}

New to IPC, can't get my pipe to work

Sorry for the length of this post... I've encountered about a zillion problems in this. Up front I'll say I'm a student and my professor is a worthless resource. So, all I want to to do is have producer fork, then the parent producer will count some stuff in a file and send two ints to consumer, which was launched by the child process. I've tested everything, the fork and the file stuff works and I have printf statements all over the place so I know what is being done and where the code is at.
When I added the
if (pipe(pipefd) == -1) {
perror("pipe");
}
it caused my parent to just terminate. It reaches "parent pipe open" but then it dies. I checked with $ ps to see if it was just hung, but it's not there; it just dies. If I take that snippet out, it runs to the end but I presume if that code isn't there, then it's not actually aware that pipefd is a pipe... right?
I did search on this site and found another example of this and followed what he did as well as the answer and mine just refuses to work. I'm pretty sure it's a trivially easy thing to fix but I've run out of ideas of what to try :(
I don't really want to post all my code because it'll be a huge wall of text but I don't want to accidentally cut something out that turns out to be important either.
producer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#include <string.h> /* strlen */
#include <sys/wait.h> /* wait */
#define SLEEP_TIME 8
int main (int argc, char *argv[]){
//PID
pid_t local_pid;
local_pid = fork();
//Logic to determine if the process running is the parent or the child
if (local_pid == -1) {
/* Error:
* When fork() returns -1, an error happened
* (for example, number of processes reached the limit).
*/
fprintf(stderr, "can't fork, error %d\n", errno);
exit(EXIT_FAILURE);
} else if (local_pid == 0) {
//Child specific code
int child;
char *temp[] = {NULL};
printf("Child PID found\n");
child = execv("./consumer", temp);
_exit(0);
} else {
//Parent specific code
printf("Parent running\n");
//open file
FILE * randStrings;
randStrings = fopen("randStrings.txt", "r");
int file_length;
int num_of_e = 0;
int c; //using this as a char
//until eof
while (feof(randStrings) == 0) {
c = fgetc(randStrings);
//calculate length of file
file_length++;
//count e chars
if (c == 'e') {
num_of_e++;
}
}
//close file
fclose(randStrings);
//send bundle to child
int a[2];
a[0] = num_of_e;
a[1] = file_length;
printf("num of e = %i\n", a[0]);
printf("len = %i\n", a[1]);
//set up parent pipe
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("x\n");
}
printf("parent pipe open\n");
close(pipefd[0]); //close the read end
write(pipefd[1], &a[0], sizeof(int));
write(pipefd[1], &a[1], sizeof(int));
close(pipefd[1]);
printf("parent pipe closed\n");
//wait for child to finish running
wait(NULL);
printf("parent out\n");
//terminate
}
}
and consumer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#define SLEEP_TIME 5
int main (int argc, char *argv[]){
sleep(SLEEP_TIME);
printf("Child program launched\n");
//receive bundle
int pipefd[2];
int buf[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("child x\n");
}
close(pipefd[1]); //child closes write end
buf[0] = 0;
buf[1] = 0;
/*int i = 0; // i dont like this
while (read(pipefd[0], &buf[i], sizeof(int)) > 0) {
i++;
}*/
printf("child reading pipe\n");
read(pipefd[0], &buf[0], sizeof(int));
read(pipefd[0], &buf[1], sizeof(int));
close(pipefd[0]);
//buf should have the stuff in it
int num_of_e = buf[0];
int file_length = buf[1];
printf("child num of e = %i\n", num_of_e);
printf("child len = %i\n", file_length);
//open file
FILE * resultStrings;
resultStrings = fopen("resultStrings.txt", "w");
for (int i = 0; i < num_of_e; i++) {
//write num_of_e e chars
fputc('e', resultStrings);
}
//or if no e chars, write - chars
if (num_of_e == 0) {
for (int i = 0; i < file_length; i++) {
//write file_length '-' chars
fputc('-', resultStrings);
}
}
//close file
fclose(resultStrings);
printf("child out\n");
}
if you're still here after all that, you deserve a thank you just due to the length of this.
You're doing it wrong. The whole mechanism works because a child process inherits the parent's open file descriptors.
It should go like this:
Open the pipe with pipe(pipefd)
fork()
Parent (producer):
closes the read side (pipefd[0])
writes to the write side (pipefd[1])
Child (consumer):
closes the write side (pipefd[1])
reads from the read side (pipefd[0]) or calls exec
You are opening distinct pipes in both the parent and child process (after you've forked.) It needs to happen before you fork.
Now since you're execing, the new process needs to be aware of read-only pipe. There are a couple ways you could do this:
Pass it the file descriptor number (pipefd[0]) on the command line
dup2(1, fd) it to be the stdin of the newly exec'd process

headache with named pipes and forks

I need to write program that have construction like this:
Parent makes fifo, then fork()
child 1 reads message from stdin and writes it to named pipe (FIFO)
then in parent process I need to create pipe (unnamed) and another fork()
child number 2 reades from FIFO, counts length of message and send number to parent via pipe(unnamed).
I created a simple program with one fork where child can communicate with parent:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO "/tmp/my_fifo"
int main()
{
pid_t fork_result;
int pipe_fd;
int res;
char writer[3];
char reader[3];
res = mkfifo(FIFO,0777);
if (res == 0)
{
printf("FIFO created!\n");
fork_result = fork();
if (fork_result == -1)
{
fprintf(stderr, "fork error");
exit(EXIT_FAILURE);
}
if (fork_result == 0)
{
printf("CHILD 1\n");
pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
scanf("%s", writer);
res = write(pipe_fd,writer,3);
if (res == -1)
{
fprintf(stderr,"error writing fifo\n");
exit(EXIT_FAILURE);
}
(void)close(pipe_fd);
exit(EXIT_SUCCESS);
}
else
{
printf("PARENT\n");
pipe_fd = open(FIFO, O_RDONLY);
res = read(pipe_fd, reader, 3);
printf("reader: 0: %c\n",reader[0]);
printf("reader: 1: %c\n",reader[1]);
printf("reader: 2: %c\n",reader[2]);
(void)close(res);
}
}
else
{
printf("deleting fifo... run program again!\n");
unlink(FIFO);
}
exit(EXIT_SUCCESS);
}
and it is working very well. So I created code that have architecture described above:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO "/tmp/my_fifo"
int main()
{
pid_t fork_result;
pid_t fork_result2;
int pipe_fd;
int res;
char writer[3];
char reader[3];
res = mkfifo(FIFO,0777);
if (res == 0)
{
printf("FIFO created!\n");
fork_result = fork();
if (fork_result == -1)
{
fprintf(stderr, "fork error");
exit(EXIT_FAILURE);
}
if (fork_result == 0)
{
printf("CHILD 1\n");
pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
scanf("%s", writer);
res = write(pipe_fd,writer,3);
if (res == -1)
{
fprintf(stderr,"error writing to fifo\n");
exit(EXIT_FAILURE);
}
(void)close(pipe_fd);
exit(EXIT_SUCCESS);
}
else
{
printf("PARENt 1\n");
//don't forget pipe!
fork_result = fork();
pipe_fd = open(FIFO, O_RDONLY);
if (fork_result == 0)
{
printf("CHILD 2\n");
res = read(pipe_fd, reader, 3);
printf("Odczytano: 0: %c\n",reader[0]);
printf("Odczytano: 1: %c\n",reader[1]);
printf("Odczytano: 2: %c\n",reader[2]);
(void)close(res);
}
}
}
else
{
printf("deleting fifo\n");
unlink(FIFO);
}
exit(EXIT_SUCCESS);
}
Running sequence is like this:
PARENT 1
CHILD 1
CHILD 2
so in Parent 1 I'm opening FIFO to read, in child 1 I'm writing to FIFO and child 2 should read it. I mean in code because when I run it I can't even write anything to FIFO. In blocks in scanf("%s", writer); which worked in first program.
Am I using open() correctly? Do I need to use getpid() somewhere? Why it's blocking when I try to write to fifo.
The problem is that CHILD1 is opening the fifo with O_NONBLOCK, which will fail (with EWOULDBLOCK or EAGAIN) if no other process has the fifo open for reading. Now in the first program, the parent continues running after the fork and opens the fifo for reading before the child gets going and opens the write end, so it works. But in the second case, the parent does an extra fork first, which slows it down just enough that CHILD1 gets to its open command before PARENT or CHILD2 has opened the fifo for reading, so the CHILD1 open fails.
Get rid of the O_NONBLOCK and it works just fine (though you do open the fifo for reading in both PARENT and CHILD2, which is probably not what you want).
You have another issue if you want to read from the keyboard. If you run this from the shell, PARENT will exit immediately (more or less), so the shell will go back to reading commands from the keyboard, which means that CHILD1 and the shell will be fighting over the input. If on the other hand, you do what you originally describe and have PARENT wait reading from a pipe from CHILD2, it should do what you want.
Isn't it because you use twice the same variable fork_result? As you created another variable fork_result2, which you don't use, it is probably unintended.
I don't know if this will solve your problem, but at least using fork_result2 at the second fork would make it easier to understand...

Resources