Как буферизировать stdout в памяти и записать его из выделенного потока

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

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

Это все прекрасно работает. Мой вопрос в том, как реализовать что-то подобное для stdout?

Я мог бы макрос функции printf() для записи в буфер памяти, но у меня нет контроля над всем кодом, который может писать в stdout (некоторые из них в сторонних библиотеках).

мысли? NickB

7 ответов


мне нравится идея использовать freopen. Вы также можете перенаправить stdout в трубу с помощью dup и dup2, а затем использовать read для захвата данных из трубы.

что-то вроде так:

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

#define MAX_LEN 40

int main( int argc, char *argv[] ) {
  char buffer[MAX_LEN+1] = {0};
  int out_pipe[2];
  int saved_stdout;

  saved_stdout = dup(STDOUT_FILENO);  /* save stdout for display later */

  if( pipe(out_pipe) != 0 ) {          /* make a pipe */
    exit(1);
  }

  dup2(out_pipe[1], STDOUT_FILENO);   /* redirect stdout to the pipe */
  close(out_pipe[1]);

  /* anything sent to printf should now go down the pipe */
  printf("ceci n'est pas une pipe");
  fflush(stdout);

  read(out_pipe[0], buffer, MAX_LEN); /* read from pipe into buffer */

  dup2(saved_stdout, STDOUT_FILENO);  /* reconnect stdout for testing */
  printf("read: %s\n", buffer);

  return 0;
}

Если вы работаете с GNU libc, вы можете использовать потоки памяти.


вы можете "перенаправить" stdout в файл с помощью freopen().

man freopen говорит:

функция freopen() открывает файл чье имя указывает строка по пути и связывает поток указал на ручей с ним. Этот исходный поток (если он существует) закрытый. Используется аргумент mode как и в функции fopen (). Первичное использование freopen() функции для изменения файла связанный со стандартным текстом потоком (stderr, stdin или stdout).

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


почему бы вам не обернуть всю программу в другую? В принципе, то, что вы хотите, это smart cat это копирует stdin в stdout, буферизуя по мере необходимости. Затем используйте стандартное перенаправление stdin/stdout. Это можно сделать без изменения текущего приложения вообще.

~MSalters/# YourCurrentApp | bufcat

вы можете изменить, как буферизация работает с setvbuf() или setbuf(). Здесь есть описание:http://publications.gbdirect.co.uk/c_book/chapter9/input_and_output.html.

[Edit]

stdout действительно FILE*. Если существующий код работает с FILE*s, Я не вижу, что мешает ему работать с stdout.


одним из решений (для обеих вещей, которые вы делаете ) было бы использовать сбор записи через writev.

каждый поток может, например, sprintf в буфер iovec, а затем передать указатели iovec в поток writer и просто вызвать writev с помощью stdout.

вот пример использования writev из Расширенное Программирование Unix

в Windows вы бы использовали WSAsend для аналогичных функций.


метод, использующий 4096 bigbuf, будет только работать. Я пробовал этот код, и хотя он успешно захватывает stdout в буфер, он непригоден для использования в реальном мире. У вас нет способа узнать, как долго захваченный вывод, поэтому нет способа узнать, когда завершить строку "\0". Если вы попытаетесь использовать буфер, вы получите 4000 символов мусора, если вы успешно захватили 96 символов вывода stdout.

в моем приложении я использую perl переводчик в программе на Си. Я понятия не имею, сколько вывода будет выплюнуто из того, что когда-либо документ брошен в программу C, и, следовательно, приведенный выше код никогда не позволит мне чисто распечатать этот вывод в любом месте.