-1

So I have this problem, I need to create 3 processes (each handle a different task). The first process sends information over to the second (the first waits for an acknowledgement from the second). The second then sends information to the third (the second waits for an acknowledgement from the third). Then the third processes the final information... This process is supposed to loop over and over until process one analyzes an entire text file. So far, I tried writing the communication between the 3 processes with pipes. I'm not sure how I send an acknowledgment from process 2 to process 1 and process 3 to process 2. I'm also not entirely sure how to loop it. Thanks!

I have to use a stop and wait protocol... I'm not sure how that is done.

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

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

    int c = 0, t = 0;

    int fd1[2], fd2[2];


    char *theFile = "/Users/Desktop/thefile";

    FILE *file = fopen (theFile, "r");

    if (file == NULL) {
        perror("FILE DOES NOT EXIST");
        exit(1);
    }

    while (c == 0) {

        int status;

        char readbuffer[80];
        char readbuffer2[80];
        int tTemp = 0;

        pipe(fd1);
        pipe(fd2);

        pid_t pid = fork();

        if (pid < 0) {
            perror("Pipe Error");
            exit(1);
        }

        if (pid == 0) {
            //Child 1
            close(fd1[0]);
            close(fd2[0]);
            close(fd2[1]);

            char line [80];
            int c2 = 0;
            file = fopen (theFile, "r");
            while (fgets(line, sizeof(line), file) != NULL){
                if (c2 == t) {
                    printf("Line: %s\n", line);
                    break;
                }
                c2++;
            }
            if (t != c2) {
                c = 1;
            } else {
                write(fd1[1], line, (strlen(line)+1));
            }

            t++;

            exit(1);
        }

        pid_t pid2 = fork();

        if (pid2 < 0) {
            perror("Pipe Error");
            exit(1);
        }

        if (pid2 == 0) {
            //Child 2
            close(fd1[1]);
            close(fd2[0]);
            read(fd1[0], readbuffer, sizeof(readbuffer));
            printf("2nd Child string: %s\n", readbuffer);
            char string2[80] = "asdfasdf";
            write(fd2[1], string2, (strlen(string2)+1));
            exit(1);
        }

        pid_t pid3 = fork();

        if (pid3 < 0) {
            perror("Pipe Error");
            exit(1);
        }

        if (pid3 == 0) {
            //Child 3
            close(fd2[1]);
            close(fd1[0]);
            close(fd1[1]);
            read(fd2[0], readbuffer2, sizeof(readbuffer2));
            exit(1);
        }


        waitpid(pid, &status, 0);

        waitpid(pid2, &status, 0);

        waitpid(pid3, &status, 0);


    }

    fclose(file);
    return 0;
}
Username
  • 33
  • 6
  • For bidirectional communication between two processes, you would normally create *two* pipes -- one for each direction. As for looping, this is what `for(;;){}` loops, `while(){}` loops, and `do{}while()` loops are for. It's not clear what challenge you see to using one of those. – John Bollinger Apr 14 '15 at 19:50
  • @JohnBollinger, Well, the assignment stated to use waitpid() (so one pipe at a time) and stop and wait protocol for acknowledgment between the processes. I understand the loop thing. But the first process sends an individual text line to the second process until it finishes the entire file. – Username Apr 14 '15 at 19:52
  • It's also unclear what process1 is expected to do when it receives an acknowledgment from process2, or what process2 is expected to do when it receives an acknowledgment from process3. – John Bollinger Apr 14 '15 at 19:52
  • When it receives acknowledgment it is supposed to go to the next line of the file. When process 2 receives the acknowledgment it's supposed to grab the next line from process 1 – Username Apr 14 '15 at 19:53
  • The `waitpid()` syscall waits for a process to *stop*, typically by exiting. If your processes are going to do that at each iteration of a loop, then you will need to create new child processes, and possibly new pipes, at each loop iteration. – John Bollinger Apr 14 '15 at 19:54
  • I understand that, is that the best way to do it? – Username Apr 14 '15 at 19:55
  • "When it receives acknowledgment it is supposed to go to the next line of the file" -- this does not make sense. How can process 3 (say) know when it's appropriate for process 2 to read new input from process 1? Surely process 2 knows best when it's ready to read, and it can read until it has obtained a complete message, even if process 1 is slow to deliver one. – John Bollinger Apr 14 '15 at 19:58
  • Process 1, reads line by line, foreach line it sends it to process 2 (process 2 sends an acknowledgment to process 1) which performs a task with the string, it then sends it to process 3, process 3 sends an acknowledgment to process 2, process 2 then gets the next line from process 1. – Username Apr 14 '15 at 20:00
  • http://stackoverflow.com/questions/15643502/parent-trying-to-read-children-exit-status-or-return-value-fork-and-wait – jiveturkey Apr 14 '15 at 20:16
  • @jnbbender so, does this mean just loop the entire thing (pipes, forks()) ? – Username Apr 14 '15 at 20:18
  • @Username You can set it up however you want. If you need to wait for process 1 before going on to process 2, etc. (I think that's what you want) get rid of your `sleep`'s, slap the `waitpd`'s after each successful fork, make it a function and call it in a `while` loop – jiveturkey Apr 14 '15 at 20:39
  • @jnbbender so do I leave the waitpid where I have them? – Username Apr 14 '15 at 20:40
  • @Username No, put them at the end of the successful processing of each child, in the parent not inside the `if == 0` block. – jiveturkey Apr 14 '15 at 20:43
  • @jnbbender, that doesn't sound like it achieves the lockstep, line-at-a-time behavior that is (I think) requested. – John Bollinger Apr 14 '15 at 20:44
  • @jnbbender isn't that where they are now? – Username Apr 14 '15 at 20:45
  • I just updated the code, however, it's not working. – Username Apr 14 '15 at 20:49
  • Are sure you are supposed to use `waitpid()` to implement a stop&wait protocol (which should be doable, but a bit messy), or is use of `waitpid()` possibly a separate requirement from implementing "stop&wait", with a coincidental similarity in names? – John Bollinger Apr 14 '15 at 20:58
  • @JohnBollinger it says to use waitpid() for the parent to wait for the children to finish – Username Apr 14 '15 at 21:00
  • Since waiting for the children to *finish* is different from waiting for the children to indicate readiness for new data, I'm interpreting that as the "coincidence" option. Is that reasonable? – John Bollinger Apr 14 '15 at 21:03
  • @JohnBollinger Yeah ok, also I updated the code however "t" does not change. – Username Apr 14 '15 at 21:05

1 Answers1

0

As I have come to understand the problem via the comments, you are asking about two distinct requirements:

  1. implementing a "stop & wait" protocol between each pair of processes, and
  2. using waitpid() to collect child processes that finish

The latter is pretty straightforward; what you already have seems fine. The former is what you seem mostly to be stuck on.

There are a couple of things here. One is a question of semantics: stop & wait, in the form we are discussing it, requires the recipient of a message to acknowledge to the sender that a message was successfully received before the sender proceeds. There is a significant difference between characterizing the acknowledgment that way, and characterizing it as a signal for the receiver of the acknowledgment to perform any particular action. What the receiver does in response to the acknowledgment is its own concern, not inherent in the acknowledgment itself.

As to communications, then, I recommend establishing two pipes between each pair of processes, one for communication in each direction. To wait for an acknowledgment, then, a process just performs a blocking read on the appropriate pipe.

As for looping, each process must loop separately, but the loops will all take about the same form:

  1. Read the next line (process 1 gets lines from a file; the others get lines from pipes connected to the previous process)
  2. Terminate if no line is available
  3. Except for process 1, write a one-byte acknowledgment message on the pipe to the previous process
  4. Process the line
  5. Write the line to the appropriate terminus (process 3 writes lines to an unspecified terminus -- maybe stdout -- the others write lines to pipes connected to the next process).
  6. Except for process 3, perform a blocking read to receive an acknowledgment from the next process.
  7. Go to (1)

Be sure to check the result codes of all the functions that provide them. Since that's most library functions and syscalls it can get tedious, so I suggest a macro to help you out there.

For clarity and readability, I suggest writing the work of each of the three processes as a separate function. After fork() to create each process just handle file descriptor mangling as needed and call the appropriate function.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Ok, but if I use waitpid() doesn't the entire first process have to finish before the second one starts? – Username Apr 14 '15 at 21:48
  • No. As you have it structured, there are four processes: a common parent that manages all the child processes, and three children that perform the work. Only the process that calls `waitpid()` is affected by it. Since you have the parent calling it after `fork()`ing all the children, it will wait until all the children have terminated, but the children are not blocked from running in the mean time. – John Bollinger Apr 14 '15 at 21:53
  • So, would I place an if statement in each child, to see if the process before it put something in the pipe? – Username Apr 14 '15 at 22:34
  • No, you just perform a *blocking* read from the pipe on which that process sends data. The condition "no line is available" means that the read returns an end-of-file signal instead of any data. Caveat: to make that work properly, you must make sure to close all extra open file descriptors on the write ends of the communication pipes, especially any held by the parent process. – John Bollinger Apr 15 '15 at 14:05
  • Is it possible to provide a quick code snippet on how I would block? – Username Apr 15 '15 at 16:14
  • You're not getting it. You don't need to do anything special to perform blocking I/O. It's the norm. You can configure a file descriptor to operate in non-blocking mode, but that doesn't happen by itself. If the FD is in blocking (default) mode, then any `read()` or `fread()` (for example) that you perform on it will not return until at least one byte can be transferred, or end-of-file or an error is detected. – John Bollinger Apr 15 '15 at 16:24