如何在C/C++中防止缓冲区溢出?

3
我正在使用以下代码将标准输出重定向到管道,然后将所有数据从管道读取到缓冲区。我有两个问题:
第一个问题:当我发送比管道的BUFF_SIZE更大的字符串(在重定向之后),程序停止响应(死锁或其他情况)。
第二个问题:当我尝试在发送到标准输出之前从管道中读取时,我会得到相同的响应,程序停止响应——_read命令卡住了……
问题在于我不知道在重定向之后将发送多少数据到管道中。
对于第一个问题,我不知道如何处理,希望能得到帮助。第二个问题我通过简单的解决方法解决了,就是在重定向之后立即向标准输出打印空格字符。但我认为这种解决方案并不正确……
#include <fcntl.h>
#include <io.h>
#include <iostream>

#define READ 0
#define WRITE 1
#define BUFF_SIZE 5

using namespace std;

int main()
{

  int stdout_pipe[2];
  int saved_stdout;

  saved_stdout = _dup(_fileno(stdout));            // save stdout 

  if(_pipe(stdout_pipe,BUFF_SIZE, O_TEXT) != 0 )   // make a pipe
  {    
    exit(1);
  }

  fflush( stdout );

  if(_dup2(stdout_pipe[1], _fileno(stdout)) != 0 ) //redirect stdout to the pipe 
  { 
    exit(1);
  }

  ios::sync_with_stdio();    
  setvbuf( stdout, NULL, _IONBF, 0 );

  //anything sent to stdout goes now to the pipe
  //printf(" ");//workaround for the second problem

  printf("123456");//first problem

  char buffer[BUFF_SIZE] = {0};
  int nOutRead = 0;
  nOutRead = _read(stdout_pipe[READ], buffer, BUFF_SIZE); //second problem
  buffer[nOutRead] = '\0';

  // reconnect stdout

  if (_dup2(saved_stdout, _fileno(stdout)) != 0 ) 
  {        
         exit(1);
  }
  ios::sync_with_stdio();

  printf("buffer: %s\n", buffer);
  }

你的缓冲区大小为什么这么小? - JimR
5
为了避免缓冲区溢出,请勿将数据写入超出缓冲区末尾的位置。 - user562374
缓冲区仅对此示例较小...我使用一个大缓冲区,但我正在寻找一种技术,以拥有平均缓冲区,并在需要时(当它溢出)使其更大... - alexpov
“不要写在缓冲区的末尾” - 我该怎么做? 在我的代码中,在重定向之后,我调用了一个函数进行计算并打印到stdout。但是我没有这个函数的代码... - alexpov
你在这里写超过了数组的末尾:buffer[nOutRead] = '\0'; 更改传递给 _read 的长度为 BUFF_SIZE - 1,以避免当 _read 实际填充缓冲区时发生这种情况。 - JimR
3个回答

1

管道是单向的。也就是说,你可以写入一个管道(x),或者从它读取。

为了模拟一个管道,请尝试以下操作(下面是C代码,不是C++):

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

int main(int argc)
{
    int pfds[2];

    pipe(pfds);

    if (!fork()) {
        close(1);       /* close stdout, check for errors */
        dup(pfds[1]);   /* make stdout same as pfds[1], dup reuses lowest fd */
        close(pfds[0]); /* not needed */
        execlp("ls", "ls", NULL); /* or write() in whatever way you want */
    } else {
        close(0);       /* close stdin, check for errors please! */
        dup(pfds[0]);   /* make stdin same as pfds[0] */
        close(pfds[1]); /* not needed on this end */
        execlp("wc", "wc", "-l", NULL); /* or read() */
    }

    return 0;
}

[编辑] 顺便说一下,你的代码并没有溢出缓冲区。它与缓冲区溢出的唯一关系是你正在读取到一个静态分配的数组中...如果你read()超过了sizeof buffer元素,那么你就会遇到问题。


我忘记发布了,我希望我的代码在一个进程下运行...但我想我已经理解了问题和解决方案...谢谢。 - alexpov
无论如何,如果您想要双向通信,应该使用两个管道..这就是要点。祝好运。 :) - Michael Foukarakis

1
你的问题是使用了阻塞式 I/O 调用,而管道两端连接的是同一进程。如果你不知道会有多少数据,这只是等待死锁发生的局面。 printf 是一个阻塞调用,这意味着它将一直等到所有数据都被写入输出设备(在这种情况下是管道),或者直到发生写入错误(例如,管道的另一端被关闭)才返回。
_read 的工作方式也类似,在缓冲区装满数据或知道输入结束时才返回(可以通过关闭管道的写端来发出信号)。
唯一的解决方法是:
  • 使用非阻塞 I/O(如果没有访问调用 printf 的代码,则不可行),或
  • 确保读写发生在不同的进程或线程中,或者
  • 使用临时文件进行缓冲,而不是使用管道的缓冲。

我觉得我明白了,在我的情况下,使用文件作为缓冲是解决方案,谢谢。 - alexpov

0
你必须使用非阻塞式I/O,如果你不想在这种情况下被阻塞读取或写入。

你的意思是什么?就像我在之前的评论中所写的一样:在我的代码中,在重定向之后,我调用一个函数进行计算并打印到标准输出。我无法访问该函数的代码。 - alexpov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接