将stderr fd指针移动到start

时间:2012-02-08 16:30:24

标签: c linux stderr

我正在编写一个伪造execv()进程的包装器。子项的输出在stderr中捕获。当waitpid()发布时,我可以阅读stderr的内容并将其报告回来。

就我而言,我想动态分配缓冲区并将stderr写入此缓冲区。

要调整缓冲区的大小,我可以realloc(),但这不是很有效,我发现它往往会使内存池使用膨胀。相反,我想知道大小而不改变它的指针。

请考虑以下事项,并注意MyStderr只是stderr的占位符:

int size=0;
int ch=0;
// int MyStderr is the stderr fd
FILE *nCountFD = fdopen(MyStderr, "r");
while ((ch = getc(nCountFD)!=EOF)
{
    ++size;
}
printf("Size is %ld\n", size);

我在这里得到了尺寸。但是,现在MyStderr的文件指针位于其缓冲区的末尾。

我尝试使用lseek

lseek()stderr失败,所以我不能在这里使用它。至少,这是我的测试和stackoverflow搜索指示的内容。

所以......

  1. 有没有办法在不将MyStderr移动到eof的情况下获得尺寸?
    1. 是否有lseek方法适用于MyStderr
    2. 请注意。这是我能想到的唯一解决方案,使用realloc ..

      char *buf=(char*)malloc(80);
      char *NewBuf=NULL;
      int n=80;
      while ((ch = getc(nCountFD)!=EOF)
      {
          buf[i]=ch;
          ++size;
          if (size>n)
          {
             n=n+80;
             NewBuf= realloc(buf, n);
             // some code to make sure it works here //
             buf=NewBuf;
          }
      }
      printf("Size is %ld\n", size);
      

      现在更新

      我没有构建解决stderr无缓冲这一事实的功能,而是决定使我的结果缓冲区的初始malloc足够大,以便在大多数情况下不太可能使用realloc()。如果发生realloc(),则建议的每个realloc()的原始分配大小加倍。

      在测试中(100000次迭代),这种方法非常有效,没有泄漏或可辨别的膨胀。

      我非常感谢Stack Overflow社区。谢谢大家。

      以下代码不会独立运行。它放在这里是为了说明我的所作所为。

      .. 在解析命令行,分叉,执行和清理所有代码之后......

      while (waitpid(nPID, &status, 0) != nPID)
          ;
      i = 0;
      nFD = fdopen(nErrFD, "r");
      if (!nFD) {
          snprintf(cErrMsg, 80, "Cannot open fd[%i].  Failed to spaw process",
                  nErrFD);
          cbuf = strcpy(cbuf, cErrMsg);
          goto NECerror;
      }
      
      close(nErrFD);
      
      
      cbuf = calloc(nBufSz, sizeof(char));
      memset(cbuf, 0x00, nBufSz);
      i = 0;
      while ((ch = getc(nFD)) != EOF) {
          cbuf[i] = (char) ch;
          ++size;
          ++i;
          if (size > nBufSz) {
              nBufSz = nBufSz + nBaseBufSz;
              NewBuf = realloc(cbuf, nBufSz);
              if (NewBuf == NULL) {
                  snprintf(cErrMsg, 80,
                          "Internal error:cannot allocate [%i] bytes", nBufSz);
                  cbuf = strcpy(cbuf, cErrMsg);
                  fclose(nFD);
                  goto NECerror;
              }
              cbuf = NewBuf;
              free(NewBuf);
          }
      
      }
      fclose(nFD);
      

4 个答案:

答案 0 :(得分:4)

管道不可寻找 - 如果您正在从另一个进程提供的管道中读取数据,那么您无法查找其数据流。您必须将数据存储为读取数据。

您可能会看到大量内存抖动的原因是您扩展缓冲区的方式 - 每次超出时只会将其扩展一个恒定的数量。如果双倍超出其大小,您将获得更好的成功:这是dynamic arrays的一种众所周知的技术。

例如:

// Error checking omitted for expository purposes
size_t size = 4096;
char *buf = malloc(size);
char *bufPtr = buf;
ssize_t n;
while((n = read(MyStderr, bufPtr, 4096) > 0)
{
    bufPtr += n;

    // Make sure we always have at least 4096 bytes of free space in the
    // buffer for the next read
    if(size - (bufPtr - buf) < 4096)
    {
        size *= 2;  // Watch out for overflow!
        buf = realloc(buf, size);
    }
}

// The (bufPtr - buf) bytes of buf now hold the entirety of the process's stderr

答案 1 :(得分:2)

您可以将stderr的输出写入临时文件吗?然后,您可以在整个文件中随机搜索,检查大小,在其上使用mmap()等。您甚至可以在仍然接收数据时执行这些操作。

答案 2 :(得分:2)

正如其他人所说,管道不可寻找。但是,所有这些意味着您需要确保stderr不是管道。在包装器中,在execv之前,执行

close( STDERR_FILENO );
open( error_log_path, O_RDWR | O_TRUNC ); // opens into STDERR_FILENO

open调用保证设置标准错误流,因为最低的空闲文件描述符始终是指定的。

然后,fork应该复制文件描述符,因此父进程位于字节零处,而子进程从头开始写入。没有必要寻求。

这假设您还没有尝试同时将错误输出传递到其他位置。在这种情况下,子进程必须将错误输出复制到外部可见管道和父级的缓冲文件,或者您必须通过父级流而不是使用waitpid与子级{{1 }和父stderr是两个不同的管道。

答案 3 :(得分:0)

不 - stderr没有缓冲。您唯一的选择是自己缓冲输入。我建议实现一个循环缓冲区(如果你能负担得起丢失最老的输出),或者只是创建一些“max sizse”的固定缓冲区。 IMHO ...