0

In my understanding, according to the https://linux.die.net/man/3/mkfifo,

I got an implication that I must have reader and writer file, in order to

utilize the pipe file. The source below is the writer file,

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>

int main(){

    int fd;
    char *myfifo = "./myfifo";

    mkfifo(myfifo, 0777);
    fd = open(myfifo, O_WRONLY);    


    int PID = fork();   

    if(PID == 0){

        execl("./reader.o", "reader", (char*)NULL);

    }

    write(fd, "Rock and roll baby\0", sizeof("Rock and roll baby"));
    close(fd);
    unlink(myfifo);

    return 0;

}

and the source being provided below is for the reader file.

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX_BUF 1024

int main(){

    int fd;

    char* myfifo = "./myfifo";
    char buf[MAX_BUF];

    fd = open(myfifo, O_RDONLY);
    read(fd, buf, MAX_BUF);
    write(STDOUT_FILENO, buf, MAX_BUF);

    close(fd);
    exit(EXIT_SUCCESS);

    return 0;

}

When run the executable for the writer file, the command prompt goes into

halt, after printing a newline. My assumption for this problem is because the

open() in the writer file is not being able to detect the pipe file,

is that the case?

Thank you.

asd
  • 29
  • 9
  • Unless you play tricks with arguments to `open()`, neither a reader nor a writer will return from the `open()` call until there is another process with the 'other end' of the FIFO open. That is, the reader waits until there is a writer, and the writer waits until there is a reader. You therefore need to fork and exec the child process before attempting to open the FIFO. This also solves another problem — you should have closed the write end of the FIFO before executing the child. The child would hang because it would have a write file descriptor and a read file descriptor for the FIFO; no EOF. – Jonathan Leffler Jun 20 '17 at 04:13

2 Answers2

1

I suggest that you should create the FIFO before the fork, but only open the FIFO after the fork. This avoids an assortment of problems. For the most part, I've used write() to report errors to standard error; it isn't as convenient as using fprintf(stderr, …) though.

Note that the writer writes a null byte at the end of the message. The reader gets the null byte, but overwrites it with a newline before writing the resulting character array (it is no longer a string; strings have a terminal null byte at the end) to standard output. If the code used <stdio.h> to write the data (e.g. printf("%s\n", buf)), it wouldn't need to replace the null byte with a newline.

writer.c

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

#ifndef READER
#define READER "./reader"
#endif

int main(void)
{
    char *myfifo = "./myfifo";

    if (mkfifo(myfifo, 0777) != 0)
    {
        write(STDERR_FILENO, "Failed to create FIFO\n",
                      sizeof("Failed to create FIFO\n") - 1);
    }

    int PID = fork();

    if (PID == 0)
    {
        execl(READER, "reader", (char *)NULL);
        write(STDERR_FILENO, "Failed to execute reader\n",
                      sizeof("Failed to execute reader\n") - 1);
        exit(EXIT_FAILURE);
    }
    if (PID < 0)
    {
        write(STDERR_FILENO, "Failed to fork\n",
                      sizeof("Failed to fork\n") - 1);
        exit(EXIT_FAILURE);
    }

    int fd = open(myfifo, O_WRONLY);
    if (fd < 0)
    {
        write(STDERR_FILENO, "Failed to open FIFO for writing\n",
                      sizeof("Failed to open FIFO for writing\n") - 1);
        unlink(myfifo);
        exit(EXIT_FAILURE);
    }

    write(fd, "Rock and roll baby", sizeof("Rock and roll baby"));
    close(fd);
    unlink(myfifo);
    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        printf("Child %d exited with status 0x%.4X\n", corpse, status);

    return 0;
}

reader.c

#include <fcntl.h>
#include <unistd.h>

#define MAX_BUF 1024

int main(void)
{
    char* myfifo = "./myfifo";
    int fd = open(myfifo, O_RDONLY);
    if (fd < 0)
        write(STDERR_FILENO, "Failed to open FIFO for reading\n",
                      sizeof("Failed to open FIFO for reading\n")-1);
    else
    {
        char buf[MAX_BUF];
        int nbytes = read(fd, buf, MAX_BUF);
        if (nbytes > 0)
        {
            buf[nbytes-1] = '\n';
            write(STDOUT_FILENO, buf, nbytes);
        }
        close(fd);
    }
    return 0;
}

Example output

Rock and roll baby
Child 43734 exited with status 0x0000
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>

int main(){

    int fd;
    char *myfifo = "./myfifo";

    int PID = fork();   

    if(PID == 0){

        execl("./reader.o", "reader", (char*)NULL);

    }

    mkfifo(myfifo, 0777);
    fd = open(myfifo, O_WRONLY);    

    write(fd, "Rock and roll baby\0", sizeof("Rock and roll baby"));
    close(fd);
    unlink(myfifo);

    return 0;

}

After having the body of the code, where the execl is, moved above the

mkfifo(),

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX_BUF 1024

int main(){

    sleep(3);

    int fd;

    char* myfifo = "./myfifo";
    char buf[MAX_BUF];

    fd = open(myfifo, O_RDONLY);
    read(fd, buf, MAX_BUF);
    write(STDOUT_FILENO, buf, MAX_BUF);

    close(fd);

    exit(EXIT_SUCCESS);

    return 0;

}

and having the reader have sleep() for 3 seconds, the programs started to

work; however, does anyone know if the two programs can open() the pipe file

exactly at the same time?

Thank you.

asd
  • 29
  • 9
  • I think maybe you should explain why you have the extra null byte in the string written by the writer — it looks unnecessary to me (and you could use the same string twice, rather than using two slightly different strings). – Jonathan Leffler Jun 20 '17 at 04:09
  • To conserve time, I have decided not to stdout the exact size of the string in buf. – asd Jun 20 '17 at 18:21
  • Well, instead of `write(fd, "Rock and roll baby\0", sizeof("Rock and roll baby"));`, you could use `write(fd, "Rock and roll baby", sizeof("Rock and roll baby"));`. This includes the null byte in the data sent on the FIFO. That has its merits; you either need to identify the length up front or use an end marker. – Jonathan Leffler Jun 20 '17 at 18:57
  • Forgive me on the misunderstanding of the first comment. Thank you for the insight. – asd Jun 20 '17 at 23:52
  • NP — I see you mostly copied the code from the question on that point anyway. I think you should be creating the FIFO before forking; otherwise, there's a risk the reader will try to open the still non-existent FIFO and fail, wreaking havoc on the best laid plans of mice and coders. The open calls should both be after the fork, though; in the case of the reader, that's unavoidable, but not for the writer. – Jonathan Leffler Jun 20 '17 at 23:54