简体   繁体   中英

Why does my code using piping hang?

I have the following code:

 switch(fork())
    {
        case -1:
           /*error case*/
           error = errno;
           printf("fork error(1): %s\n", strerror(error));
           break;

        case 0: //child case: execute remove_non_alpha and send result to pfd write end
            remove_non_alpha(arg_str);

            res = write(pfd[1], arg_str, arg_str_size);
            if(res != arg_str_size)
            {
                return -1;
            }

            char *arg_str2 = NULL;
            res = read(pfd[0], &arg_str2, 1); //hang happens right here

            break;

        default:
            /*parent case-- fall through*/ 
            break;
    }

pfd is created using pipe() . arg_str is a non-empty char * string, and arg_str_size is an int equal to the size of arg_str . I added the read() call as a debugging statement to make sure that my data was successfully written to the pipe. However, when I call it, the call hangs indefinitely. Can someone help explain to me what I am doing wrong here?

You need to allocate some memory to read() into. Right now you're trying to read() into the pointer , rather than reading into some memory that the pointer is pointing to.

That being said, although what you're doing isn't going to work, it shouldn't cause read() to block. You're only trying to read() 1 byte, and a pointer is valid memory, even if it doesn't make sense to read() into it like you're doing. There's a lot of code you're not showing, so your problem is likely somewhere else. In principle, what you're doing should work, like so:

#define _POSIX_C_SOURCE 200809L

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    char mystring1[] = "Some output";
    char mystring2[100] = {0};

    int pfd[2];
    if ( pipe(pfd) == -1 ) {
        perror("error calling pipe");
        return EXIT_FAILURE;
    }

    if ( write(pfd[1], mystring1, sizeof mystring1) == -1 ) {
        perror("error calling write()");
        return EXIT_FAILURE;
    }

    if ( read(pfd[0], mystring2, 100) == -1 ) {
        perror("error calling read()");
        return EXIT_FAILURE;
    }

    printf("Read '%s' from pipe.\n", mystring2);

    if ( close(pfd[0]) == -1 || close(pfd[1]) == -1 ) {
        perror("error calling close");
        return EXIT_FAILURE;
    }

    return 0;
}

which outputs:

paul@thoth:~/src/sandbox$ ./sillypipe
Read 'Some output' from pipe.
paul@thoth:~/src/sandbox$ 

Your best strategy is to simplify your program until you hit the thing that's going wrong. It's not clear, for instance, how you're demonstrating that read() really is what's causing your program to hang, rather than something it's doing later, and you don't show what your parent process might be doing with that pipe, for instance.

With a little effort one can make a self-contained example that works properly for the usually useless case of reading and writing the pipe in only one of the processes .

I say useless because the point of having a pipe is usually to communicate between different processes .

Minor differences aside, it is similar to @Paul Griffiths above.

working.c

#include <stdio.h>
#include <stdlib.h>

int go(){

  int pfd[2];
  const char *arg_str = "Can you read it now\n"; 
  int arg_str_size = 20;
  int res;
  char arg_str2[30];

  if (pipe (pfd))
    {
      fprintf (stderr, "Pipe failed.\n");
      return -999;
    }

  switch(fork())
    {
        case -1:
           fprintf(stderr, "fork error(1) \n");
           break;

        case 0: //child case: send result to pfd write end
      res = write(pfd[1], arg_str, arg_str_size);
      if(res != arg_str_size)
            {
          return -1;
            }

      res = read(pfd[0], &arg_str2, arg_str_size); //won't hang because unread by parent

      fprintf(stderr, "read: %s \n", arg_str2);

      break;

    default:
      /*parent case-- fall through*/ 
      break;
    }
 return 0;
}

void main(){
  int x = go();
}

Notice that some space is allocated for char arg_str2[30]; If instead, as in the OP code, the code used char *arg_str2 = NULL; which is undefined behaviour, and could result in an error signal like SIGSEGV (segmentation violation), etc.

Don't do the following: If both parent and child read from the same pipe, one will block.

If you want both parent and child to be able to read and write, then you need two pipe() calls. If you are trying to test in the code to ensure correctness, test only the error indicators from reading and writing, do not test by reading back from a pipe what was just written. A pipe is a special kind of file, not an ordinary disk file.

Data written into a pipe is written into a first-in-first-out (FIFO) queue, and deleted once read. Reading an empty pipe will block (wait), which can seem like a hang.

We can see that behavior here. With both processes reading the pipe (which code **can but shouldn't ** do) one process will read the contents of the pipe, but the other will hang.

hang.c

#include <stdio.h>
#include <stdlib.h>

int go(){

  int pfd[2];
  const char *arg_str = "Can you read it now\n";
  int arg_str_size = 20;
  int res;
  char arg_str2[30];

  if (pipe (pfd))
    {
      fprintf (stderr, "Pipe failed.\n");
      return -999;
    }

  switch(fork())
    {
        case -1:
           fprintf(stderr, "fork error(1) \n");
           break;

        case 0: //child case: send result to pfd write end
      res = write(pfd[1], arg_str, arg_str_size);
      if(res != arg_str_size)
            {
          return -1;
            }

      res = read(pfd[0], &arg_str2, arg_str_size); 

      fprintf(stderr, "child read: %s \n", arg_str2);

      break;

    default:
      /*parent case-- try to read here as well (not a good idea) */ 
      res = read(pfd[0], &arg_str2, arg_str_size); 
      fprintf(stderr, "parent read: %s \n", arg_str2);
      break;
    }
 return 0;
}

void main(){
  int x = go();
}

To get the child to block instead of the parent, add some sleep() calls so that the child reads second and so that the parent sticks around after reading.

Final Note: As you might imagine, filling in the rest of the C code from your example takes time and delays getting an answer. Often times, if you go to the trouble of constructing good test cases, or "Minimum Complete Verifiable Example" MCVE , you will be able to answer your own question!

You are deadlocking the content what you are doing is first reading and then writing. If you put fprintf before fscanf for at least one process it will continue

#include <stdio.h>
#include <unistd.h>

int main() {
int child_to_parent[2];
int parent_to_child[2];
pipe(child_to_parent);
pipe(parent_to_child);

pid_t id = fork();

if (id == 0) {
    close(parent_to_child[1]);
    close(child_to_parent[0]);
    FILE* out = fdopen(child_to_parent[1], "w");
    FILE* in = fdopen(parent_to_child[0], "r");

    char msg[7];

    fprintf(out, "hi\n");
    fflush(out);

    fscanf(in ,"%s", msg);
    printf("Child got: %s\n", msg);


    printf("Child sent: hi\n"); 

} else {
    close(parent_to_child[0]);
    close(child_to_parent[1]);
    FILE* in = fdopen(child_to_parent[0], "r");
    FILE* out = fdopen(parent_to_child[1], "w");

    fprintf(out, "hello");
    fflush(out);
    printf("Parent sent: hello\n");

    char msg[4];
    fscanf(in, "%s", msg);
    printf("Parent got: %s\n", msg);

}

Also don't forget to add \\n to fprintf as fscanf will wait newline. As you see for buffering mentioned flushing will help,

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM