Переместить указатель stderr fd в начало - PullRequest
1 голос
/ 08 февраля 2012

Я пишу оболочку, которая разветвляется на 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, поэтому я не могу использовать его здесь.По крайней мере, это то, что показывает мой тестирование и поиск в стеке.

Итак ...

  1. Есть ли способ получить размер, не перемещая MyStderr в eof?

или

  1. Существует ли метод 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 итераций) это работает очень хорошо, без утечек или заметных вздутий.

Я многим обязан сообществу переполнения стека.Спасибо всем.

Приведенный ниже код не будет работать автономно.Это помещено здесь, чтобы проиллюстрировать, что я сделал.

.. после всего кода, который анализирует командную строку, разветвляется, выполняет execv и очищает ...

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 ]

4 голосов
/ 08 февраля 2012

Каналы недоступны для поиска - если вы читаете данные из канала, предоставленного другим процессом, вы не можете искать его поток данных. Вы должны хранить данные как прочитанные.

Причина, по которой вы, вероятно, сталкиваетесь с большим перерасходом памяти, связана с тем, как вы расширяете свой буфер - вы расширяете его только на постоянную величину каждый раз, когда он превышается. У вас будет гораздо больший успех, если вы удвоите его размер при превышении: это хорошо известная техника для динамических массивов .

Например:

// 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
2 голосов
/ 08 февраля 2012

Как уже говорили другие, трубы нельзя искать. Однако все это означает, что вам нужно убедиться, что stderr не труба. В обёртке, перед execv, делаем

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

Вызов open гарантированно устанавливает стандартный поток ошибок, поскольку всегда назначается самый низкий дескриптор свободного файла.

Затем fork должен дублировать файловый дескриптор, чтобы родительский процесс находился в нулевом байте, в то время как дочерний процесс начинает писать с начала. Нет необходимости искать.

Предполагается, что вы также не пытаетесь передать сообщение об ошибке в другом месте одновременно. В этом случае дочерний процесс должен будет продублировать вывод ошибки во внешне видимый канал и файл буферизации родителя, или вам придется выполнять потоковую передачу через родительский элемент вместо использования waitpid, с дочерним stderr и родительским stderr - это две разные трубы.

2 голосов
/ 08 февраля 2012

Можете ли вы записать вывод из stderr во временный файл? Затем вы можете иметь возможность случайного поиска по всему файлу, проверки размера, использования mmap() для него и т. Д. Вы можете даже делать это, пока вы все еще получаете данные.

0 голосов
/ 08 февраля 2012

Нет - stderr не буферизован.Ваш единственный выбор, чтобы буферизовать ввод самостоятельно.Я бы порекомендовал реализовать кольцевой буфер (если вы можете позволить себе потерять самый старый вывод) или просто создать фиксированный буфер некоторого «максимального размера».ИМХО ...

...