2009-06-05 32 views
16

Tôi có một ứng dụng C với nhiều chuỗi công nhân. Điều quan trọng là chúng không bị chặn, do đó các luồng công nhân cần phải ghi vào một tệp trên đĩa, tôi đã ghi chúng vào một bộ đệm tròn trong bộ nhớ, và sau đó có một luồng chuyên dụng để ghi bộ đệm đó vào đĩa.Làm thế nào để đệm bộ đệm trong bộ nhớ và ghi nó từ một chủ đề chuyên dụng

Chuỗi công việc không chặn nữa. Chuỗi chuyên dụng có thể chặn một cách an toàn khi ghi vào đĩa mà không ảnh hưởng đến chuỗi công việc (nó không giữ khóa khi ghi vào đĩa). Bộ đệm bộ nhớ của tôi được điều chỉnh đủ lớn để chuỗi ghi có thể theo kịp.

Tất cả đều hoạt động tốt. Câu hỏi của tôi là, làm thế nào để thực hiện một cái gì đó tương tự cho stdout?

Tôi có thể printf() để viết vào bộ đệm bộ nhớ, nhưng tôi không có quyền kiểm soát tất cả mã có thể ghi vào stdout (một số trong thư viện của bên thứ ba).

Suy nghĩ? NickB

+0

Tất cả các giải pháp ở đây đều sai, bởi vì việc ghi vào đường ống có thể chặn. (Và nếu bạn đặt chúng là nonblocking, 'FILE' sẽ chỉ nhận được trong trạng thái lỗi không thể khôi phục nếu nó sẽ chặn khi nó cần tuôn ra.) –

Trả lời

27

Tôi thích ý tưởng sử dụng freopen. Bạn cũng có thể chuyển hướng stdout đến một đường ống sử dụng dupdup2, sau đó sử dụng read để lấy dữ liệu từ đường ống.

Something như vậy:

#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

Điều này nghe có vẻ như nó giải quyết được vấn đề của tôi. Tôi se thử no. Cảm ơn bạn! – NickB

+4

Khi tôi thử một cách tiếp cận như thế này, tôi thấy 'đọc 'treo nếu không có gì trong ống. Nhưng thêm 'long flags = fcntl (out_pipe [0], F_GETFL); flags | = O_NONBLOCK; fcntl (out_pipe [0], F_SETFL, flags); 'tới phần init đã sửa lỗi đó. – aschepler

+0

Chính xác những gì tôi đang tìm kiếm! – Marcin

4

Bạn có thể "chuyển hướng" stdout vào tệp bằng cách sử dụng freopen().

man freopen nói:

Các freopen() chức năng mở file có tên là chuỗi chỉ vào bởi đường dẫn và liên kết các dòng trỏ đến bởi dòng với nó. Dòng gốc (nếu nó tồn tại) là đã đóng. Đối số chế độ được sử dụng giống như trong hàm fopen(). Việc sử dụng chính hàm freopen() là thay đổi tệp được kết hợp với văn bản chuẩn luồng (stderr, stdin hoặc stdout).

Tệp này cũng có thể là một chuỗi công nhân - chủ đề sẽ ghi vào chuỗi và chủ đề của người viết đó sẽ nghe.

+0

Điều này không giải quyết được vấn đề của tôi. Tôi đang cố gắng di chuyển đĩa bằng văn bản ra khỏi thread làm cho printf() gọi. Sử dụng freopen() sẽ vẫn có các cuộc gọi printf() của tôi ghi vào một tập tin, mặc dù một tập tin khác để stdout. Có thể cho tôi để chỉ định một "tập tin" để freopen() mà không phải là một tập tin đĩa? – NickB

+0

Chắc chắn. Sử dụng đường ống thay vì tệp. – qrdl

0

Bạn có thể thay đổi cách hoạt động của bộ đệm với setvbuf() hoặc setbuf(). Có mô tả ở đây: http://publications.gbdirect.co.uk/c_book/chapter9/input_and_output.html.

[Chỉnh sửa]

stdout thực sự là một FILE*. Nếu mã hiện tại hoạt động với FILE* s, tôi không thấy điều gì ngăn nó hoạt động với stdout.

2

Tại sao bạn không quấn toàn bộ ứng dụng của mình vào một ứng dụng khác? Về cơ bản, những gì bạn muốn là một thông minh cat sao chép stdin để stdout, đệm khi cần thiết. Sau đó sử dụng chuyển hướng tiêu chuẩn stdin/stdout. Điều này có thể được thực hiện mà không cần sửa đổi ứng dụng hiện tại của bạn.

~MSalters/# YourCurrentApp | bufcat 
8

Nếu bạn đang làm việc với GNU libc, bạn có thể sử dụng memory streams.

+1

Điều này tôi nghĩ là câu trả lời đúng. Cách tiếp cận đường ống liên quan đến các cuộc gọi hệ thống đắt tiền và chắc chắn sẽ chặn. –

0

Một giải pháp (đối với cả hai việc bạn làm) sẽ sử dụng tính năng ghi thu thập qua writev.

Mỗi chủ đề có thể cho sprintf ví dụ vào bộ đệm iovec và sau đó chuyển con trỏ iovec đến chuỗi ghi và chỉ đơn giản là gọi writev với stdout.

Dưới đây là một ví dụ của việc sử dụng writev từ Advanced Unix Programming

Dưới Windows bạn sẽ sử dụng WSAsend cho các chức năng tương tự.

0

Phương pháp sử dụng 4096 bigbuf sẽ chỉ sắp xếp công việc. Tôi đã thử mã này, và trong khi nó thành công chụp stdout vào bộ đệm, nó không thể sử dụng trong một trường hợp thế giới thực. Bạn không có cách nào biết được đầu ra bị bắt bao lâu, vì vậy không có cách nào để biết khi nào chấm dứt chuỗi '\ 0'. Nếu bạn cố gắng sử dụng bộ đệm bạn nhận được 4000 ký tự rác thải ra nếu bạn đã bắt thành công 96 ký tự của đầu ra stdout.

Trong đơn đăng ký của tôi, tôi đang sử dụng trình thông dịch perl trong chương trình C. Tôi không có ý tưởng bao nhiêu đầu ra sẽ được nhổ ra khỏi những gì bao giờ tài liệu được ném vào chương trình C, và do đó mã ở trên sẽ không bao giờ cho phép tôi in sạch đầu ra ra bất cứ nơi nào.

+0

Nếu bạn điền vào các bigbuf với số không đầu tiên, sau đó bạn được đảm bảo rằng chuỗi sẽ được null chấm dứt ... trừ khi byte cuối cùng của bigbuf không phải là số không. Nhưng trong trường hợp đó, bạn có thể phát hiện tràn. –

Các vấn đề liên quan