कांटा () के साथ एक कार्यक्रम कभी-कभी अपने आउटपुट को कई बार क्यों प्रिंट करता है?


50

प्रोग्राम 1 में Hello worldसिर्फ एक बार प्रिंट होता है, लेकिन जब मैं \nइसे हटाता हूं और इसे (प्रोग्राम 2) चलाता हूं , तो आउटपुट 8 बार प्रिंट हो जाता है। क्या कोई मुझे \nयहाँ का महत्व समझा सकता है और यह कैसे प्रभावित करता है fork()?

कार्यक्रम 1

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("hello world...\n");
    fork();
    fork();
    fork();
}

आउटपुट 1:

hello world... 

कार्यक्रम 2

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("hello world...");
    fork();
    fork();
    fork();
}

आउटपुट 2:

hello world... hello world...hello world...hello world...hello world...hello world...hello world...hello world...

10
फ़ाइल ( ./prog1 > prog1.out) या एक पाइप ( ./prog1 | cat) के आउटपुट के साथ प्रोग्राम 1 चलाने का प्रयास करें । अपने दिमाग को उड़ाने के लिए तैयार रहें। :
जी-मैन ने कहा कि मोनिका '

प्रासंगिक क्यू + ए इस मुद्दे के एक और प्रकार को कवर करता है: सी सिस्टम ("बैश") स्टीन को अनदेखा करता है
माइकल होमर

13
इसने कुछ करीबी वोटों को इकट्ठा किया है, इसलिए उस पर एक टिप्पणी: "यूनिक्स सी एपीआई और सिस्टम इंटरफेस" पर सवाल स्पष्ट रूप से अनुमति है । शैल स्क्रिप्ट में बफ़रिंग समस्याएँ भी एक सामान्य मुठभेड़ हैं, और fork()कुछ हद तक यूनिक्स-विशिष्ट भी हैं, इसलिए ऐसा लगेगा कि यह यूनिक्स के लिए काफी ऑन-टॉपिक है।
ilkachachu

@ilkachach वास्तव में, यदि आप उस लिंक को पढ़ते हैं, और मेटा प्रश्न पर क्लिक करते हैं, तो यह बहुत स्पष्ट रूप से बताता है कि यह बंद विषय है। सिर्फ इसलिए कि कुछ सी है, और यूनिक्स में सी है, इसे विषय नहीं बनाता है।
पैट्रिक

@ पैट्रिक, वास्तव में, मैंने किया था। और मुझे अभी भी लगता है कि यह "कारण के भीतर" खंड में फिट बैठता है, लेकिन निश्चित रूप से यह सिर्फ मेरे लिए है।
इलकाचू

जवाबों:


93

जब सी लाइब्रेरी के printf()फ़ंक्शन का उपयोग करके मानक आउटपुट पर आउटपुट किया जाता है , तो आउटपुट आमतौर पर बफर हो जाता है। जब तक आप एक नई लाइन आउटपुट, कॉल fflush(stdout)या प्रोग्राम से बाहर नहीं निकलते (तब तक कॉलिंग के माध्यम से नहीं _exit()) तब तक बफर को फ्लश नहीं किया जाता है । मानक आउटपुट स्ट्रीम डिफ़ॉल्ट लाइन-बफ़र द्वारा इस तरह से होती है जब यह TTY से जुड़ा होता है।

जब आप "प्रोग्राम 2" में प्रक्रिया का कांटा लगाते हैं, तो बच्चे की प्रक्रिया माता-पिता की प्रक्रिया के हर हिस्से को विरासत में मिलती है, जिसमें अप्रकाशित आउटपुट बफर भी शामिल है। यह प्रभावी रूप से प्रत्येक बच्चे की प्रक्रिया में अप्रभावित बफर की प्रतिलिपि बनाता है।

जब प्रक्रिया समाप्त हो जाती है, तो बफ़र्स को प्रवाहित कर दिया जाता है। आप कुल आठ प्रक्रियाओं (मूल प्रक्रिया सहित) की शुरुआत करते हैं, और प्रत्येक व्यक्तिगत प्रक्रिया के समापन पर अप्रभावित बफर को फ्लश किया जाएगा।

यह आठ है क्योंकि प्रत्येक में fork()आपको fork()(आपके बिना शर्त होने से पहले ) दो बार प्रक्रियाएँ मिलती हैं, और आपके पास इनमें से तीन (2 3 = 8) हैं।


14
संबंधित: आप समाप्त कर सकते हैं mainके साथ _exit(0)सिर्फ निस्तब्धता बफ़र्स के बिना एक निकास प्रणाली कॉल करते हैं, और फिर इसे एक नई पंक्ति के बिना शून्य बार प्रिंट किया जाएगा करने के लिए। ( एग्ज़िट का सिस्कल इम्प्लीमेंटेशन () और कैसे आना _exit (0) (syscall से बाहर आना) मुझे किसी भी तरह की स्टडआउट सामग्री प्राप्त करने से रोकता है? )। या आप प्रोग्राम 1 में पाइप कर सकते हैं catया इसे एक फ़ाइल पर रीडायरेक्ट कर सकते हैं और इसे 8 बार प्रिंट कर सकते हैं। (जब यह TTY नहीं है तो डिफ़ॉल्ट रूप से stdout पूर्ण-बफ़र्ड होता है)। या fflush(stdout)2 से पहले नो-न्यूलाइन केस में जोड़ें fork()...
पीटर कॉर्डेस

17

यह किसी भी तरह से कांटा को प्रभावित नहीं करता है।

पहले मामले में, आप कुछ भी नहीं लिखने के साथ 8 प्रक्रियाओं के साथ समाप्त होते हैं, क्योंकि आउटपुट बफर पहले से ही खाली हो गया था (के कारण \n)।

दूसरे मामले में आपके पास अभी भी 8 प्रक्रियाएं हैं, हर एक बफर में "हैलो वर्ल्ड ..." है और बफर प्रक्रिया के अंत में लिखा गया है।


12

@ कुसलानंद ने बताया कि आउटपुट क्यों दोहराया जाता है । यदि आप उत्सुक हैं कि आउटपुट 8 बार क्यों दोहराया जाता है और न केवल 4 बार (आधार प्रोग्राम + 3 कांटे):

int main()
{
    printf("hello world...");
    fork(); // here it creates a copy of itself --> 2 instances
    fork(); // each of the 2 instances creates another copy of itself --> 4 instances
    fork(); // each of the 4 instances creates another copy of itself --> 8 instances
}

2
यह कांटा का मूल है
Prvt_Yadav

3
@Debian_yadav शायद तभी स्पष्ट हो जब आप इसके निहितार्थ से परिचित हों। उदाहरण के लिए, स्टीडियो बफ़र्स फ्लशिंग की तरह ।
रोआमा

2
@Debian_yadav: en.wikipedia.org/wiki/False_consensus_effect - अगर हर कोई सब कुछ जानता है तो हमें सवाल क्यों पूछना चाहिए?
होनज़ा ज़िदक

8
@Debian_yadav मैं ओपी के दिमाग को नहीं पढ़ सकता इसलिए मुझे नहीं पता। वैसे भी, स्टैटेक्सचेंज एक ऐसी जगह है जहाँ अन्य लोग भी ज्ञान की खोज करते हैं और मुझे लगता है कि मेरा जवाब कुलसंडा के अच्छे उत्तर के लिए उपयोगी हो सकता है। मेरा जवाब कहते हैं edc65 के एक जो सिर्फ दोहराता Kulasandra उसे पहले 2 घंटे क्या कहा की तुलना में, कुछ (मूल लेकिन उपयोगी)।
होनज़ा ज़िदेक

2
यह उत्तर के लिए केवल एक छोटी टिप्पणी है, वास्तविक उत्तर नहीं है। सवाल "कई बार" के बारे में पूछता है कि यह बिल्कुल 8. क्यों नहीं है
पाइप करें

3

यहां महत्वपूर्ण पृष्ठभूमि यह है कि डिफ़ॉल्ट सेटअप के रूप में मानक द्वारा लाइन बफ़र किया stdoutजाना आवश्यक है ।

यह \nआउटपुट को फ्लश करने का कारण बनता है ।

चूंकि दूसरे उदाहरण में न्यूलाइन नहीं है, इसलिए आउटपुट को फ्लश नहीं किया जाता है और जैसा fork()कि पूरी प्रक्रिया को कॉपी करता है, यह stdoutबफर की स्थिति को भी कॉपी करता है ।

अब, fork()आपके उदाहरण में ये कॉल कुल मिलाकर 8 प्रक्रियाएँ बनाते हैं - ये सभी stdoutबफर की स्थिति की एक प्रति है ।

परिभाषा के अनुसार, ये सभी प्रक्रियाएं कॉल exit()करते समय से लौटती हैं main()और exit()कॉल के fflush()बाद fclose()सभी सक्रिय stdio धाराओं पर। इसमें शामिल है stdoutऔर परिणामस्वरूप, आप एक ही सामग्री को आठ बार देखते हैं।

कॉल करने fflush()से पहले लंबित आउटपुट के साथ सभी धाराओं को कॉल करना fork()या कांटेक्टेड बच्चे को स्पष्ट रूप से कॉल करने देना अच्छा है _exit()जो केवल प्रक्रिया को स्टीडियो धाराओं को प्रवाहित किए बिना बाहर निकालता है।

ध्यान दें कि कॉलिंग exec()stdio बफ़र्स को फ्लश नहीं करता है, इसलिए यदि आप ( fork()कॉल करने के बाद ) exec()और (यदि वह विफल रहता है) कॉल तो stdio बफ़र्स की परवाह नहीं करना ठीक है _exit()

BTW: यह समझने के लिए कि गलत बफ़रिंग का कारण हो सकता है, यहाँ लिनक्स में एक पूर्व बग है जिसे हाल ही में ठीक किया गया है:

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

नीचे टिप्पणी देखें, यह अब तय हो गया है।

इस लिनक्स समस्या के आसपास काम करने के लिए मैं यही करता हूं:

    /* 
     * Linux comes with a broken libc that makes "stderr" buffered even 
     * though POSIX requires "stderr" to be never "fully buffered". 
     * As a result, we would get garbled output once our fork()d child 
     * calls exit(). We work around the Linux bug by calling fflush() 
     * before fork()ing. 
     */ 
    fflush(stderr); 

यह कोड अन्य प्लेटफार्मों पर नुकसान नहीं पहुंचाता है क्योंकि fflush()एक धारा पर कॉल करने से जो सिर्फ फ्लश किया गया था वह एक नॉओप है।


2
नहीं, स्टडआउट को पूरी तरह से बफर होना आवश्यक है जब तक कि यह एक इंटरैक्टिव डिवाइस नहीं है जिस स्थिति में यह अनिर्दिष्ट है, लेकिन व्यवहार में यह तब लाइन-बफ़र्ड है। पूरी तरह से बफरिंग न करने के लिए stderr की आवश्यकता होती है। देखें pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/...
स्टीफन Chazelas

setbuf()डेबियन पर ( मैन 7 ओन पर एक जैसा दिखता है ) के लिए मेरा मैन पेज , कहता है कि "मानक त्रुटि स्ट्रीम स्टेडर हमेशा डिफ़ॉल्ट रूप से अप्रभावित रहता है।" और एक साधारण परीक्षण उस तरह से कार्य करता है, भले ही आउटपुट किसी फ़ाइल, पाइप या टर्मिनल पर जाता हो। क्या आपके पास कोई संदर्भ है कि C लाइब्रेरी का कौन सा संस्करण अन्यथा करेगा?
इलकाचू

4
लिनक्स एक कर्नेल है, stdio बफरिंग एक यूजरलैंड फीचर है, कर्नेल वहां शामिल नहीं है। लिनक्स कर्नेल के लिए बहुत सारे libc कार्यान्वयन उपलब्ध हैं, सर्वर / वर्कस्टेशन-टाइप सिस्टम में सबसे आम है GNU कार्यान्वयन, जिसके साथ stdout पूर्ण-बफ़र्ड होता है (यदि tty हो तो लाइन बफ़र की जाती है), और stderr असंबद्ध होता है।
स्टीफन चेज़लस

1
@ सामान्य रूप से, केवल परीक्षण मैंने चलाया: paste.dy.fi/xk4 । मुझे वही परिणाम बुरी तरह से आउट-ऑफ-डेट सिस्टम के साथ भी मिला।
इलकाचू

1
@ सच में यह सच नहीं है। उदाहरण के लिए, मैं अल्पाइन लिनक्स का उपयोग करके यह टिप्पणी लिख रहा हूं, जो इसके बजाय मसल का उपयोग करता है।
निएडजेजकोब
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.