2009-06-05 14 views
16

मेरे पास कई कार्यकर्ता धागे के साथ एक सी अनुप्रयोग है। यह आवश्यक है कि ये ब्लॉक न करें, जहां कार्यकर्ता धागे को डिस्क पर फ़ाइल में लिखने की आवश्यकता है, मैंने उन्हें स्मृति में एक गोलाकार बफर में लिखा है, और उसके बाद डिस्क पर बफर लिखने के लिए एक समर्पित धागा है।स्मृति में stdout कैसे बफर करें और इसे समर्पित थ्रेड से लिखें

कार्यकर्ता धागे अब और अवरुद्ध नहीं होते हैं। समर्पित धागा कार्यकर्ता धागे को प्रभावित किए बिना डिस्क पर लिखते समय सुरक्षित रूप से ब्लॉक कर सकता है (डिस्क पर लिखते समय यह लॉक नहीं रखता है)। मेरा मेमोरी बफर पर्याप्त रूप से बड़ा होने के लिए ट्यून किया गया है कि लेखक धागा जारी रख सकता है।

यह सब बढ़िया काम करता है। मेरा सवाल है, मैं stdout के लिए कुछ समान कैसे लागू करूं?

मै मैक्रो प्रिंटफ() को मेमोरी बफर में लिखने के लिए मैक कर सकता था, लेकिन मेरे पास सभी कोड पर नियंत्रण नहीं है जो stdout को लिख सकते हैं (इनमें से कुछ तीसरे पक्ष के पुस्तकालयों में हैं)।

विचार? निकबी

+0

यहां सभी समाधान गलत हैं, क्योंकि पाइप को लिखना ब्लॉक कर सकता है। (और यदि आप उन्हें अनब्लॉकिंग सेट करते हैं, तो 'FILE' केवल एक अप्राप्य त्रुटि स्थिति में आ जाएगा यदि इसे फ़्लश करने की आवश्यकता होने पर अवरुद्ध हो जाए।) –

उत्तर

27

मुझे freopen का उपयोग करने का विचार पसंद है। आप को पाइप से 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; 
} 
+0

ऐसा लगता है जैसे यह मेरी समस्या हल करता है। मैं इसे आज़माउंगा। धन्यवाद! – NickB

+4

जब मैंने इस तरह के दृष्टिकोण की कोशिश की, तो पाइप में कुछ भी नहीं होने पर मुझे 'पढ़ा' लटका मिला। लेकिन 'लंबे झंडे = fcntl (out_pipe [0], F_GETFL जोड़ना; झंडे | = O_NONBLOCK; fcntl (out_pipe [0], F_SETFL, झंडे); 'init अनुभाग में उस समस्या को ठीक किया गया है। – aschepler

+0

बिल्कुल वही जो मैं खोज रहा था! – Marcin

4

आप freopen() का उपयोग कर फ़ाइल में stdout को "रीडायरेक्ट" कर सकते हैं।

man freopen का कहना है:

freopen() फ़ंक्शन फ़ाइल जिसका नाम है स्ट्रिंग पथ द्वारा की ओर इशारा किया और धारा इसके साथ धारा के द्वारा की ओर इशारा किया एकत्रित करती है खोलता है। मूल धारा (यदि यह मौजूद है) बंद है। मोड तर्क का प्रयोग जैसा कि fopen() फ़ंक्शन में किया जाता है। फ्रीपेन() फ़ंक्शन का प्राथमिक उपयोग मानक मानक टेक्स्ट स्ट्रीम (stderr, stdin, या stdout) से जुड़े फ़ाइल को बदलने के लिए है।

यह फ़ाइल अच्छी तरह से पाइप हो सकती है - कार्यकर्ता धागे उस पाइप को लिखेंगे और लेखक धागे सुनेगा।

+0

यह मेरी समस्या का समाधान नहीं करता है। मैं printf() कॉल बनाने वाले थ्रेड के डिस्क लेखन को स्थानांतरित करने की कोशिश कर रहा हूं। Freopen() का उपयोग करना अभी भी मेरे printf() को फ़ाइल में लिखने के लिए कॉल करेगा, यद्यपि stdout पर एक अलग फ़ाइल। क्या मेरे लिए freopen() में "फ़ाइल" निर्दिष्ट करना संभव है जो डिस्क फ़ाइल नहीं है? – NickB

+0

ज़रूर। फ़ाइल के बजाय पाइप का प्रयोग करें। – qrdl

0

आप setvbuf() या setbuf() के साथ बफरिंग कैसे काम कर सकते हैं बदल सकते हैं। यहां एक विवरण दिया गया है: http://publications.gbdirect.co.uk/c_book/chapter9/input_and_output.html

[संपादित करें]

stdout वास्तव में एक FILE* है। यदि मौजूदा कोड FILE* एस के साथ काम करता है, तो मुझे नहीं लगता कि stdout के साथ काम करने से क्या रोकता है।

2

आप अपने पूरे एप्लिकेशन को दूसरे में क्यों नहीं लपेटते? असल में, आप जो चाहते हैं वह एक स्मार्ट cat है जो stdin को stdout की प्रतिलिपि बनाता है, आवश्यकतानुसार बफरिंग करता है। फिर मानक stdin/stdout पुनर्निर्देशन का उपयोग करें। यह आपके वर्तमान एप्लिकेशन को संशोधित किए बिना किया जा सकता है।

~MSalters/# YourCurrentApp | bufcat 
8

आप जीएनयू libc साथ काम कर रहे हैं, तो आप memory streams का उपयोग हो सकता है।

+1

यह मुझे लगता है कि असली सही जवाब है। पाइप दृष्टिकोण में महंगा सिस्टम कॉल शामिल है और निश्चित रूप से ब्लॉक करने जा रहा है। –

0

एक समाधान (दोनों चीजों के लिए) writev के माध्यम से एक एकत्रित लेखन का उपयोग करना होगा।

प्रत्येक थ्रेड उदाहरण के लिए एक आईवेक बफर में sprintf सकता है और फिर Iovec पॉइंटर्स को लेखक थ्रेड में पास कर सकता है और इसे बस stdout के साथ writev को कॉल कर सकता है।

यहाँ Advanced Unix Programming

से writev उपयोग का एक उदाहरण विंडोज के अंतर्गत आप इसी तरह की सुविधा के लिए WSAsend का उपयोग होता है।

0

विधि केवल तरह होगा काम के 4096 bigbuf का उपयोग कर। मैंने इस कोड को आजमाया है, और जब यह बफर में सफलतापूर्वक स्टडआउट कैप्चर करता है, तो यह वास्तविक दुनिया के मामले में उपयोग करने योग्य नहीं है। आपके पास यह जानने का कोई तरीका नहीं है कि कैप्चर आउटपुट कितना समय है, इसलिए स्ट्रिंग '\ 0' को समाप्त करने के बारे में जानने का कोई तरीका नहीं है। यदि आप बफर का उपयोग करने का प्रयास करते हैं तो आपको 4000 वर्ण कचरा थूकना पड़ता है यदि आपने सफलतापूर्वक स्टडआउट आउटपुट के 96 वर्णों को कैप्चर किया था।

मेरे आवेदन में, मैं सी प्रोग्राम में एक पर्ल दुभाषिया का उपयोग कर रहा हूं। मुझे नहीं पता कि सी प्रोग्राम में कभी भी दस्तावेज़ को फेंकने के बारे में कितना आउटपुट जा रहा है, और इसलिए ऊपर दिया गया कोड मुझे कभी भी उस आउटपुट को कहीं भी प्रिंट करने की अनुमति नहीं देगा।

+0

यदि आप पहले ज़ीरो के साथ बिगबफ भरते हैं, तो आपको गारंटी है कि स्ट्रिंग को निरस्त कर दिया जाएगा ... जब तक कि bigbuf का अंतिम बाइट शून्य नहीं है। लेकिन उस स्थिति में, आप एक अतिप्रवाह का पता लगा सकते हैं। –