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

发布于 2024-10-11 14:15:03 字数 1422 浏览 4 评论 0原文

我使用以下代码将标准输出重定向到管道,然后将所有数据从管道读取到缓冲区。我有两个问题:

第一个问题:当我发送一个大于管道的 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);
  }

I am using the following code to redirect stdout to a pipe, then read all the data from the pipe to a buffer. I have 2 problems:

first problem: when i send a string (after redirection) bigger then the pipe's BUFF_SIZE, the program stops responding (deadlock or something).

second problem: when i try to read from a pipe before something was sent to stdout. I get the same response, the program stops responding - _read command stuck's ...

The issue is that i don't know the amount of data that will be sent to the pipe after the redirection.

The first problem, i don't know how to handle and i'll be glad for help. The second problem i solved by a simple workaround, right after the redirection i print space character to stdout. but i guess that this solution is not the correct one ...

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

望喜 2024-10-18 14:15:03

您的问题是您正在使用阻塞 I/O 调用,而管道的两端都连接到同一进程。如果您不知道会有多少数据,那么这只是等待发生的死锁情况。

printf 是一个阻塞调用,这意味着它不会返回,直到所有数据都写入输出设备(本例中为管道),或者发出写入错误信号(例如,管道的另一端是封闭的)。
_read 的工作原理类似。仅当它具有完整的数据缓冲区或知道已到达输入末尾(可以通过关闭管道的写入端来发出信号)时,它才返回。

解决这个问题的唯一方法是

  • 使用非阻塞 I/O(如果您无权访问调用 printf 的代码,这是不可行的),或者
  • 确保读取和写入发生在不同的进程或线程,或者
  • 使用临时文件进行缓冲,而不是管道的缓冲区。

Your problem is that you are using blocking I/O calls, while both ends of the pipe are connected to the same process. If you don't know how much data there will be, this is just a deadlock situation waiting to happen.

printf is a blocking call, which means that it will not return until all data has been written to the output device (the pipe in this case), or until a write error is signalled (for example, the other end of the pipe is closed).
_read works similarly. It only returns when it has a full buffer worth of data or it knows that the end of the input has been reached (which can be signalled by closing the write-end of the pipe).

The only ways around this are

  • to use non-blocking I/O (which is not feasible if you don't have access to the code that calls printf), or
  • to ensure the reading and writing happens in different processes or threads, or
  • to use a temporary file for buffering, instead of the buffer of a pipe.
苄①跕圉湢 2024-10-18 14:15:03

管道是单向的。 IE。您可以向管道 (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 元素,那么您将运行陷入问题。

Pipes are unidirectional. Ie. you can either write to a pipe (x)or you can read from it.

To simulate a pipeline, try the following (the below is C, not 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;
}

[edit] By the way, your code does not overflow a buffer. Its only relation to buffer overflow is that you're reading into a statically allocated array...if you read() more than sizeof buffer elements, then you'll run into problems.

千寻… 2024-10-18 14:15:03

如果您不希望在这种情况下读取或写入被阻止,则必须使用非阻塞 I/O。

You must use non-blocking I/O if you don't want read or write to be blocked in this case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文