我正在编写一个伪造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搜索指示的内容。
所以......
MyStderr
移动到eof的情况下获得尺寸?或
lseek
方法适用于MyStderr
?请注意。这是我能想到的唯一解决方案,使用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);
答案 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 ...