Why can't I write to or read from pipes?

时间:2016-02-12 20:01:24

标签: c concurrency pipe fork mapper

I've seen a lot of similar questions, but all of them addressed specific cases and did not assist me in finding a solution. I would appreciate any feedback on my situation, which is as follows:

I am trying to write a program to get character counts from a text file. The program works by forking four mappers and 26 reducers, and creating pipes for each of them. The parent process separates the input into four lines and passes one to each mapper, which counts the number of each character in its line. Each mapper then passes the counts on to the appropriate reducers, which sum up all four counts and print the result.

Below is my code thus far:

 int main(int argc, char *argv[])
{
  int i = 0;

  FILE *input = fopen("input.txt", "r");

  // Things for reading line-by-line: see getline reference on man7.org
  char *line = NULL;
  size_t len = 0;

  // Where we'll store the messages
  static char message[MSGSIZE];

  for(i = 0; i < NUMREDUCERS; i++)
    {
      pipe(reducer_pipes[i]);
    }

  for(i = 0; i < NUMMAPS; i++)
    {
      // Step 1: Create pipes for communication using the system call pipe()
      pipe(mapper_pipes[i]);

      // Step 2: Fork a number of mappers (4).
      if (fork() == 0)
    {
      // Don't want to close the write pipe yet
      // Child process: one of the mappers

      read(mapper_pipes[i][0], message, MSGSIZE); // Read from reading end
      char *msg = "Error reading from pipe";
      check_errors(msg);

      // Get char count and write to pipe
      int j = 0;
      int ccount = 0;
      for(j = 0; j < NUMREDUCERS; j++)
        {
          // Count up the number of chars
          ccount = count_char(message, (char) (j + 97), MSGSIZE);

          // Write to the appropriate reducer pipe
          write(reducer_pipes[j][1], (char *) ccount, MSGSIZE);
          msg = "error writing to reducer pipe";
          check_errors(msg);

        }

      exit(EXIT_SUCCESS);
    }
      else
    {
      getline(&line, &len, input);
      // Parent process

      write(mapper_pipes[i][1], line, (int) len);
      char *msg = "Error writing to pipe";
      check_errors(msg);
    }
    }

  return 0;
}

The problem I am encountering is that I cannot write to the reducer pipes. I get a bad address error whenever I attempt to write to, read from, or close them. Did they somehow expire? Did I not create them correctly? If anyone has input to offer, I would greatly appreciate it.

Quick edit: I have removed all of my "close" statements, as they had the same issue. I have, however, tried closing the pipes where they should be closed, only to find the same error message.

2 个答案:

答案 0 :(得分:1)

"Bad address" (MSGSIZE) means you have passed an invalid pointer to a system call. It's essentially the equivalent of a segfault (I believe some systems actually just raise errno == EFAULT in this situation).

Look at this line:

SIGSEGV

Here write(reducer_pipes[j][1], (char *) ccount, MSGSIZE); is of type int. Casting an int to a pointer is always suspicious.

In the previous line, you assigned ccount to the return value of ccount. Now you didn't show us the code for that function, but I'm going to guess it returns a count of characters - most likely a small integer. Let's say it returned 17. You are telling count_char() to write write bytes which are located at address 17. That is assuredly not what you want.

If you wanted to send that integer to the reducer, in binary format, you probably wanted to say

MSGSIZE

And of course you'll have to have matching code on the reducer side.


Other people have addressed a number of other issues in your code, such as the fact that you can't reliably detect errors just by looking at write(reducer_pipes[j][1], &ccount, sizeof(ccount)); (which presumably is what errno does). If a system call doesn't have an error, it leaves check_errors untouched, even if it had a nonzero value previously. What you should do instead is check the return value from errno; if it is write() then an error occurred and only then should you look at -1 (or call errno) to see what it was.

Basically, any time you make a system call and don't check the return value, you have a bug.

答案 1 :(得分:0)

You don't show what your assert( buffer==0 || buffer_len >= sizeof(BTreeNodeWriter_X<X>) ); assert( buffer==0 || buffer_len >= sizeof(BTreePackedNodeWriter_X<X>) ); function is, but I would guess it is just printing an error message without actually checking for errors, under the assumption that you only call it after an error. So you might not actually be getting an error.

You need to actually check the return value of your read and write calls to see if an error occured:

check_errors

Note that the return value also tells you the length of the message received if there isn't an error (and you should use that instead of if ((len = read(mapper_pipes[i][0], message, MSGSIZE)) < 0) // Read from reading end perror("Error reading from pipe"); in the subsequent code that looks at the message.