2009-06-05 2 views
16

많은 작업자 스레드가있는 C 응용 프로그램이 있습니다. 이것들은 블록하지 않아야 작업자 스레드가 디스크의 파일에 쓸 필요가 있고, 그것들을 메모리의 원형 버퍼에 쓴 다음 그 버퍼를 디스크에 쓰는 전용 스레드를 가지고 있어야합니다.stdout을 메모리에 버퍼링하고 전용 스레드에서 쓰기하는 방법

작업자 스레드는 더 이상 차단하지 않습니다. 전용 스레드는 작업자 스레드에 영향을 미치지 않고 디스크에 쓰는 동안 안전하게 차단할 수 있습니다 (디스크에 쓰는 동안 잠금을 유지하지는 않습니다). 내 메모리 버퍼는 작가 스레드가 유지할 수있을만큼 충분히 크게 조정됩니다.

모두 훌륭한 작품입니다. 내 질문은, 내가 어떻게 stdout 비슷한 구현합니까?

매크로 printf()를 사용하여 메모리 버퍼에 쓸 수 있지만 stdout에 쓸 수있는 모든 코드 (일부는 타사 라이브러리에 있음)를 제어 할 수 없습니다.

생각하십니까? NickB

+0

파이프에 쓰기가 차단 될 수 있기 때문에 여기의 모든 솔루션이 잘못되었습니다. (그리고 그들을 nonblocking으로 설정하면'FILE'은 flush 할 필요가있을 때 블럭 될 경우 복구 할 수없는 에러 상태가됩니다.) –

답변

27

나는 freopen을 사용하는 것을 좋아합니다. dupdup2을 사용하여 stdout을 파이프로 리디렉션 한 다음 read을 사용하여 파이프의 데이터를 가져올 수도 있습니다. 그래서 같은

뭔가 :만이 종류의 작품의 4096 bigbuf를 사용

#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; 
} 
+0

내 문제를 해결하는 것처럼 들립니다. 나는 그것을 밖으로 시도 할 것이다. 고맙습니다! – NickB

+4

이와 같은 접근 방식을 시도했을 때, 파이프에 아무것도 없다면'read'가 멈추는 것을 발견했습니다. 그러나 '긴 플래그 = fcntl (out_pipe [0], F_GETFL); 플래그 | = O_NONBLOCK; fcntl (out_pipe [0], F_SETFL, flags);'init 섹션에서 문제를 수정했습니다. – aschepler

+0

정확히 내가 무엇을 찾고 있었습니까! – Marcin

4

freopen()을 사용하여 stdout을 파일로 리디렉션 할 수 있습니다.

man freopen는 말한다 :

freopen을() 함수는 그 이름 문자열이 경로 지적하고 그것으로 stream이 가리키는 스트림을 연관 파일 을 엽니 다. 원본 스트림 (있는 경우)은 입니다. mode 인수는 fopen() 함수에서와 마찬가지로 으로 사용됩니다. freopen() 함수는 주로 표준 텍스트 (stderr, stdin 또는 stdout)와 연결된 파일 을 변경하는 데 사용됩니다.

이 파일은 파이프 스레드 일 수 있습니다. 작업자 스레드는 해당 파이프에 쓰고 작성자 스레드는 수신 대기합니다.

+0

이것은 나의 문제를 해결하지 못합니다. printf() 호출을 만드는 스레드에서 디스크 쓰기를 이동하려고합니다. freopen()을 사용하면 여전히 stdout과 다른 파일 임에도 불구하고 내 printf() 호출이 파일에 기록됩니다. 디스크 파일이 아닌 freopen()에 "파일"을 지정할 수 있습니까? – NickB

+0

예. 파일 대신 파이프를 사용하십시오. – qrdl

0

setvbuf() 또는 setbuf()을 사용하여 버퍼링 작동 방식을 변경할 수 있습니다. 여기에 설명이 있습니다 : http://publications.gbdirect.co.uk/c_book/chapter9/input_and_output.html.

는 [편집]

stdout 정말 FILE*이다. 기존 코드가 FILE*과 함께 작동하는 경우 stdout과 작동하지 않는 이유는 무엇인지 알 수 없습니다.

2

전체 애플리케이션을 다른 애플리케이션으로 포장하지 않는 이유는 무엇입니까? 기본적으로 stdin을 stdout에 복사하고 필요에 따라 버퍼링하는 똑똑한 cat이 필요합니다. 그런 다음 표준 stdin/stdout 리다이렉션을 사용하십시오. 현재 응용 프로그램을 전혀 수정하지 않고도이 작업을 수행 할 수 있습니다.

~MSalters/# YourCurrentApp | bufcat 
8

GNU libc를 사용하는 경우 memory streams을 사용할 수 있습니다.

+1

이것이 진짜 맞는 대답이라고 생각합니다. 파이프 방식은 값 비싼 시스템 호출을 필요로하며 확실히 차단됩니다. –

0

하나의 해결 방법 (두 가지 작업 모두)은 writev을 통한 모임 작성을 사용하는 것입니다.

각 스레드는 예를 들어 sprintf를 iovec 버퍼에 넣은 다음 iovec 포인터를 작성자 스레드로 전달하고 단순히 stdout을 사용하여 writev를 호출하게 할 수 있습니다.여기

는 유사한 기능을 WSASend() 함수를 사용하는 것이 윈도우에서 Advanced Unix Programming

에서 writev는 사용의 예입니다.

0

방법. 나는이 코드를 시도해 보았고 버퍼로 stdout을 성공적으로 캡처했지만 실제로는 사용할 수 없다. 캡쳐 된 출력의 길이를 알 수있는 방법이 없으므로 '\ 0'문자열을 종료 할시기를 알 수 없습니다. 버퍼를 사용하려고하면 96 문자의 stdout 출력을 성공적으로 캡처 한 경우 4000 문자의 쓰레기가 튀어 나옵니다.

제 응용 프로그램에서는 C 프로그램에서 펄 인터프리터를 사용하고 있습니다. 어떤 문서가 C 프로그램에서 던져 지는지 얼마나 많은 출력이 침을 뱉어 낼지 전혀 모르므로, 위의 코드는 절대로 어디에서나 그 출력을 깨끗하게 인쇄 할 수는 없습니다.

+0

bigbuf를 0으로 채우면 bigbuf의 마지막 바이트가 0이 아닌 이상 문자열이 null로 끝날 것이라고 보장 할 수 있습니다. 그러나이 경우 오버플로를 감지 할 수 있습니다. –

관련 문제