Подтвердить что ты не робот

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

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

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

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

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

Мысли? NickB

4b9b3361

Ответ 1

Мне нравится идея использования 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;
}

Ответ 2

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

Ответ 3

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

man freopen говорит:

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

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

Ответ 4

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

~MSalters/# YourCurrentApp | bufcat

Ответ 5

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

[изменить]

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

Ответ 6

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

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

Вот пример использования writev из Advanced Unix Programming

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

Ответ 7

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

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