कांटा (), vfork (), निष्पादन (और क्लोन) के बीच का अंतर


197

मैं Google पर इन चार के बीच अंतर ढूंढ रहा था और मुझे उम्मीद थी कि इस पर भारी मात्रा में जानकारी होगी, लेकिन वास्तव में चार कॉल के बीच कोई ठोस तुलना नहीं थी।

मैंने इन सिस्टम कॉल्स और यहां मुझे जो कुछ मिला, उसके बीच के अंतर पर एक तरह की बुनियादी नज़र रखने की कोशिश करने की कोशिश की। क्या यह सब जानकारी सही है / क्या मुझे कुछ महत्वपूर्ण याद आ रहा है?

Fork : कांटा कॉल मूल रूप से वर्तमान प्रक्रिया की एक डुप्लिकेट बनाता है, लगभग हर तरह से समान है (उदाहरण के लिए सब कुछ कॉपी नहीं किया गया है, कुछ कार्यान्वयन में संसाधन सीमाएं लेकिन विचार यह है कि कॉपी को यथासंभव बंद करें)।

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

Vfork: Vfork और fork के बीच मूल अंतर यह है कि जब vfork () के साथ एक नई प्रक्रिया बनाई जाती है, तो मूल प्रक्रिया अस्थायी रूप से निलंबित हो जाती है, और बच्चे की प्रक्रिया माता-पिता के पते की जगह उधार ले सकती है। यह अजीब स्थिति तब तक जारी रहती है जब तक कि बच्चे की प्रक्रिया या तो बाहर निकल जाती है, या कॉल निष्पादित (), जिस बिंदु पर मूल प्रक्रिया जारी रहती है।

इसका मतलब यह है कि मूल प्रक्रिया के अनपेक्षित रूप से संशोधित चर से बचने के लिए एक vfork () की बाल प्रक्रिया सावधान रहना चाहिए। विशेष रूप से, बच्चे की प्रक्रिया को vfork () कॉल वाले फ़ंक्शन से वापस नहीं लौटना चाहिए, और इसे बाहर निकलना नहीं चाहिए () (यदि इसे बाहर निकलने की आवश्यकता है, तो इसे _exit () का उपयोग करना चाहिए; वास्तव में, यह बच्चे के लिए भी सही है। एक सामान्य कांटा ())।

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

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

जब बच्चे की प्रक्रिया क्लोन के साथ बनाई जाती है, तो यह फ़ंक्शन एप्लिकेशन fn (arg) को निष्पादित करता है। (यह कांटा से भिन्न होता है, जहां मूल कांटा कॉल के बिंदु से बच्चे में निष्पादन जारी रहता है।) fn तर्क एक फ़ंक्शन के लिए एक संकेतक है जिसे उसके निष्पादन की शुरुआत में बच्चे की प्रक्रिया द्वारा बुलाया जाता है। Arg तर्क fn फ़ंक्शन को दिया जाता है।

जब fn (arg) फ़ंक्शन अनुप्रयोग देता है, तो चाइल्ड प्रक्रिया समाप्त हो जाती है। Fn द्वारा दिया गया पूर्णांक बाल प्रक्रिया के लिए निकास कोड है। बाहर निकलने (2) को कॉल करके या घातक संकेत प्राप्त करने के बाद बच्चे की प्रक्रिया भी स्पष्ट रूप से समाप्त हो सकती है।

जानकारी प्राप्त प्रपत्र:

इसे पढ़ने का समय निकालने के लिए धन्यवाद ! :)


2
Vfork को निकास क्यों नहीं कहना चाहिए ()? या नहीं लौटना है? बाहर नहीं निकलता () सिर्फ _exit () का उपयोग करता है? मैं भी समझने की कोशिश कर रहा हूँ :)
लेज़रशेक्स

2
@Gnuey: क्योंकि यह संभावित रूप से है (यदि इसे अलग तरह से लागू fork()किया जाता है, जो कि यह लिनक्स में है, और शायद सभी BSDs) अपने माता-पिता के पते की जगह उधार लेते हैं। कुछ भी, जो कॉल करने के अलावा execve()या _exit(), माता-पिता को गड़बड़ करने की एक बड़ी क्षमता है। विशेष रूप से, exit()कॉल atexit()हैंडलर और अन्य "फाइनल", जैसे: यह stdio धाराओं को फ्लश करता है। एक vfork()बच्चे से वापस आना संभवतः (पहले की तरह ही कैवियट) माता-पिता के ढेर को गड़बड़ कर देगा।
निंजाल

मैं सोच रहा था कि माता-पिता की प्रक्रिया के धागे क्या होते हैं; क्या उन सभी को क्लोन किया गया है या केवल उस धागे को कहा जाता है जो forkसिस्कॉल कहता है ?
मोहम्मद जाफर मशहदी

@LazerSharks vfork एक थ्रेड-जैसी प्रक्रिया का निर्माण करता है, जहाँ मेमोरी को कॉपी-ऑन-राइट प्रोटेक्शन के बिना साझा किया जाता है, इसलिए स्टैक स्टफ करने से मूल प्रक्रिया को नष्ट किया जा सकता है।
जसेंन

जवाबों:


159
  • vfork()एक अप्रचलित अनुकूलन है। अच्छे स्मृति प्रबंधन से पहले, fork()माता-पिता की स्मृति की पूरी प्रतिलिपि बनाई गई थी, इसलिए यह बहुत महंगा था। चूंकि कई मामलों में एक के fork()बाद एक किया गया था exec(), जो वर्तमान मेमोरी मैप को छोड़ देता है और एक नया बनाता है, यह एक अनावश्यक खर्च था। आजकल, fork()स्मृति की नकल नहीं करता है; यह बस इतना, "लिखने पर प्रतिलिपि" के रूप में सेट है fork()+ exec()के रूप में कुशल के रूप में बस है vfork()+ exec()

  • clone()द्वारा उपयोग किया जाने वाला syscall है fork()। कुछ मापदंडों के साथ, यह एक नई प्रक्रिया बनाता है, दूसरों के साथ, यह एक धागा बनाता है। उनके बीच का अंतर सिर्फ यह है कि कौन सी डेटा संरचनाएं (मेमोरी स्पेस, प्रोसेसर स्टेट, स्टैक, पीआईडी, ओपन फाइलें आदि) साझा की जाती हैं या नहीं।



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

5
दरअसल, मैंने पहली बार देखा है कि जब आपका आरएसएस बड़ा होता है तो कांटा () सस्ते से कितना दूर होता है। मुझे लगता है कि क्योंकि कर्नेल को अभी भी सभी पृष्ठ तालिकाओं को कॉपी करना है।
मार्टिना फेरारी 3

4
इसे सभी पृष्ठ तालिकाओं को कॉपी करना है, सभी लेखन योग्य मेमोरी कॉपी-ऑन-राइट को दोनों प्रक्रियाओं में सेट करना है , TLB फ्लश करना है, और फिर इसे माता-पिता को सभी परिवर्तनों को वापस करना होगा (और TLB को फिर से फ्लश करना होगा) exec
zwol

3
vfork अभी भी cygwin (Microsoft के विंडोज के शीर्ष पर चलने वाला कर्नेल, जो कर्नेल का उपयोग करता है) में उपयोगी है। साइबरविन एक कुशल कांटा लागू नहीं कर सकता है, क्योंकि अंतर्निहित ओएस में एक नहीं है।
ctrl-alt-delor

80
  • execve() एक निष्पादन योग्य फ़ाइल से लोड किए गए एक और एक के साथ वर्तमान निष्पादन योग्य छवि को बदलता है।
  • fork() एक बच्चे की प्रक्रिया बनाता है।
  • vfork()का एक ऐतिहासिक अनुकूलित संस्करण है fork(), जिसका उपयोग तब किया जाता है जब execve()इसे सीधे बाद कहा जाता है fork()। यह गैर-एमएमयू सिस्टम (जहां fork()एक कुशल तरीके से काम नहीं कर सकता है) में अच्छी तरह से काम करने के लिए निकला और जब fork()कुछ छोटे प्रोग्राम (जावा के बारे में सोचें Runtime.exec()) को चलाने के लिए एक विशाल मेमोरी फ़ुटप्रिंट के साथ प्रक्रिया होती है । POSIX ने posix_spawn()इन दोनों के बाद के आधुनिक उपयोगों को बदलने के लिए मानकीकृत किया है vfork()
  • posix_spawn()एक के बराबर है fork()/execve(), और बीच में कुछ fd करतब दिखाने की भी अनुमति देता है। यह fork()/execve()मुख्य रूप से गैर-एमएमयू प्लेटफार्मों के लिए प्रतिस्थापित करना चाहिए ।
  • pthread_create() एक नया सूत्र बनाता है।
  • clone()लिनक्स-विशिष्ट कॉल है, जिसका उपयोग किसी भी चीज़ को लागू करने के लिए किया जा सकता fork()है pthread_create()। यह बहुत नियंत्रण देता है। पर प्रेरित किया rfork()
  • rfork()एक योजना -9 विशिष्ट कॉल है। यह एक सामान्य कॉल माना जाता है, जो पूर्ण प्रक्रियाओं और थ्रेड्स के बीच साझा करने की कई डिग्री की अनुमति देता है।

2
अधिक जानकारी जोड़ने के लिए धन्यवाद, जिसके लिए वास्तव में पूछा गया था, इससे मुझे अपना समय बचाने में मदद मिली
नीरज

5
योजना 9 एक ऐसी चिढ़ है।
जेजे

1
उन लोगों के लिए जो एमएमयू का मतलब नहीं याद कर सकते हैं: "मेमोरी मैनेजमेंट यूनिट" - विकिपीडिया पर
एमजीआरवाई

43
  1. fork()- एक नई बाल प्रक्रिया बनाता है, जो कि मूल प्रक्रिया की एक पूरी प्रति है। बच्चे और माता-पिता की प्रक्रियाएं अलग-अलग वर्चुअल एड्रेस स्पेस का उपयोग करती हैं, जो शुरू में एक ही मेमोरी पेज द्वारा पॉप्युलेट होती हैं। फिर, जैसा कि दोनों प्रक्रियाओं को निष्पादित किया जाता है, वर्चुअल पता स्थान अधिक से अधिक भिन्न होने लगते हैं, क्योंकि ऑपरेटिंग सिस्टम मेमोरी पृष्ठों की एक आलसी प्रतिलिपि बनाता है जो इन दोनों प्रक्रियाओं में से किसी एक द्वारा लिखी जा रही हैं और संशोधित पृष्ठों की एक स्वतंत्र प्रतियां असाइन करती हैं प्रत्येक प्रक्रिया के लिए स्मृति। इस तकनीक को कॉपी-ऑन-राइट (गाय) कहा जाता है।
  2. vfork()- एक नई बाल प्रक्रिया बनाता है, जो मूल प्रक्रिया की एक "त्वरित" प्रतिलिपि है। सिस्टम कॉल के विपरीत fork(), बच्चे और माता-पिता की प्रक्रिया समान वर्चुअल पता स्थान साझा करती हैं। ध्यान दें! एक ही वर्चुअल एड्रेस स्पेस का उपयोग करते हुए, माता-पिता और बच्चे दोनों एक ही स्टैक, स्टैक पॉइंटर और इंस्ट्रक्शन पॉइंटर का उपयोग करते हैं, जैसा कि क्लासिक के मामले में है fork()! माता-पिता और बच्चे के बीच अवांछित हस्तक्षेप को रोकने के लिए, जो एक ही स्टैक का उपयोग करते हैं, माता-पिता की प्रक्रिया का निष्पादन तब तक जमे हुए है जब तक कि बच्चा या तो कॉल नहीं करेगा exec()(एक नया वर्चुअल एड्रेस स्पेस और एक अलग स्टैक के लिए एक संक्रमण बनाएं) या _exit()प्रक्रिया निष्पादन की समाप्ति )। "कांटा-और-निष्पादन" मॉडल vfork()के fork()लिए अनुकूलन है । यह 4-5 गुना तेजी से किया जा सकता है fork(), क्योंकि इसके विपरीतfork()(यहां तक ​​कि गाय को ध्यान में रखकर), कार्यान्वयनvfork() सिस्टम कॉल में एक नई पता स्थान का निर्माण (आवंटन और नए पृष्ठ निर्देशिकाओं की स्थापना) शामिल नहीं है।
  3. clone()- एक नई बच्चे की प्रक्रिया बनाता है। इस प्रणाली के विभिन्न पैरामीटर कॉल करते हैं, निर्दिष्ट करते हैं कि मूल प्रक्रिया के किन हिस्सों को बच्चे की प्रक्रिया में कॉपी किया जाना चाहिए और उनके बीच कौन से हिस्से साझा किए जाएंगे। नतीजतन, इस सिस्टम कॉल का उपयोग सभी प्रकार के निष्पादन संस्थाओं को बनाने के लिए किया जा सकता है, थ्रेड्स से शुरू होकर पूरी तरह से स्वतंत्र प्रक्रियाओं द्वारा समाप्त किया जा सकता है। वास्तव में, clone()सिस्टम कॉल वह आधार है जिसका उपयोग pthread_create()और उसके सभी परिवार के कार्यान्वयन के लिए किया जाता हैfork() सिस्टम कॉल के ।
  4. exec()- प्रक्रिया की सभी मेमोरी को रीसेट करता है, निष्पादन योग्य बाइनरी निर्दिष्ट करता है और लोड करता है, नए स्टैक सेट करता है और लोड किए गए निष्पादन योग्य के प्रवेश बिंदु पर नियंत्रण रखता है। यह सिस्टम कॉल कभी भी कॉलर पर नियंत्रण वापस नहीं करता है और पहले से मौजूद प्रक्रिया के लिए एक नया कार्यक्रम लोड करने के लिए कार्य करता है। सिस्टम कॉल के साथ यह सिस्टम कॉल fork()एक साथ एक क्लासिक UNIX प्रक्रिया प्रबंधन मॉडल बनाती है जिसे "कांटा-और-निष्पादन" कहा जाता है।

2
ध्यान दें कि BSD और POSIX आवश्यकताएं vforkइतनी कमजोर हैं कि यह (और POSIX.1-2008 पूरी तरह से कल्पना से निकाल देता है) vforkका एक पर्याय बनाने के लिए कानूनी होगा । यदि आप अपने कोड का परीक्षण किसी सिस्टम पर करते हैं, जो उन्हें समानार्थी बनाता है (उदाहरण के लिए, नेटबेड से अधिकांश 4.4 बीएसडी, प्री-2.2.0-प्री 6 लिनक्स कर्नेल, आदि), तो यह अनुबंध के उल्लंघन करने पर भी काम कर सकता है , फिर विस्फोट हो सकता है। यदि आप इसे कहीं और चलाते हैं। उनमें से कुछ जो इसे (उदाहरण के लिए ओपनबीएसडी) के साथ अनुकरण करते हैं, अभी भी माता-पिता को यह गारंटी देते हैं कि बच्चे के एस या एस तक चलने को फिर से शुरू न करें । यह हास्यास्पद रूप से गैर-पोर्टेबल है। forkvforkvforkforkexec_exit
शैडो रेंजर

2
आपके तीसरे बिंदु के अंतिम वाक्य के बारे में: मैंने लिनक्स पर स्ट्रेस का उपयोग करते हुए देखा कि जब वास्तव में कांटा () के लिए ग्लिबक रैपर क्लोन syscall को कॉल करता है, तो vfork के लिए आवरण () कॉल vfork syscall
illsam

7

कांटा (), vfork () और क्लोन () सभी do_fork () को वास्तविक कार्य करने के लिए कहते हैं, लेकिन विभिन्न मापदंडों के साथ।

asmlinkage int sys_fork(struct pt_regs regs)
{
    return do_fork(SIGCHLD, regs.esp, &regs, 0);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
    unsigned long clone_flags;
    unsigned long newsp;

    clone_flags = regs.ebx;
    newsp = regs.ecx;
    if (!newsp)
        newsp = regs.esp;
    return do_fork(clone_flags, newsp, &regs, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
    return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0);
}
#define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_VM    0x00000100  /* set if VM shared between processes */

SIGCHLD means the child should send this signal to its father when exit.

कांटा के लिए, बच्चे और पिता के पास स्वतंत्र वीएम पेज टेबल है, लेकिन दक्षता के बाद से, कांटा वास्तव में किसी भी पेज की नकल नहीं करेगा, यह सिर्फ बच्चे की प्रक्रिया के लिए आसानी से पढ़ने के लिए सभी लेखनीय पेज सेट करता है। इसलिए जब चाइल्ड प्रोसेस उस पेज पर कुछ लिखना चाहता है, तो एक पेज अपवाद होता है और कर्नेल को लिखित अनुमति के साथ पुराने पेज से क्लोन किया गया नया पेज आवंटित होगा। इसे "कॉपी ऑन राइट" कहा जाता है।

Vfork के लिए, वर्चुअल मेमोरी बिल्कुल बच्चे और पिता द्वारा होती है --- सिर्फ इस वजह से कि पिता और बच्चा एक दूसरे के साथ नहीं जा सकते क्योंकि वे एक दूसरे को प्रभावित करेंगे। इसलिए पिता "do_fork ()" के अंत में सो जाएगा और जब बच्चा कॉल एक्जिट () या निष्पादित (तब से) जाग जाएगा तब से उसके पास नया पेज टेबल होगा। यहाँ कोड (do_fork ()) में है कि पिता सोते हैं।

if ((clone_flags & CLONE_VFORK) && (retval > 0))
down(&sem);
return retval;

यहाँ कोड है (mm_release () जिसे एक्ज़िट () और एक्ज़ीक्यूट () कहा जाता है जो पिता को जगाता है।

up(tsk->p_opptr->vfork_sem);

Sys_clone () के लिए, यह अधिक लचीला है क्योंकि आप इसमें किसी भी क्लोन_फ्लैग को इनपुट कर सकते हैं। इसलिए pthread_create () इस सिस्टम कॉल को कई क्लोन_फ्लैग्स के साथ कॉल करें:

int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL_ CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM) |

सारांश: कांटा (), vfork () और क्लोन () पिता प्रक्रिया के साथ संसाधन साझा करने के विभिन्न माउंट के साथ बच्चे की प्रक्रियाओं का निर्माण करेगा। हम यह भी कह सकते हैं कि vfork () और क्लोन () धागे बना सकते हैं (वास्तव में वे प्रक्रियाएं हैं क्योंकि उनके पास स्वतंत्र कार्य_ बाधा है) क्योंकि वे पिता प्रक्रिया के साथ VM पृष्ठ तालिका साझा करते हैं।


-4

कांटा () में, या तो बच्चे या माता-पिता की प्रक्रिया cpu चयन के आधार पर निष्पादित होगी .. लेकिन vfork () में, निश्चित रूप से बच्चा पहले निष्पादित करेगा। बच्चे की समाप्ति के बाद, माता-पिता निष्पादित करेंगे।


3
गलत। vfork()के रूप में लागू किया जा सकता है fork()
नवजाल

AnyFork () के बाद, यह परिभाषित नहीं है कि कौन पहले माता-पिता / बच्चे को चलाता है।
अजयकुमारबसुथकर

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

दरअसल @AjayKumarBasuthkar और @ninjalj, आप दोनों गलत हैं। के साथ vfork(), बच्चा पहले चलाता है। यह आदमी पृष्ठों में है; माता-पिता का निष्पादन तब तक निलंबित रहता है जब तक कि बच्चा या तो मर नहीं जाता है exec। और निंजाल कर्नेल स्रोत कोड को देखते हैं। लागू करने के लिए कोई तरीका नहीं है vfork()के रूप में fork(), क्योंकि वे करने के लिए विभिन्न तर्क पारित do_fork()गिरी के भीतर। हालाँकि, आप syscall के vforkसाथ कार्यान्वित कर सकते हैंclone
Zac Wimer

@ZacWimer: ShadowRanger की टिप्पणी को अन्य उत्तर stackoverflow.com/questions/4856255// पर देखें। पुराने लिनक्स ने उन्हें समानित किया, जैसा कि जाहिर तौर पर NetBSD के अलावा बीएसडी (जो कि गैर-एमएमयू सिस्टम के बहुत सारे पोर्ट किए जाते हैं) करते हैं। लिनक्स मैनपेज से: 4.4BSD में इसे फोर्क (2) का पर्याय बनाया गया था, लेकिन NetBSD ने इसे फिर से पेश किया; see netbsd.org/Documentation/kernel/vfork.html .org देखें। लिनक्स में, यह 2.2.0-pre6 या तो तक फोर्क (2) के बराबर रहा है।
नवजलज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.