【问题标题】:How to prevent garbage being read from read end of PIPE in Linux如何防止在 Linux 中从 PIPE 的读取端读取垃圾
【发布时间】:2012-03-26 17:32:20
【问题描述】:

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

当 fork() 之后程序的第一次调用转到父函数时,问题就出现了。这里写端是关闭的。现在的问题是 read 调用正在将一些垃圾读入 buf 而 nread 给出的 value > 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);
}

【问题讨论】:

  • 您正在向管道写入 16 个字节,但字符串只有 5 个字节(6 个以 '\0' 结尾)。当你收到垃圾时,nread 是什么?你收到的字符串是什么?

标签: c linux pipe


【解决方案1】:

还有

msg1msg2 是小于 MSGSIZE 的字符串字面量。

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

不希望管道中有垃圾?在厨房水槽上使用排水管。

【讨论】:

    【解决方案2】:

    一个问题是这样的:

    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关于阻塞/非阻塞配置问题的回答。

    【讨论】:

    • 请注意,使用这种方法,read() 可能不会返回完整的字符串,这可能会或可能不会重要,具体取决于如何处理读取的数据。
    • @nos,我错过了非阻塞配置。鉴于有一个 strcmp() 我认为完整的字符串是必需的。
    【解决方案3】:

    你真正的问题是这一行:

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

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

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

    如果您不想发送固定大小的消息,或者想保持读取端非阻塞,事情就会变得更加棘手,因为您至少必须考虑以下情况:

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

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

    【讨论】:

      【解决方案4】:

      Read 不会终止输入。

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

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

      如果您想取消读取缓冲区,则需要进行 2 处更改。

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

      char buf[MSGSIZE + 1];
      

      2 。每次读取后 nul 终止 buf。

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

      【讨论】:

        猜你喜欢
        • 2020-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多