2011-11-28 26 views
27

करता है, इसलिए मैंने fork() का उपयोग किया है और मुझे पता है कि यह क्या करता है। शुरुआत के रूप में मैं इससे बहुत डरता था (और मैं अभी भी इसे पूरी तरह से समझ नहीं पा रहा हूं)। fork() का सामान्य विवरण जो आप ऑनलाइन पा सकते हैं, यह है कि यह वर्तमान प्रक्रिया की प्रतिलिपि बनाता है और अलग-अलग पीआईडी, पैरेंट पीआईडी ​​असाइन करता है और प्रक्रिया में अलग पता स्थान होगा। सभी अच्छे हैं, हालांकि, इस कार्यक्षमता विवरण को देखते हुए एक शुरुआती आश्चर्यचकित होगा "यह कार्य इतना महत्वपूर्ण क्यों है ... मैं अपनी प्रक्रिया की प्रतिलिपि क्यों लेना चाहूंगा?"। तो मैंने आश्चर्यचकित किया और अंततः मुझे पता चला कि आप execve() परिवार के माध्यम से अपनी वर्तमान प्रक्रिया के भीतर से अन्य प्रक्रियाओं को कैसे कॉल कर सकते हैं।क्यों फोर्क()

जो मुझे अभी भी समझ में नहीं आता है, आपको ऐसा क्यों करना है? सबसे तार्किक बात एक समारोह है कि आप जो एक नई प्रक्रिया() अंडे जाएगा और मुख्य की शुरुआत में उसका चलना आरंभ और नई पीआईडी ​​लौट

तरह
create_process("executable_path+name",params..., more params); 

कॉल कर सकते हैं करने के लिए किया जाएगा।

मुझे परेशान करता है कि यह महसूस कर रहा है कि कांटा/निष्पादन समाधान संभावित रूप से अनियंत्रित काम कर रहा है। क्या होगा यदि मेरी प्रक्रिया बहुत मेमोरी का उपयोग कर रही है? क्या कर्नेल मेरी पेज टेबल और इस तरह की प्रतिलिपि बनाता है। मुझे यकीन है कि यह वास्तव में वास्तविक स्मृति आवंटित नहीं करता है जब तक कि मैंने इसे छुआ नहीं है। इसके अलावा, अगर मेरे पास धागे हैं तो क्या होगा? यह मुझे लगता है कि यह बहुत गन्दा है।

फोर्क के बारे में लगभग सभी विवरण बताते हैं कि यह सिर्फ प्रक्रिया की प्रतिलिपि बनाता है और fork() कॉल के बाद नई प्रक्रिया चलने लगती है। यह वास्तव में होता है लेकिन यह इस तरह क्यों होता है और क्यों नई प्रक्रियाओं को विकसित करने का एकमात्र तरीका फोर्क/निष्पादित करता है और आपके वर्तमान से एक नई प्रक्रिया बनाने का सबसे सामान्य यूनिक्स तरीका क्या है? क्या प्रक्रिया को बढ़ाने के लिए कोई और अधिक प्रभावी तरीका है? ** जो अधिक स्मृति की प्रतिलिपि बनाने की आवश्यकता नहीं होगी।

This धागा एक ही मुद्दे के बारे में बात करती है, लेकिन मैं इसे काफी संतोषजनक नहीं मिला:

धन्यवाद।

+0

कृपया http://unix.stackexchange.com/ या http://superuser.com/ – rlemon

+7

पर पोस्ट करें क्यों यूनिक्स? यह एक प्रोग्रामिंग प्रश्न है जो ढेर ओवरफ्लो से संबंधित है। – Petr

+0

vfork के मैन पेज को पढ़ने के बाद http://cm.bell-labs.com/who/dmr/hist.html – ninjalj

उत्तर

0

अच्छी तरह से पेजिंग/वर्चुअल मेमोरी के मामले में ऐसी तकनीकें हैं जिनमें फोर्क() हमेशा एक प्रक्रिया के पूरे पता स्थान की प्रतिलिपि नहीं बनाती है। लिखने पर प्रतिलिपि है जहां एक फोर्कड प्रक्रिया को उसके माता-पिता के समान पता स्थान मिलता है और फिर केवल उस स्थान के एक हिस्से की प्रतिलिपि बनाता है जो बदल जाता है (किसी भी प्रक्रिया से)।

2

spawn और दोस्तों को देखें।

+2

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

+0

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

10

याद रखें कि fork का आविष्कार यूनिक्स (शायद पहले) में बहुत जल्दी आविष्कार किया गया था जो मशीनों पर आज हास्यास्पद रूप से छोटा लगता है (उदाहरण के लिए स्मृति के 64 के बाइट्स)।

और यह मूलभूत तंत्र प्रदान करने के समग्र (मूल) दर्शन के साथ चरण में अधिक है, नीतियों को नहीं, सबसे प्राथमिक संभव कार्यों के माध्यम से।

fork बस एक नई प्रक्रिया बनाता है, और सोचने का सबसे आसान तरीका है कि वर्तमान प्रक्रिया को क्लोन करना है। तो fork अर्थशास्त्र बहुत स्वाभाविक है, और यह सबसे सरल machanism संभव है।

अन्य सिस्टम कॉल (execve) एक नई निष्पादन आदि लोड हो रहा है, के प्रभारी रहे हैं ..

उन्हें अलग (और यह भी pipe और dup2 syscalls उपलब्ध कराने के) बहुत लचीलापन देता है।

और वर्तमान सिस्टम पर, fork बहुत कुशलता से लागू किया गया है (लेखन पेजिनेशन तकनीकों पर आलसी प्रतिलिपि के माध्यम से)। यह ज्ञात है कि fork तंत्र यूनिक्स प्रक्रिया निर्माण को तेज़ बनाता है (उदा। विंडोज या वीएक्स/वीएमएस पर तेज, जिसमें सिस्टम आपके द्वारा प्रस्तावित प्रक्रियाओं के समान प्रक्रियाओं को बनाने के लिए कॉल करता है)।

vfork सिस्कल भी है, जिसे मैं उपयोग करने से परेशान नहीं करता हूं।

और posix_spawn एपीआई ज्यादा fork या अकेले execve तुलना में अधिक जटिल है, इसलिए दिखाता है कि fork सरल है ...

+0

तो, मैंने स्पॉन के बारे में सुना है, लेकिन मुझे आश्चर्य है, उदाहरण के लिए, जो एक नई प्रक्रिया प्रक्रिया बनाता है, बड़े पैमाने पर लिनक्स अनुप्रयोगों का उपयोग करता है (जैसे गिंप, ओपनऑफिस, gnome, आदि)। मुझे लगता है कि उनमें से कुछ को ऐसा करने की आवश्यकता होगी। – user1068779

+0

जीटीके प्रदान करता है (ग्लिब लाइब्रेरी में) 'फोर्क' सिस्कल के ऊपर कॉल करता है, जैसे कि http://developer.gnome.org/glib/unstable/glib-Spawning-Processes.html –

+0

मुझे लगता है कि अंत में यह केवल स्पष्ट है जवाब जो सिर्फ कहता है "याद रखें कि फोर्क का आविष्कार बहुत जल्दी हुआ था"। किसी ने भी पुष्टि नहीं की, मुझे विश्वास है कि नया कार्य अधिक प्रभावी होगा, जो कि 'फोर्क() 'करता है, अतिरिक्त मेमोरी/विशेषता क्लोनिंग को छोड़कर, एकमात्र अलग प्रक्रिया शुरू करने के उद्देश्य से क्लोनिंग को छोड़कर जो कुछ भी साझा नहीं करता है अपने माता पिता के साथ। – Petr

2

जब fork मौजूदा प्रक्रिया को कॉपी करके एक नई प्रक्रिया बनाता है, जिसे प्रदर्शन करती एक कॉपी-ऑन लिखना। इसका मतलब यह है कि नई प्रक्रिया की याददाश्त माता-पिता प्रक्रिया के साथ साझा की जाती है जब तक कि यह परिवर्तित न हो जाए। जब स्मृति बदल जाती है, तो यह सुनिश्चित करने के लिए स्मृति की प्रतिलिपि बनाई जाती है कि प्रत्येक प्रक्रिया में स्मृति की अपनी वैध प्रति होती है। execvefork आईएनजी के ठीक बाद, स्मृति की कोई प्रति नहीं है, क्योंकि नई प्रक्रिया सिर्फ एक नई निष्पादन योग्य लोड करती है, और इस प्रकार एक नई मेमोरी स्पेस होती है।

सवाल यह है कि यह क्यों किया जाता है, मुझे निश्चित रूप से पता नहीं है, लेकिन ऐसा लगता है कि यह यूनिक्स-मार्ग का हिस्सा है - एक चीज अच्छी तरह से करें। एक ऐसा कार्य करने के बजाय जो एक नई प्रक्रिया बनाता है और एक नया निष्पादन योग्य लोड करता है, ऑपरेशन दो कार्यों में विभाजित होता है। यह डेवलपर अधिकतम लचीलापन देता है। यद्यपि मैंने अपने आप पर अभी तक फ़ंक्शन का उपयोग नहीं किया है ...

+0

यह एमओयू द्वारा गायब पृष्ठों को टैग करके हार्डवेयर में किया जाता है। विंडोज एक नई प्रक्रिया शुरू करने के लिए एक ही तंत्र का उपयोग करता है। सिस्टम कॉल अंतर्निहित कांटा (क्लोन) अंतर्निहित CreateProcess (ZwCreateProcess) के सिस्टम कॉल के समान है, और आप वास्तव में ZwCreateProcess के शीर्ष पर कांटा लागू कर सकते हैं। –

+0

http://doxygen.scilab.org/5.4/d0/d8f/forkWindows_8c_source.html –

1

बहुत कम स्मृति आवंटन के साथ फोर्क() को कार्यान्वित करना संभव है, मानते हैं कि अंतर्निहित कार्यान्वयन एक कॉपी-ऑन-राइट एड्रेसिंग सिस्टम का उपयोग करता है। उस अनुकूलन के साथ create_process फ़ंक्शन को कार्यान्वित करना असंभव है।

5

"कांटा()" एक शानदार नवाचार था जिसने एक एकल एपीआई के साथ समस्याओं की पूरी कक्षा हल की। इसका आविष्कार उस समय किया गया था जब मल्टीप्रोसेसिंग आम नहीं थी (और इससे पहले कि आप मल्टीप्रोसेसिंग के प्रकार और मैं आज बीस साल तक उपयोग करता हूं)।

+0

एर, 1 9 50 के दशक से वास्तव में मल्टीप्रोसेसिंग चल रहा था। – EJP

+0

शानदार? मैं एक बेवकूफ कहूंगा (तर्कसंगत) एक नई प्रक्रिया को बढ़ाने के एक विशेष छोटे सबसेट को हल करता हूं - मौजूदा क्लोन करें। ज्यादातर मामलों में आपको केवल आपके लिए कुछ छोटा काम करने के लिए एक छोटी सहायक प्रक्रिया शुरू करने की आवश्यकता है और आपको जो कुछ मिला है वह 'कांटा' है? आउच! तो लंगड़ा, इसे कभी पसंद नहीं आया। क्लोनिंग कई मामलों में समझ में आता है, लेकिन यहां नहीं, मेरा विश्वास करो। – Sergey

0

फोर्क का उपयोग करने का मुख्य कारण निष्पादन गति है।

यदि आपने सुझाव दिया है कि आपने पैरामीटर के एक सेट के साथ प्रक्रिया की एक नई प्रतिलिपि शुरू की है तो नई प्रक्रिया को उन पैरामीटरों को पार्स करने की आवश्यकता होगी और मूल प्रक्रिया के अधिकांश प्रोसेसिंग को दोहराया जाएगा। "फोर्क()" के साथ पैरेंट प्रक्रियाओं की एक पूरी प्रतिलिपि बच्चे के लिए तत्काल उपलब्ध है, जैसा कि यह होना चाहिए और स्वरूपित किया जाना चाहिए।

इसके अलावा अधिकांश मामलों में कार्यक्रम ".so" या ".dll" होगा, इसलिए निष्पादन योग्य निर्देशों की प्रतिलिपि नहीं बनाई जाएगी केवल ढेर और ढेर भंडारण की प्रतिलिपि बनाई जाएगी।

2

इसलिए जैसा कि अन्य ने कहा, fork बहुत तेज़ होने के लिए लागू किया गया है ताकि कोई समस्या न हो। लेकिन create_process() जैसे फ़ंक्शन क्यों नहीं? जवाब है: लचीलापन के लिए सादगी। सभी यूनिक्स में सिस्टम कॉल केवल एक चीज करने के लिए प्रोग्राम किए जाते हैं। create_process जैसे फ़ंक्शन दो चीजें करेंगे: एक प्रक्रिया बनाएं और इसमें बाइनरी लोड करें।

जब भी आप चीजों को समानांतर करने का प्रयास करते हैं तो आप थ्रेड का उपयोग कर सकते हैं - या fork() के साथ खोले गए प्रक्रियाएं। ज्यादातर मामलों में आप प्रक्रियाओं को fork() के माध्यम से खोलते हैं और फिर इन प्रक्रियाओं के बीच संचार और सिंक्रनाइज़ करने के लिए आईपीसी-तंत्र का उपयोग करते हैं। कुछ आईपीसी वैश्विक अंतरिक्ष में चर होने पर जोर देते हैं।

पाइप के साथ उदाहरण:

  • पाइप
  • कांटा एक बच्चे जो पाइप संभाल
  • बच्चे इनपुट पक्ष
  • माता पिता बंद कर देता है उत्पादन पक्ष
बंद कर देता है विरासत में बनाया जा रहा है

fork() के बिना असंभव ...

एक और महत्वपूर्ण तथ्य यह है कि पूरे यूनिक्स एपीआई में केवल कुछ ही कार्य हैं। प्रत्येक प्रोग्रामर आसानी से इस्तेमाल किए गए कार्यों पर याद कर सकता है। लेकिन विंडोज एपीआई देखें: हजारों फ़ंक्शन से अधिक कोई भी कभी याद नहीं रख सकता है। लचीलापन

+1

जबकि मैं आपसे सहमत हूं कि कांटा() ऐसी चीजें कर सकता है जो "create_process()" मैं दृढ़ता से असहमत नहीं हो सकता था कि यह भी दिया गया कि फोर्क() को बहुत तेजी से लागू किया गया था, यह कभी भी एक फ़ंक्शन से तेज़ी से कर सकता है जो वास्तव में करेगा मेमोरी प्रतिलिपि को छोड़कर वही फोर्क() करता है। वह हमेशा सीपीयू निर्देशों का एक समूह बचाएगा, इस प्रकार यह बहुत तेज़ होगा। – Petr

+0

@ पीटर: एक नई प्रक्रिया लोड करने से अधिकतर 'फोर्क() 'ओवरहेड तुलनात्मक रूप से महत्वहीन होता है। – ninjalj

+0

क्लोनिंग एमएमयू द्वारा पृष्ठों को कॉपी-ऑन-राइट टैग करके किया जाता है। यह कोई सीपीयू चक्र नहीं खाता है।वास्तव में, यूनिक्स और लिनक्स पर फोर्क को लागू करने के लिए प्रयुक्त उसी सिस्टम कॉल के साथ स्पॉन्गिंग थ्रेड किया जाता है, और फोर्किंग में स्पॉन्गिंग थ्रेड की तुलना में अधिक ऊपरी नहीं होता है। यह कम ज्ञात है कि विंडोज भी फोर्किंग द्वारा एक नई प्रक्रिया शुरू करता है, हालांकि इसे ZwCreateProcess कहा जाता है और ntdll.dll में छिपा हुआ है। CreateProcess बनाम कांटा में ऊपरी भाग खाली होने और खाली प्रक्रिया शुरू करने के लिए क्लोन को फिर से शुरू करने से आता है। –

1

के लिए सादगी तो, अपने मुख्य चिंता का विषय है:

तो योग करने के लिए और फिर यह कहना कांटा() अनावश्यक स्मृति नकल की ओर जाता है।

उत्तर है: नहीं, कोई स्मृति अपशिष्ट नहीं है। संक्षेप में, कांटा() पैदा हुआ था जब स्मृति बहुत सीमित संसाधन था, इसलिए कोई भी इस तरह बर्बाद करने के बारे में भी सोचता नहीं था।

हालांकि प्रत्येक प्रक्रिया का अपना पता स्थान होता है, भौतिक स्मृति पृष्ठ और प्रक्रिया के वर्चुअल मेमोरी पेज के बीच कोई भी एक मैपिंग नहीं होता है। इसके बजाए, भौतिक स्मृति का एक पृष्ठ कई वर्चुअल पृष्ठों (मैप के लिए सीपीयू टीएलबी के लिए खोज) के लिए मैप किया जा सकता है।

तो जब आप फोर्क() के साथ नई प्रक्रिया बनाते हैं, तो उनके वर्चुअल एड्रेस स्पेस को उसी भौतिक मेमोरी पेज पर मैप किया जाता है। कोई स्मृति प्रति आवश्यक नहीं है। इसका यह भी अर्थ है कि प्रयुक्त पुस्तकालयों की कोई डुप्लिकेट नहीं है क्योंकि उनके कोड अनुभाग केवल पढ़ने के लिए चिह्नित हैं।

वास्तविक स्मृति प्रतिलिपि तब होती है जब माता-पिता या बाल प्रक्रिया कुछ स्मृति पृष्ठ को संशोधित करती है। उस स्थिति में नया भौतिक स्मृति पृष्ठ आवंटित किया जाता है और पृष्ठ को संशोधित करने वाली प्रक्रिया के आभासी पता स्थान पर मैप किया जाता है।

+0

सीपीयू कचरे के बारे में क्या? क्या यह ऑपरेशन नहीं है जब प्रक्रिया के कुछ गुणों को नई प्रक्रिया में कॉपी किया गया है, केवल अतिरिक्त निर्देशों का एक समूह जिसे निष्पादित करने की आवश्यकता नहीं है, क्योंकि मुझे पता है कि मैं उन्हें किसी भी तरह से त्याग दूंगा? मेरा मतलब है कांटा() प्रक्रिया की एक प्रति बनाता है। यह कई विशेषताओं की प्रतिलिपि बनाता है जो बाद में अतिसंवेदनशील होते हैं और जो कुछ सीपीयू का उपभोग करते हैं जिन्हें उपभोग करने की आवश्यकता नहीं होती है, या नहीं? – Petr

+0

इतने सारे गुण ओवरराइट नहीं होंगे। इस तरह के एक ओवरहेड –

1

यह एक अच्छा सवाल है। मुझे यह देखने के लिए स्रोत में थोड़ा सा खोदना पड़ा कि वास्तव में क्या हो रहा था।

कांटा() कॉलिंग प्रक्रिया को डुप्लिकेट करके एक नई प्रक्रिया बनाता है।

लिनक्स के तहत, फोर्क() को कॉपी-ऑन-राइट पेजों का उपयोग करके कार्यान्वित किया जाता है, इसलिए यह केवल एक ही जुर्माना है जो माता-पिता के पेज टेबल को डुप्लिकेट करने के लिए आवश्यक समय और स्मृति है, और बच्चे के लिए एक अद्वितीय कार्य संरचना बनाने के लिए ।

नई प्रक्रिया जिसे बच्चे के रूप में जाना जाता है, कॉलिंग प्रक्रिया का सटीक डुप्लिकेट है (जिसे माता-पिता के रूप में जाना जाता है)। इस पीआईडी ​​ से मेल नहीं खाता

  • बच्चे की अपनी अनूठी प्रक्रिया ID है, और किसी भी मौजूदा प्रक्रिया समूह की आईडी: को छोड़कर के लिए।
  • बच्चे की मूल प्रक्रिया आईडी माता-पिता की प्रक्रिया आईडी के समान है।
  • बच्चा अपने माता-पिता के स्मृति ताले का वारिस नहीं करता है।
  • प्रक्रिया संसाधन उपयोग और सीपीयू टाइम काउंटर बच्चे में शून्य पर रीसेट कर दिए जाते हैं।
  • बच्चे के लंबित संकेतों का सेट प्रारंभ में खाली है।
  • बच्चे को अपने माता-पिता से सेमफोर समायोजन का वारिस नहीं मिलता है।
  • बच्चे को अपने माता-पिता से रिकॉर्ड ताले का वारिस नहीं मिलता है।
  • बच्चा अपने माता-पिता से टाइमर का वारिस नहीं करता है।
  • बच्चे को अपने माता-पिता से बकाया एसिंक्रोनस I/O ऑपरेशंस का वारिस नहीं मिलता है, और न ही यह अपने माता-पिता से किसी भी एसिंक्रोनस I/O संदर्भों का उत्तराधिकारी है।

निष्कर्ष:

कांटा के मुख्य उद्देश्य माता-पिता का अनूठा कार्य संरचना को प्रभावित किए बिना छोटे उप-कार्य में माता-पिता की प्रक्रिया के कार्यों को विभाजित करने के लिए है। यही कारण है कि कांटा मौजूदा प्रक्रिया क्लोन।

सूत्रों का कहना है:

http://www.quora.com/Linux-Kernel/After-a-fork-where-exactly-does-the-childs-execution-start http://learnlinuxconcepts.blogspot.in/2014/03/process-management.html

+1

+1 को खोदने के लिए स्वीकार्य है कि फोर्क() कैसे काम करता है। हालांकि मौजूदा क्लोनिंग की तुलना में एक नई प्रक्रिया शुरू करने के लिए वास्तव में कोई बेहतर तरीका नहीं है? मुझे बस इसमें कोई बिंदु नहीं दिख रहा है। यदि आप नई, अलग प्रक्रिया शुरू करना चाहते हैं, तो आप पहले मौजूदा प्रक्रिया को क्यों क्लोन करना चाहते हैं? – Petr

+0

मैंने आपकी टिप्पणी के जवाब में मेरे उत्तर में परिवर्तन किए हैं। –

+0

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

17

इस ऐतिहासिक कारणों की वजह से है।

  • आवश्यक प्रारंभ करते हैं (उद्घाटन stdin/stdout): जैसा कि https://www.bell-labs.com/usr/dmr/www/hist.html में बताया गया है, यूनिक्स बहुत जल्दी fork() है और न ही exec*() न, और जिस तरह से खोल निष्पादित आदेशों था की क्या ज़रूरत थी।
  • एक कमांड लाइन पढ़ें।
  • कमांड खोलें, कुछ बूटस्ट्रैप कोड लोड करें और उस पर कूदें।
  • बूटस्ट्रैप कोड खोले गए आदेश को पढ़ता है, (खोल की स्मृति को ओवरराइट करना), और उस पर कूद गया। एक
  • एक बार आदेश समाप्त हो गया है, यह exit() है, जो तब खोल (आदेश की स्मृति अधिलेखित) को फिर से लोड, और यह करने के लिए कूद, वापस जा रहा द्वारा काम कदम 1.

वहां से कहेंगे, fork() था आसान जोड़ (27 असेंबली लाइनें), शेष कोड का पुन: उपयोग करना।

यूनिक्स विकास के उस चरण में, एक कमांड को क्रियान्वित बने:

  • एक कमांड लाइन पढ़ें।
  • fork() एक बाल प्रक्रिया, और इसके लिए प्रतीक्षा करें (इसे एक संदेश भेजकर)।
  • बाल प्रक्रिया ने आदेश लोड किया (बच्चे की स्मृति को ओवरराइट करना), और उस पर कूद गया।
  • एक बार आदेश समाप्त होने के बाद, यह exit() पर कॉल करेगा, जो अब आसान था। यह सिर्फ अपनी प्रक्रिया प्रविष्टि को साफ कर दिया, और नियंत्रण छोड़ दिया।

मूल रूप से, fork() ने लिखित पर प्रतिलिपि नहीं की। चूंकि इसने fork() महंगा बनाया है, और fork() को अक्सर नई प्रक्रियाओं को बढ़ाने के लिए उपयोग किया जाता था (अक्सर exec*() द्वारा तुरंत पीछा किया गया था), fork() का एक अनुकूलित संस्करण दिखाई दिया: vfork() जो माता-पिता और बच्चे के बीच स्मृति साझा करता है। vfork() के उन कार्यान्वयन में माता-पिता को exec*() 'ed या _exit()' एड तक निलंबित कर दिया जाएगा, इस प्रकार माता-पिता की स्मृति को छोड़ दिया जाएगा।बाद में, fork() को लिखने पर कॉपी करने के लिए अनुकूलित किया गया था, केवल स्मृति पृष्ठों की प्रतियां बनाते समय जब वे माता-पिता और बच्चे के बीच भिन्न होना शुरू करते थे। vfork() ने बाद में बंदरगाहों में नवीनीकृत रुचि देखी! एमएमयू सिस्टम (उदाहरण: यदि आपके पास एडीएसएल राउटर है, तो यह शायद एमएमयू एमआईपीएस सीपीयू पर लिनक्स चलाता है), जो गाय अनुकूलन नहीं कर सका, और इसके अलावा fork() 'एड का समर्थन नहीं कर सका कुशलता से प्रक्रिया करता है।

fork() में अक्षमताओं के अन्य स्रोत है कि यह शुरू में पता स्थान (और पृष्ठ सारणी) माता पिता की है, जो विशाल कार्यक्रमों से कम कार्यक्रमों को चलाने अपेक्षाकृत धीमी गति से कर सकते हैं, या ओएस से इनकार एक fork() सोच वहाँ हो सकता है कर सकते हैं डुप्लिकेट है इसके लिए पर्याप्त स्मृति नहीं है (इस पर काम करने के लिए, आप अपनी स्वैप स्पेस बढ़ा सकते हैं, या अपनी ओएस की मेमोरी ओवरकॉमिट सेटिंग्स बदल सकते हैं)। एक उपेक्षा के रूप में, जावा 7 इन समस्याओं से बचने के लिए vfork()/posix_spawn() का उपयोग करता है।

दूसरी ओर, fork() एक ही प्रक्रिया के कई उदाहरणों को बहुत ही कुशल बनाता है: उदाहरण: एक वेब सर्वर में विभिन्न ग्राहकों की सेवा करने वाली कई समान प्रक्रियाएं हो सकती हैं। अन्य प्लेटफार्म धागे का पक्ष लेते हैं, क्योंकि एक अलग प्रक्रिया को बढ़ाने की लागत वर्तमान प्रक्रिया को डुप्लिकेट करने की लागत से काफी बड़ी है, जो कि एक नए धागे को बढ़ाने की तुलना में थोड़ा बड़ा हो सकता है। जो दुर्भाग्यपूर्ण है, क्योंकि साझा-सब कुछ धागे त्रुटियों के लिए चुंबक हैं।

+0

सभी उत्तरों में, यह केवल एक जैसा दिखता है जो यहां होना चाहिए: ^) –

+0

लिंक मर चुका है। पेपर की तलाश में किसी को भी: शीर्षक: "यूनिक्स टाइम-शेयरिंग सिस्टम का विकास" लेखक: "डेनिस एम। रिची" – Sidervs

0

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

यदि आप समांतर कंप्यूटिंग करना चाहते हैं, तो आपकी प्रक्रियाएं लूप के ठीक ऊपर कई क्लोन में विभाजित हो सकती हैं। प्रत्येक क्लोन गणना के सबसेट करता है जबकि माता-पिता उन्हें पूरा करने के लिए इंतजार कर रहे हैं। ऑपरेटिंग सिस्टम सुनिश्चित करता है कि वे समानांतर में चल सकते हैं। विंडोज़ में आप उदा। समान अभिव्यक्ति प्राप्त करने के लिए ओपनएमपी का उपयोग करने की आवश्यकता है।

यदि आपको फ़ाइल से पढ़ने या लिखने की ज़रूरत है, लेकिन प्रतीक्षा नहीं कर सकता है, तो आप केवल अपने फोर्क और आपके क्लोन को अपने मूल कार्य को जारी रखते समय i/o कर सकते हैं। विंडोज़ पर आप कई स्थितियों में स्प्रेडिंग धागे या ओवरलैप्ड i/o का उपयोग कर सकते हैं जहां यूनिक्स में एक साधारण कांटा किया जाएगा। विशेष रूप से, प्रक्रियाओं में थ्रेड के समान स्केबिलिटी समस्या नहीं होती है। यह 32 बिट सिस्टम पर विशेष रूप से सच है। ओवरलैप किए गए I/o की जटिलताओं से निपटने के लिए बस फोर्किंग अधिक समृद्ध है। जबकि प्रक्रियाओं की अपनी मेमोरी स्पेस होती है, थ्रेड उसी में रहते हैं, और इस प्रकार 32 बिट प्रक्रिया में डालने के लिए आपको कितने धागे पर विचार करना चाहिए इसकी एक सीमा है। फोर्क के साथ 32 बिट सर्वर ऐप बनाना बहुत आसान है, जबकि थ्रेड के साथ 32 बिट सर्वर ऐप बनाना एक दुःस्वप्न हो सकता है। और इसलिए यदि आप 32 बिट विंडोज़ पर प्रोग्रामिंग कर रहे थे तो आपको ओवरलैप्ड आई/ओ जैसे अन्य समाधानों का सहारा लेना होगा, जो काम करने के लिए एक पिटा है।

क्योंकि प्रक्रियाएं वैश्विक संसाधनों जैसे धागे को साझा नहीं करती हैं (उदाहरण के लिए मॉलोक में वैश्विक लॉक) यह अधिक स्केलेबल है। जबकि थ्रेड अक्सर एक-दूसरे को अवरुद्ध करेंगे, प्रक्रियाएं स्वतंत्र रूप से चलती हैं।

यूनिक्स पर क्योंकि कांटा आपकी प्रक्रिया की एक कॉपी-ऑन-राइट क्लोन बनाता है, यह विंडोज में एक नया धागा बनाने से ज्यादा हेवीवेट नहीं है।

यदि आप व्याख्या की गई भाषाओं से निपटते हैं, जहां आम तौर पर वैश्विक दुभाषिया लॉक (पायथन, रूबी, PHP ...) है, तो एक ओएस जो आपको कांटा की क्षमता देता है, अनिवार्य है। अन्यथा एकाधिक प्रोसेसर का फायदा उठाने की आपकी क्षमता बहुत सीमित है।

एक और बात यह है कि यहां एक सुरक्षा आईएस है। प्रक्रिया मेमोरी स्पेस साझा नहीं करती है और एक दूसरे के आंतरिक विवरण को गड़बड़ नहीं कर सकती है। इससे उच्च स्थिरता होती है।यदि आपके पास एक सर्वर है जो धागे का उपयोग करता है, तो एक थ्रेड में एक क्रैश पूरे सर्वर एप्लिकेशन को नीचे ले जाएगा। एक दुर्घटना को तोड़ने के साथ केवल फोर्कड क्लोन नीचे ले जाएगा। यह त्रुटि को और अधिक सरलीकृत करने में भी त्रुटि देता है। अक्सर आपके फोर्क किए गए क्लोन को छोड़ने के लिए पर्याप्त होता है क्योंकि इससे मूल ऐप के लिए कोई फर्क नहीं पड़ता है।

एक सुरक्षा समस्या भी है। यदि एक फोर्क प्रक्रिया को दुर्भावनापूर्ण कोड से इंजेक्शन दिया जाता है तो यह माता-पिता को और प्रभावित नहीं कर सकता है। आधुनिक वेब ब्राउज़र इस का उपयोग करते हैं उदा। एक टैब से दूसरे टैब की रक्षा करने के लिए। यदि आपके पास फोर्क सिस्टम कॉल है तो यह सब प्रोग्राम के लिए अधिक दृढ़ है।

-1

अन्य उत्तरों ने यह बताने का अच्छा काम किया है कि क्यों fork ऐसा लगता है जितना तेज़ होगा, और यह मूल रूप से अस्तित्व में कैसे आया। लेकिन fork + exec कॉम्बो रखने के लिए भी एक मजबूत मामला बनना है, और यह लचीलापन है जो यह प्रदान करता है।

अक्सर, जब बच्चे की प्रक्रिया में वृद्धि होती है, तो बच्चे को निष्पादित करने से पहले प्रारंभिक कदम उठाए जाते हैं। उदाहरण के लिए: आप pipe (एक पाठक और लेखक) का उपयोग करके पाइप की एक जोड़ी बना सकते हैं, फिर लेखक को stdout या stderr पर रीडायरेक्ट करें, या पाठक को प्रक्रिया के stdin - या किसी अन्य फ़ाइल डिस्क्रिप्टर के रूप में उपयोग करें, उस मामले के लिए । या, आप पर्यावरण चर सेट करना चाहते हैं (लेकिन केवल बच्चे में)। या setrlimit के साथ संसाधन सीमा निर्धारित करें ताकि बच्चे संसाधनों की मात्रा को सीमित कर सकें (माता-पिता को सीमित किए बिना)। या उपयोगकर्ताओं को setuid/seteuid (अभिभावक को बदलने के बिना) बदलें। आदि आदि

निश्चित रूप से, आप यह सब एक hypothetical create_process फ़ंक्शन के साथ कर सकते हैं। लेकिन यह कवर करने के लिए बहुत सारी चीजें है! fork चलाने की लचीलापन क्यों न दें, जो भी आप बच्चे को सेट करना चाहते हैं, फिर exec चला रहे हैं?

इसके अलावा, कभी-कभी आपको वास्तव में किसी बच्चे की प्रक्रिया की आवश्यकता नहीं होती है। यदि आपका वर्तमान प्रोग्राम (या स्क्रिप्ट) पूरी तरह से उन सेटअप चरणों में से कुछ करने के लिए मौजूद है, और आखिरी चीज जो कभी भी करने जा रही है वह नई प्रक्रिया को चलाती है, तो दो प्रक्रियाएं क्यों होती हैं? आप वर्तमान प्रक्रिया को प्रतिस्थापित करने के लिए exec का उपयोग कर सकते हैं, अपनी याददाश्त और पीआईडी ​​जारी कर सकते हैं।

फोर्किंग केवल पढ़ने-योग्य डेटासेट के संबंध में कुछ उपयोगी व्यवहार की अनुमति देता है। उदाहरण के लिए, आपके पास एक मूल प्रक्रिया हो सकती है जो बड़ी मात्रा में डेटा एकत्र और अनुक्रमित करती है, फिर उस डेटा के आधार पर ट्रैवर्सल और गणना करने के लिए बाल श्रमिकों को रोक देती है। माता-पिता को इसे कहीं भी सहेजने की आवश्यकता नहीं है, बच्चों को इसे पढ़ने की आवश्यकता नहीं है, और आपको साझा स्मृति के साथ कोई जटिल काम करने की आवश्यकता नहीं है। (उदाहरण के तौर पर: कुछ डेटाबेस इसका उपयोग करते हैं कि बाल प्रक्रिया में माता-पिता को डिस्क पर बिना किसी स्मृति प्रक्रिया को डंप कर दिया जाता है।)

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

फोर्किंग स्मृति का प्रबंधन करने और विखंडन से बचने का एक उपयोगी तरीका भी हो सकता है, खासकर उच्च स्तरीय भाषाओं के लिए जो स्वचालित मेमोरी प्रबंधन (कचरा संग्रह) का उपयोग करते हैं और उनके मेमोरी लेआउट पर प्रत्यक्ष नियंत्रण नहीं रखते हैं। यदि आपकी प्रक्रिया को किसी विशेष ऑपरेशन के लिए बड़ी मात्रा में स्मृति की आवश्यकता होती है, तो आप फोर्क कर सकते हैं और उस ऑपरेशन को निष्पादित कर सकते हैं, फिर बाहर निकलने वाली सभी मेमोरी को मुक्त कर सकते हैं। इसके विपरीत, यदि आपने माता-पिता में ऑपरेशन किया है, तो आपके पास महत्वपूर्ण स्मृति विखंडन हो सकता है जो प्रक्रिया की अवधि के लिए जारी रह सकता है - लंबे समय तक चलने वाली प्रक्रिया के लिए बहुत अच्छा नहीं है।

और अंत में: एक बार आप स्वीकार करते हैं fork और exec दोनों का अपना उपयोग हैं, एक दूसरे से स्वतंत्र, प्रश्न बन जाता है - क्यों एक अलग समारोह है कि दो को जोड़ती है बनाने परेशान? ऐसा कहा जाता है कि यूनिक्स दर्शन का औजार "एक काम करना और इसे अच्छी तरह से करना" था। आपको अलग-अलग बिल्डिंग ब्लॉक के रूप में fork और exec देकर - और प्रत्येक को जितना संभव हो सके तेज़ और कुशल बनाकर - वे create_process फ़ंक्शन की तुलना में अधिक लचीलापन की अनुमति देते हैं।

संबंधित मुद्दे