C语言 如何防止垃圾从 Linux 中 PIPE 的读取端读取



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

当 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 pipen");
        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] = '';
    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 conversationn");
                exit(0);
            }
            else
                printf("MSG=%sn" , 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);
}

一个问题是:

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

这仅将 buf 中的第一个字符设置为 NULL 终止符: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: %sn", strerror(errno));
}
else
{
    ...
}

编辑:

请参阅有关阻塞/非阻塞配置问题的回答。

你真正的问题是这一行:

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

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

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

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

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

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

Read 不会终止输入。

要打印未以 nul 结尾的字符缓冲区,请执行以下操作:

printf("MSQ=%.*sn", nread, buf);

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

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

char buf[MSGSIZE + 1];

2 . 每次读取后终止 BUF。

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

msg1msg2 是小于 MSGSIZE 的字符串文字。

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

不想在管道里放垃圾?在厨房水槽上使用排水陷阱。

最新更新