1

I am using these two programs of this answer. This answer uses named-pipes and not pipes, am I correct?

I have written main.c, which is actually the code of my actual project, minimized to this specific question (that's why I have a for loop for example).

#include <unistd.h>
#include <sys/wait.h>
#include <stddef.h>
#include <limits.h>
#include <stdio.h>


int main(void) {
  pid_t pid;

  int i;
  for(i = 0; i < 2; ++i) {
    pid = fork();
    if (pid == -1) {
      // error, failed to fork()
      perror("failed to fork()");
      return 1;
     } else if (pid == 0) {
      // child code
      if(i < 1) {
        // the writer.c
        char* arg_list[] = { "w", NULL };
        execv( "w", arg_list );
        printf("exec FAILED\n");
      } else {
        // the reader.c
        char* arg_list[] = { "r", NULL };
        execv( "r", arg_list );
        printf("exec FAILED\n");
      }
     }
    }

    // parent code
  int status;
  // wait for all children to terminate
  while ((pid = wait(&status)) > 0) {
    if (status == 1) {
      printf("The child process terminated with an error!\n");
      return -1; 
    }
  }
  printf("All children are done\n");

  return 0;
}

The problem is that sometimes, the reader receives garbage (or most likely nothing) and it hangs up.

Sample output:

Received: Hi
All children are done
samaras@samaras-A15:~/test$ ./m
Received: Hi
All children are done
samaras@samaras-A15:~/test$ ./m
Received:        <----------------- This is garbage, that is not reproducible 
^C

So, what am I missing?

No need to read below that point.


My guesses are (not checked, so if I am correct, I still need clarification):

The reader runs before writer, that's why it has garbage, but then why it hangs?

or

I need to write a wrapper function read_all() (and one for the write case as well?) that collects all the data that the pipe spits, but then why if I replace "Hi" with "H", I have the same behaviour?


EDIT:

In case my first guess is the case, I put a loop for reading, but it will execute forever in the case that reader starts first.

In the case that I see garbage, after running with strace -f I got this:

...
[pid  3326] read(-1, 0xbfddd80c, 1024)  = -1 EBADF (Bad file descriptor)^C
Process 3324 resumed
Process 3325 detached
Process 3326 detached
Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • You *should* use a read loop around `read()` to ensure that all bytes are read. Likewise, you *should* use a write loop around `write()` to ensure that all bytes are written. I don't think your failure to do those things is responsible for the behavior you observe, however. Running your program under `strace -f` might provide more insight into what's happening. – John Bollinger Nov 14 '14 at 21:05
  • Note, too, that you could `fopen()` your FIFO and use stream I/O if you don't need or want to manage low-level `read()` and `write()` calls. – John Bollinger Nov 14 '14 at 21:08
  • Well on that case how I am supposed to know how many characters I am expected to read? I am not clear on that for a start. I mean I don't really see how this loop is going to look like @JohnBollinger. – gsamaras Nov 14 '14 at 21:08
  • Give full path to your "w" and "r" binaries in the first of `execv()` calls, such as "./w" and "./r". But this is not directly related to what say. – P.P Nov 14 '14 at 21:10
  • 1
    How indeed are you supposed to know how many characters to read? With or without a read loop, how do you know that you have read all the data of one message? The answer is that you need to define some kind of protocol between reader and writer. It doesn't necessarily have to be complicated. For example, maybe each message is terminated by a newline. – John Bollinger Nov 14 '14 at 21:10
  • 1
    If your messages are going to basically be text based, like your example, then you will probably find stream I/O a lot easier to use than the low-level `read()` and `write()` syscalls. – John Bollinger Nov 14 '14 at 21:14
  • @BlueMoon but they are executed. John I think I have to use `read()` and `write()` for the bigger project. So you mean that the read loop should wait to see a newline? It would be nice if you made an example like the one I have in my question which would use these loops you say and actually work. And yes, what I want to transmit is just words. – gsamaras Nov 14 '14 at 21:23
  • I suspect your reader completed its operation before writer did any writes. Check the return value of `open()` in your reader, which should explain (and writer too). – P.P Nov 14 '14 at 21:35
  • My first guess then! It's -1 in the case that nothing is printed. @BlueMoon – gsamaras Nov 14 '14 at 21:39
  • I still don't know what do, because the loops I am trying aren't working too. – gsamaras Nov 14 '14 at 22:02
  • Your loops have nothing to do with it. But when your reader opens (`open()`) the pipe for reading *before* writer creates the pipe, then the fd your readers waits on is invalid (-1). So even when writer writes something later on, reader just waits on -1 (fd) and is never going to read anything. It's UB. Trivially, you could solve it with: `while( (fd = open(myfifo, O_RDONLY)) == -1);` in reader so that it waits until pipe is available. – P.P Nov 14 '14 at 22:23
  • That's it! Are you going to answer the question @BlueMoon or should I delete it? Thanks. – gsamaras Nov 14 '14 at 22:25
  • @G.Samaras If that solves it and you're ok, then I can post it. But I am not sure if that's the best solution ;-) – P.P Nov 14 '14 at 22:30
  • That's the best I have so far @BlueMoon :/ I mean if you have something else to suggest I am willing to hear. :) – gsamaras Nov 14 '14 at 22:30
  • 1
    Let's see if anyone comes up with a different idea. Interesting though:) – P.P Nov 14 '14 at 22:40

1 Answers1

1

Your loops (or lack of) have nothing to do with it. When your reader opens (open()) the pipe for reading before writer creates the pipe, then the file descriptor your readers waits on is invalid (-1). So even when writer writes something later on, reader just waits on an invalid fd (-1) and is never going to read anything. Trivially, you could solve it with:

while( (fd = open(myfifo, O_RDONLY)) == -1); 

in reader so that it waits until pipe is available. I am actually wondering if there can be a better approach than this. One other way I can think of is a loop over access(), but it's not massively different to this...

P.P
  • 117,907
  • 20
  • 175
  • 238
  • I also have a question regarding this line `char * myfifo = "/tmp/myfifo";`. Let's say I will have two writers and two readers and I want the one reader to be able to read from both writers and similarly the other's reader behaviour. Should I change this line? I mean it means that this `tmp/myfifo` is treated as a shared address space? I am not accepting your answer yet, to see if someone comes in with something more efficient. :) – gsamaras Nov 14 '14 at 22:43
  • A named pipe is conceptually similar to a normal file where one process and writes and another reads. Unlike a normal file, data written to a pipe can be read only once. So multiple readers can't "share" a pipe. Multiple writers can write to same pipe but data may be interleaved or possibly corrupted. Pipes are not designed for multiple processes' reads/writes. You are better off using other [IPC mechanisms](http://en.wikipedia.org/wiki/Inter-process_communication#Approaches). – P.P Nov 14 '14 at 23:10
  • I am instructed to use named pipes. So I should create more pipes. How many is something that I have to still think of. – gsamaras Nov 14 '14 at 23:22