0

I need to create program with 3 processes:

  1. The first process should repeatedly read /dev/urandom and send 15 chars each cycle to the second process via a pipe.
  2. The second process should convert received data to hex and send the result to the third process via a fifo.
  3. The third process should print the received data.

This is what I wrote so far. Communication using the pipe is working fine, however there is some problem with the fifo - when I change n to a larger number such as 100000 or 1000000, the program doesn't start. When it's smaller, say 500 or 1000, the program works. What could be the reason behind that?

This is how I run it:

cat /dev/urandom | ./a.out

And here is the code:

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

#define FIFO "my_fifo"

int main(void) {
    int pdesk[2];
    char buf[15];
    char buffer[15];
    char hex[30];
    char f[30];
    int len;
    int n;

    n = 100;

    umask(0);
    mkfifo(FIFO, 0666);

    pipe(pdesk);

    if (fork() == 0) {
        for (int i = 0; i < n; i++) {
            read(STDIN_FILENO, buffer, 15);
            write(pdesk[1], buffer, 15);
        }
        close(pdesk[1]);
    } else {
        sleep(1);
        int fp;

        for(int i = 0; i < n; i++) { 
            read(pdesk[0], buf, 15);

            for(int a = 0, b = 0; b < 30; ++a, b+= 2) 
                sprintf(hex + b, "%02x", buf[a] & 0xff);

            fp = open(FIFO, O_WRONLY);
            write(fp, hex, 30);
            close(fp);
            usleep(10000);
        }
        close(pdesk[0]);
    }

    if (fork() == 0) {
        sleep(2);
        int fp;

        for (int i = 0; i < n; i++) {
            fp = open(FIFO, O_RDONLY);
            read(fp, f, 30);
            printf("Odczytano: %s\n", f);
            close(fp);
            usleep(10000);
        }
    }    
}
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
Michał
  • 43
  • 1
  • 7
  • No wonder you don't know what actually happens: there is not a single error check in the code. Fixing that first would have revealed the answer to you. – Nominal Animal Dec 20 '18 at 17:41

2 Answers2

1

If I understand your code correct, it will do the following:

With the first fork you start a child that reads from stdin and writes to the pipe.

Your parent process reads from the pipe and writes to the FIFO.

When your parent process has finished its loop it calls the second fork to create another child which will read from the FIFO and print the data.

When the loop count is too large you will reach the buffer limit of the FIFO and the parent will block because no process is reading from the FIFO. When the process is blocked in writing to the FIFO it will never create the child that is expected to read from the FIFO.

I think the main problem is that you should create the second child before starting the loop that reads the data from the pipe and writes to the FIFO.

Some additional remarks:

With cat /dev/urandom | ./a.out your program does not read /dev/urandom directly. It reads from a pipe which might behave differently.

You should always check the return value of read. It will tell you how many bytes it has read which may be less than you asked it to read. If you want to have exactly 15 characters you might have to read several times if you get less than 15 characters. The same applies to write.

Bodo
  • 9,287
  • 1
  • 13
  • 29
  • Additionally, I see no good reason to keep opening the FIFO and closing it again. This, too, can cause processes to block, perhaps indefinitely, because usually opening one end of a FIFO blocks until the other end is also opened by at least one process. Moreover, there is a risk of data loss from all the opening and closing, because a FIFO does not retain any data when neither end is open in any process. – John Bollinger Dec 20 '18 at 15:34
  • Thank you! I added larger comment below – Michał Dec 20 '18 at 17:20
0

Thank you very much. When the process that displays data is above other child processes, it finally works.

With cat /dev/urandom | ./a.out your program does not read /dev/urandom directly. It reads from a pipe which might behave differently.

How could I change it?

The programs also needs to read files the same way it reads from /dev/urandom, for example:

cat file.txt | ./a.out

I took your advice and started to check the value of read and now it doesn't go behind the range of file. The problem is I don't know how to check which parameter was called (and hence I can't check the length of file) - if it was file.txt, /dev/urandom, none or anything else. I tried with

int main(char argc, char* argv[])

but argv is always ./a.out, no matter what I call. Is there any way to check that?

Michał
  • 43
  • 1
  • 7
  • instead of `cat /dev/urandom | ./a.out` you can use `./a.out < /dev/urandom` without changing your program. You can replace `/dev/urandom` with any file. – Bodo Dec 20 '18 at 18:10
  • I think it is not a good idea to try to check the size of the input file because `/dev/urandom` or a pipe as input doesn't have a size. You can check the return code of `read` to find out when you reached the end of the file. – Bodo Dec 20 '18 at 18:14
  • If you want to see the name of your input file in `argc` you have to call cour program as `./a.out /dev/urandom` or `./a.out file.txt` and handle the command line arguments and open the file in your program. It will need a bit more effort if you want to allow both specifying the file name as command line argument (`./a.out file.txt`)or redirecting the input from the shell (`./a.out < file.txt`). – Bodo Dec 20 '18 at 18:18
  • @Bodo Thank you. Is it normal for multiple processes, that without `usleep` they sometimes desynchronize and eventually stop working? Same happens when I change argument of `usleep` to smaller value such as `usleep(10)` or `usleep(100)` – Michał Dec 21 '18 at 18:52
  • I didn't analyze what might be the effect of changing the `usleep` duration in your program and if you still have deadlock conditions. There is no guarantee about the timing of your processes. When one process is waiting for something, the OS will run a different process. With parallel processes it is possible to create deadlocks. If something blocks when you change some sleep time, you should try to analyze what the processes are waiting for. Probably there is some deadlock. You should also check the return code of all system calls or library functions and handle unexpected results. – Bodo Dec 27 '18 at 15:49
  • By the way - when changing `else` to `if(fork() ==0)` (so that communication will be between 3 childs, not 2 childs and 1 parent) program doesn't work properly. Is there any way to change it? – Michał Jan 13 '19 at 19:05
  • Ok, I found a solution. Just needed to add `waitpid(process_pid, NULL, 0)` for all child processes at the end of program. – Michał Jan 13 '19 at 19:44