How to prevent garbage reading from end of PIPE read in Linux

I wrote a little code using pipe and fork. The child process calls the child function, which writes to the pipe. The parent process calls the parent function, which reads from the pipe.

The problem arises when the first call to the program after fork () goes to the parent function. Here the end of the recording is closed. Now the problem is that the read call reads some garbage in buf, and nread has a value> 0. How can this be prevented.

Using Linux 2.6.32-30-generic and gcc 4.4.3. Below is the code ::

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

#define MSGSIZE 16

void parent(int* p);
void child(int* p);

char* msg1 = "hello";
char* msg2 = "bye";

int main()
{
    int pfd[2];
    if(pipe(pfd) == -1)
    {
        printf("Unable to create pipe\n");
        exit(1);
    }
    fcntl(pfd[0],F_SETFL,O_NDELAY);

    if(fork() == 0)
        child(pfd);
    else
        parent(pfd);

    return 0;
}

void parent(int p[2])
{
    int nread;
    char buf[MSGSIZE];
    buf[0] = '\0';

    close(p[1]);
    for(;;)
    {
        nread = read(p[0] , buf , MSGSIZE);
        if(nread == 0)
        {
            printf("pipe Empty/n");
            sleep(1);
        }
        else
        {
            if(strcmp(buf,msg2) == 0)
            {
                printf("End of conversation\n");
                exit(0);
            }
            else
                printf("MSG=%s\n" , buf);
        }
    }
}

void child(int p[2])
{
    int count;
    close(p[0]);
    for(count = 0 ; count < 3 ; count++)
    {
        write(p[1],msg1 , MSGSIZE);
        sleep(3);
    }
    write(p[1],msg2,MSGSIZE);
    exit(0);
}

      

+3


source to share


4 answers


One problem is this:

char buf[MSGSIZE];
buf[0] = '\0';

      

it only sets the first character in buf

to the null terminator: the remaining characters in buf

are concatenated. read()

tries to read 16

bytes, which mean that characters in buf

will not be zero-terminated, but printf("%s", buf)

requires that it be buf

zero-terminated. Even if it buf

was initialized correctly, it would still be undersized due to its size 16

, but read()

reading it 16

, also leaving no room for a null terminator.

Possible fix:

char buf[MSGSIZE + 1] = ""; /* +1 added to store the null terminator and
                               all characters set to 0 (null terminator). */

      

Another problem is write()

(as Joachim Pileborg commented ):

write(p[1],msg1 , MSGSIZE);
write(p[1],msg2 , MSGSIZE);

      

msg1

and the bytes msg2

are not long 16

. Change to:



write(p[1],msg1 , strlen(msg1));
write(p[1],msg2 , strlen(msg2));

      

It also read()

returns -1

on failure, so not enough:

nread = read(p[0] , buf , MSGSIZE);
if(nread == 0)
{
    ...
}

      

Also check -1

:

else if(nread == -1)
{
    fprintf(stderr, "read() failed: %s\n", strerror(errno));
}
else
{
    ...
}

      

EDIT:

See nos 's answer regarding locking / non-locking configuration issues.

+4


source


Your real problem is this line:

fcntl(pfd[0],F_SETFL,O_NDELAY);

      

This ensures that the end of the read channel is not blocked. Thus, each call to read () returns the same amount of data as in the buffer, or returns -1 and sets errno to EWOULDBLOCK if there is no data at that particular time.

However, your code does not handle this case, it only checks if(nread == 0)

and outputs the buffer even if you haven't read anything. So remove this line.



If you don't want to send messages of a fixed size, or you want to prevent the end of reading from blocking, things get more complicated as you have to consider at least these cases:

  • read () returns -1 and sets errno to EWOULDBLOCK (just try read () again).
  • read () reads the first 4 bytes of your "message", and the next read returns the rest of the message.
  • read () reads the first message and half of the subsequent message.

i.e. you need some form of frames / delimiters for your posts that you need to process, unless you just need to pass the feed on.

+3


source


Read

does not nul-complete input.

To print a character buffer that is not nul-terminated, follow these steps:

printf("MSQ=%.*s\n", nread, buf);

      

If you want to nul-terminate the read buffer, you need to make 2 changes.

1. Increase the buffer size to MSGSIZE + 1:

char buf[MSGSIZE + 1];

      

2.nul-terminate buf after every read.

buf[nread > 0 ? nread : 0] = 0;  // read returns -1 on error

      

0


source


Also

msg1

and msg2

- string literals that are smaller MSGSIZE

.

The garbage thing is a principle called by GIGO: garbage, garbage.

Don't need garbage in the chimney? Use a drain trap on your kitchen sink.

0


source







All Articles