如何防止在Linux中从PIPE的读端读取垃圾

时间:2012-03-09 08:42:47

标签: c linux pipe

我用管道和叉子编写了一个小代码。子进程调用写入管道的子函数。父进程调用从管道读取的父函数。

当fork()之后第一次调用程序进入父函数时出现问题。这里写入结束。现在问题是读取调用正在读取一些垃圾进入buf而nread正在给出值> 0。如何防止这种情况。

使用Linux 2.6.32-30-generic和gcc 4.4.3。下面是代码::

#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);
}

4 个答案:

答案 0 :(得分:4)

一个问题是:

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

这只将buf中的第一个字符设置为空终止符:buf中的其余字符被整合。 read()正在尝试读取16个字节,这意味着buf中的字符不会被空终止,printf("%s", buf)要求buf为空终止。即使buf已正确初始化,由于其大小为16,但read()读数16也不足,因此无法为空终结符留出空间。

可能的解决方法是:

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

另一个问题是write() s(由Joachim Pileborg评论):

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

msg1msg2的长度不是16个字节。改为:

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

此外,read()在失败时返回-1,因此以下内容是不够的:

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

同时检查-1

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

编辑:

请参阅nos关于阻止/非阻止配置问题的回答。

答案 1 :(得分:3)

你真正的问题是这一行:

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

这会将读取结束管道设置为非阻塞。因此,每次read()调用都将返回与缓冲区中一样多的数据,如果在此特定时间没有要读取的数据,则返回-1并将errno设置为EWOULDBLOCK。

但是,您的代码不处理这种情况,它只检查if(nread == 0)并打印出缓冲区,即使您没有读取任何内容。所以删除该行。

如果您不想发送固定大小的消息,或者希望保持读取结束不阻塞,则事情变得更加棘手,因为您必须考虑至少这些情况:

  • read()返回-1并将errno设置为EWOULDBLOCK(再次尝试read())。
  • read()读取“message”的前4个字节,下一个读取返回消息的其余部分。
  • read()读取第一条消息,后续消息的一半。

即。除非你只需要进一步传输管道的内容,否则你需要在你需要处理的消息上使用某种形式的框架/分隔符。

答案 2 :(得分:0)

Read不会终止输入。

要打印非空终止的字符缓冲区,请执行以下操作:

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

如果要终止读缓冲区,则需要进行2次更改。

1。将缓冲区的大小增加到MSGSIZE + 1:

char buf[MSGSIZE + 1];

2。每次阅读后都会终止buf。

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

答案 3 :(得分:0)

同时

msg1msg2是字符串文字,小于MSGSIZE

关于垃圾的事情就是这个叫GIGO的原则:垃圾输入,垃圾输出。

不要管道里有垃圾吗?在厨房水槽上使用排水管。