1
votes

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.

2
Hi, you don't test whether the second fork is a success. If it fails, the printf doesn't happen and the scanf will hang... Maybe you are in this case? Also, the open(FIFO, 0_RDONLY) takes place both on the PARENT1 and CHILD2, and it seems unnecessary on PARENT1. - GHL
I added test for second fork. About open(FIFO, O_RDONLY) in PARENT1: I need it because when I don't there is error writing to fifo in CHILD1 (no one opened fifo for reading)... EDIT: I removed open from PARENT1 and in CHILD1 I changed pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK); to pipe_fd = open(FIFO, O_WRONLY); and now it doesnt read from keyboard but write to screen empty reader array... - lvp

2 Answers

1
votes

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.

0
votes

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...