आपस में क्या मतभेद हैं fork
और केexec
?
fork
मूल रूप से क्लोनिंग है: O
आपस में क्या मतभेद हैं fork
और केexec
?
fork
मूल रूप से क्लोनिंग है: O
जवाबों:
UNIX की भावना का उपयोग fork
और exec
उसमें अनुकरण करना, यह नई प्रक्रियाओं को शुरू करने का एक बहुत ही सरल तरीका प्रदान करता है।
fork
कॉल मूल रूप में, मौजूदा प्रक्रिया का डुप्लिकेट समान बनाता है लगभग हर तरह से। हर चीज को कॉपी नहीं किया जाता है (उदाहरण के लिए, कुछ कार्यान्वयन में संसाधन सीमाएं) लेकिन विचार यह है कि प्रतिलिपि को यथासंभव बंद करें।
नई प्रक्रिया (चाइल्ड) को एक अलग प्रक्रिया आईडी (पीआईडी) मिलती है और पुरानी प्रक्रिया (पेरेंट) की पीआईडी है, जो उसके माता-पिता पीआईडी (पीपीआईडी) के रूप में है। क्योंकि दो प्रक्रियाएं अब एक ही कोड के साथ चल रही हैं, वे बता सकते हैं कि कौन सा रिटर्न कोड है fork
- बच्चे को 0 मिलता है, माता-पिता को बच्चे का पीआईडी मिलता है। यह सब, निश्चित रूप से, fork
कॉल कार्यों को मान रहा है - यदि नहीं, तो कोई बच्चा नहीं बना है और माता-पिता को एक त्रुटि कोड मिलता है।
exec
कॉल एक तरह से मूल रूप से एक नया कार्यक्रम के साथ पूरे मौजूदा प्रक्रिया को बदलने के लिए है। यह कार्यक्रम को वर्तमान प्रक्रिया स्थान में लोड करता है और इसे प्रवेश बिंदु से चलाता है।
इसलिए, fork
और exec
वर्तमान प्रक्रिया के एक बच्चे के रूप में चल रहे एक नए कार्यक्रम को प्राप्त करने के लिए अक्सर अनुक्रम में उपयोग किया जाता है। शैल आमतौर पर ऐसा करते हैं जब भी आप किसी प्रोग्राम को चलाने की कोशिश करते हैं जैसे find
- शेल फोर्क, तो बच्चा लोड करता हैfind
प्रोग्राम को मेमोरी में , सभी कमांड लाइन तर्क, मानक I / O और इसके आगे की स्थापना करता है।
लेकिन उन्हें एक साथ उपयोग करने की आवश्यकता नहीं है। यह किसी भी कार्यक्रम के लिए पूरी तरह से fork
अपने आप में स्वीकार्य है exec
अगर उदाहरण के लिए, कार्यक्रम में माता-पिता और बच्चे दोनों कोड शामिल हैं (आपको सावधान रहना होगा कि आप क्या करते हैं, प्रत्येक कार्यान्वयन में प्रतिबंध हो सकते हैं)। यह डेमों के लिए काफी उपयोग किया जाता था (और अभी भी है) जो केवल एक टीसीपी पोर्ट पर सुनते हैं और fork
एक विशिष्ट अनुरोध को संसाधित करने के लिए खुद की एक कॉपी करते हैं, जबकि माता-पिता वापस सुनने के लिए जाते हैं।
इसी तरह, ऐसे कार्यक्रम जो जानते हैं कि वे समाप्त हो चुके हैं और बस एक और कार्यक्रम चलाना चाहते हैं fork
, exec
और फिर करने की आवश्यकता नहीं हैwait
बच्चे के लिए नहीं। वे बच्चे को सीधे अपनी प्रक्रिया के स्थान में लोड कर सकते हैं।
कुछ UNIX कार्यान्वयन में एक अनुकूलित होता है fork
जो कॉपी-ऑन-राइट को कॉल करता है। यह प्रक्रिया अंतरिक्ष की नकल में देरी करने के लिए एक चाल है fork
जब तक कि कार्यक्रम उस स्थान में कुछ बदलने का प्रयास नहीं करता है। यह केवल उन्हीं कार्यक्रमों का उपयोग करने के लिए उपयोगी है, fork
न exec
कि इसमें उन्हें संपूर्ण प्रक्रिया स्थान की प्रतिलिपि बनाने की आवश्यकता है।
अगर exec
है निम्नलिखित कहा जाता fork
है (और यह क्या ज्यादातर होता है), उस प्रक्रिया को अंतरिक्ष के लिए एक लिखने का कारण बनता है और यह उसके बाद बच्चे की प्रक्रिया के लिए नकल की जाती है।
नोट वहाँ के एक पूरे परिवार है कि exec
कॉल ( execl
, execle
, execve
और पर इतना), लेकिनexec
संदर्भ में यहां उनमें से किसी को मतलब है।
निम्न आरेख उस विशिष्ट fork/exec
ऑपरेशन को दिखाता है जहां bash
शेल का उपयोग ls
कमांड के साथ डायरेक्टरी को सूचीबद्ध करने के लिए किया जाता है :
+--------+
| pid=7 |
| ppid=4 |
| bash |
+--------+
|
| calls fork
V
+--------+ +--------+
| pid=7 | forks | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash | | bash |
+--------+ +--------+
| |
| waits for pid 22 | calls exec to run ls
| V
| +--------+
| | pid=22 |
| | ppid=7 |
| | ls |
V +--------+
+--------+ |
| pid=7 | | exits
| ppid=4 | <---------------+
| bash |
+--------+
|
| continues
V
fork()
वर्तमान प्रक्रिया को दो प्रक्रियाओं में विभाजित करता है। या दूसरे शब्दों में, प्रोग्राम के बारे में सोचने के लिए आपका अच्छा रैखिक आसान अचानक दो अलग-अलग प्रोग्राम बन जाता है, जो एक कोड चला रहा है:
int pid = fork();
if (pid == 0)
{
printf("I'm the child");
}
else
{
printf("I'm the parent, my child is %i", pid);
// here we can kill the child, but that's not very parently of us
}
यह आपके दिमाग को उड़ा सकता है। अब आपके पास एक समान कोड है जिसमें दो प्रक्रियाओं द्वारा निष्पादित किए जाने वाले बहुत समान राज्य हैं। बच्चे की प्रक्रिया उस कोड के सभी कोड और मेमोरी को विरासत में लेती है, जिसने इसे अभी बनाया है, जिसमें से शुरू करना जहां fork()
कॉल बस छोड़ दिया गया है। फर्क सिर्फ इतना हैfork()
है कि आपको यह बताने के लिए रिटर्न कोड है कि आप माता-पिता या बच्चे हैं। यदि आप माता-पिता हैं, तो वापसी मूल्य बच्चे की आईडी है।
exec
पकड़ना थोड़ा आसान है, आप बस exec
लक्ष्य निष्पादन योग्य का उपयोग करके एक प्रक्रिया को निष्पादित करने के लिए कहते हैं और आपके पास एक ही कोड को चलाने या एक ही राज्य को इनहेरिट करने वाली दो प्रक्रियाएँ नहीं हैं। जैसे @Steve हॉकिन्स कहते हैं, exec
आपके fork
द्वारा वर्तमान प्रक्रिया में लक्ष्य निष्पादन योग्य निष्पादित करने के लिए उपयोग किया जा सकता है ।
pid < 0
और जब fork()
कॉल विफल
मुझे लगता है कि मार्क रोचाइंड द्वारा "एडवांस्ड यूनिक्स प्रोग्रामिंग" के कुछ कॉन्सेप्ट fork()
/ की विभिन्न भूमिकाओं को समझने में मददगार थे exec()
, खासकर विंडोज CreateProcess()
मॉडल के लिए इस्तेमाल होने वाले किसी व्यक्ति के लिए :
एक प्रोग्राम निर्देशों और डेटा का एक संग्रह है जिसे डिस्क पर एक नियमित फ़ाइल में रखा जाता है। (1.1.2 कार्यक्रमों, प्रक्रियाओं और थ्रेड्स से)
।
प्रोग्राम चलाने के लिए, कर्नेल को पहली बार एक नई प्रक्रिया बनाने के लिए कहा जाता है , जो एक ऐसा वातावरण है जिसमें एक प्रोग्राम निष्पादित होता है। (1.1.2 कार्यक्रमों, प्रक्रियाओं और थ्रेड्स से भी)
।
एक प्रक्रिया और एक कार्यक्रम के बीच के अंतर को पूरी तरह से समझे बिना निष्पादन या कांटा सिस्टम कॉल को समझना असंभव है। यदि ये शब्द आपके लिए नए हैं, तो आप वापस जा सकते हैं और धारा 1.1.2 की समीक्षा कर सकते हैं। यदि आप अभी आगे बढ़ने के लिए तैयार हैं, तो हम एक वाक्य में अंतर को संक्षेप में प्रस्तुत करेंगे: एक प्रक्रिया एक निष्पादन वातावरण है जिसमें निर्देश, उपयोगकर्ता-डेटा और सिस्टम-डेटा सेगमेंट के साथ-साथ रनटाइम पर हासिल किए गए कई अन्य संसाधन शामिल हैं। , जबकि एक प्रोग्राम एक फाइल है जिसमें निर्देश और डेटा होते हैं जिनका उपयोग किसी प्रक्रिया के निर्देश और उपयोगकर्ता-डेटा सेगमेंट को आरंभ करने के लिए किया जाता है। (5.3
exec
सिस्टम कॉल से)
एक बार जब आप एक कार्यक्रम और एक प्रक्रिया के बीच के अंतर को समझ लेते हैं, तो निम्न के रूप में कार्य fork()
और exec()
कार्य के व्यवहार को संक्षेप में प्रस्तुत किया जा सकता है:
fork()
वर्तमान प्रक्रिया की डुप्लिकेट बनाता हैexec()
प्रोग्राम को वर्तमान प्रक्रिया में किसी अन्य प्रोग्राम के साथ बदलता है(यह अनिवार्य रूप से पैक्सिडाब्लो के अधिक विस्तृत जवाब के डमी के संस्करण के लिए 'सरलीकृत' है )
फोर्क एक कॉलिंग प्रक्रिया की एक प्रति बनाता है। आम तौर पर संरचना का अनुसरण करता है
int cpid = fork( );
if (cpid = = 0)
{
//child code
exit(0);
}
//parent code
wait(cpid);
// end
(चाइल्ड प्रोसेस टेक्स्ट (कोड), डेटा, स्टैक कॉलिंग प्रोसेस के समान है) चाइल्ड प्रोसेस अगर ब्लॉक में कोड निष्पादित करता है।
EXEC नई प्रक्रिया के कोड, डेटा, स्टैक के साथ वर्तमान प्रक्रिया को बदलता है। आम तौर पर संरचना का अनुसरण करता है
int cpid = fork( );
if (cpid = = 0)
{
//child code
exec(foo);
exit(0);
}
//parent code
wait(cpid);
// end
(एक्ज़िक कॉल के बाद यूनिक्स कर्नेल चाइल्ड प्रोसेस टेक्स्ट, डेटा, स्टैक और foo प्रोसेस रिलेटेड टेक्स्ट / डेटा से भरता है) इस प्रकार चाइल्ड प्रोसेस अलग कोड (foo का कोड {पैरेंट के समान नहीं) के साथ होता है
एक नई बाल प्रक्रिया बनाने के लिए उनका उपयोग एक साथ किया जाता है। सबसे पहले, कॉलिंग fork
वर्तमान प्रक्रिया (बच्चे की प्रक्रिया) की एक प्रति बनाता है। फिर,exec
नई प्रक्रिया के साथ मूल प्रक्रिया की प्रतिलिपि को "प्रतिस्थापित" करने के लिए बच्चे की प्रक्रिया के भीतर से बुलाया जाता है।
प्रक्रिया कुछ इस प्रकार है:
child = fork(); //Fork returns a PID for the parent process, or 0 for the child, or -1 for Fail
if (child < 0) {
std::cout << "Failed to fork GUI process...Exiting" << std::endl;
exit (-1);
} else if (child == 0) { // This is the Child Process
// Call one of the "exec" functions to create the child process
execvp (argv[0], const_cast<char**>(argv));
} else { // This is the Parent Process
//Continue executing parent process
}
कांटा () मौजूदा प्रक्रिया की एक प्रति बनाता है, नए बच्चे में कांटा () कॉल के बाद से शुरू होने के साथ निष्पादन। कांटा () के बाद, वे समान हैं, फोर्क () फ़ंक्शन के रिटर्न मान को छोड़कर। (अधिक विवरण के लिए RTFM।) फिर भी दो साझा प्रक्रियाओं को किसी भी साझा फ़ाइल हैंडल के माध्यम से छोड़कर, दूसरे के साथ हस्तक्षेप करने में असमर्थ होने के साथ, आगे भी डायवर्ज किया जा सकता है।
निष्पादित () वर्तमान प्रक्रिया को एक नए के साथ बदल देता है। कांटा () के साथ इसका कोई लेना-देना नहीं है, सिवाय इसके कि एक निष्पादन () अक्सर कांटा का पालन करता है () जब जो चाहता है वह एक अलग बाल प्रक्रिया को लॉन्च करने के बजाय वर्तमान को बदलने के लिए है।
के बीच मुख्य अंतर fork()
और exec()
वह यह है कि,
fork()
सिस्टम कॉल वर्तमान में चल रहे कार्यक्रम का क्लोन पैदा करता है। मूल कार्यक्रम फोर्क () फ़ंक्शन कॉल के बाद कोड की अगली पंक्ति के साथ निष्पादन जारी रखता है। क्लोन कोड की अगली पंक्ति में भी निष्पादन शुरू करता है। निम्नलिखित कोड को देखें जो मुझे http://timmurphy.org/2014/04/26/use-fork-in-cc-a-minimum-working-example/ से मिला था।
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printf("--beginning of program\n");
int counter = 0;
pid_t pid = fork();
if (pid == 0)
{
// child process
int i = 0;
for (; i < 5; ++i)
{
printf("child process: counter=%d\n", ++counter);
}
}
else if (pid > 0)
{
// parent process
int j = 0;
for (; j < 5; ++j)
{
printf("parent process: counter=%d\n", ++counter);
}
}
else
{
// fork failed
printf("fork() failed!\n");
return 1;
}
printf("--end of program--\n");
return 0;
}
यह प्रोग्राम fork()
आईएनजी से पहले शून्य पर सेट एक काउंटर वेरिएबल घोषित करता है । फोर्क कॉल के बाद, हमारे पास समानांतर में चलने वाली दो प्रक्रियाएं हैं, दोनों काउंटर के अपने स्वयं के संस्करण को बढ़ाते हैं। प्रत्येक प्रक्रिया पूरी होने और बाहर निकलने के लिए चलेगी। क्योंकि प्रक्रियाएं समानांतर में चलती हैं, हमारे पास यह जानने का कोई तरीका नहीं है कि कौन पहले खत्म होगा। इस कार्यक्रम को चलाने से नीचे दिखाए गए कुछ समान प्रिंट होंगे, हालांकि परिणाम एक रन से अगले तक भिन्न हो सकते हैं।
--beginning of program
parent process: counter=1
parent process: counter=2
parent process: counter=3
child process: counter=1
parent process: counter=4
child process: counter=2
parent process: counter=5
child process: counter=3
--end of program--
child process: counter=4
child process: counter=5
--end of program--
exec()
सिस्टम कॉल का परिवार वर्तमान प्रक्रिया के कोड को दूसरे कोड के कोड के साथ बदल देता है। प्रक्रिया अपने पीआईडी को बरकरार रखती है लेकिन यह एक नया कार्यक्रम बन जाता है। उदाहरण के लिए, निम्नलिखित कोड पर विचार करें:
#include <stdio.h>
#include <unistd.h>
main() {
char program[80],*args[3];
int i;
printf("Ready to exec()...\n");
strcpy(program,"date");
args[0]="date";
args[1]="-u";
args[2]=NULL;
i=execvp(program,args);
printf("i=%d ... did it work?\n",i);
}
यह प्रोग्राम execvp()
फ़ंक्शन को अपने कोड को दिनांक प्रोग्राम के साथ बदलने के लिए कहता है । यदि कोड को exec1.c नामक फ़ाइल में संग्रहीत किया जाता है, तो इसे निष्पादित करने से निम्न आउटपुट का उत्पादन होता है:
Ready to exec()...
Tue Jul 15 20:17:53 UTC 2008
कार्यक्रम निष्पादित करने के लिए लाइन yReady आउटपुट ()। । । The और execvp () फ़ंक्शन को कॉल करने के बाद, दिनांक प्रोग्राम के साथ इसके कोड को बदल देता है। ध्यान दें कि लाइन -। । । क्या यह काम नहीं किया गया है, क्योंकि उस बिंदु पर कोड को बदल दिया गया है। इसके बजाय, हम udate -u. the को निष्पादित करने का आउटपुट देखते हैं
यह चलने की प्रक्रिया की एक प्रति बनाता है। चल रही प्रक्रिया को पैरेंट प्रोसेस कहा जाता है और नई बनाई गई प्रक्रिया को चाइल्ड प्रोसेस कहा जाता है । लौटाए गए मूल्य को देखकर दोनों में अंतर करने का तरीका है:
fork()
माता-पिता में बच्चे की प्रक्रिया की प्रक्रिया पहचानकर्ता (पीआईडी) लौटाता है
fork()
बच्चे में 0 देता है।
exec()
:
यह एक प्रक्रिया के भीतर एक नई प्रक्रिया शुरू करता है। यह मौजूदा प्रक्रिया में एक नए प्रोग्राम को लोड करता है, जो मौजूदा की जगह लेता है।
fork()
+ exec()
:
जब एक नया प्रोग्राम लॉन्च करना है fork()
, तो सबसे पहले , एक नई प्रक्रिया बनाना, और फिर exec()
(यानी मेमोरी में लोड करें और प्रोग्राम को बाइनरी करें) इसे चलाने के लिए माना जाता है।
int main( void )
{
int pid = fork();
if ( pid == 0 )
{
execvp( "find", argv );
}
//Put the parent to sleep for 2 sec,let the child finished executing
wait( 2 );
return 0;
}
अवधारणा fork()
और exec()
अवधारणा को समझने के लिए मुख्य उदाहरण शेल है , कमांड दुभाषिया कार्यक्रम जिसे उपयोगकर्ता आमतौर पर सिस्टम में लॉग इन करने के बाद निष्पादित करते हैं। शेल कमांड लाइन के पहले शब्द को कमांड नाम के रूप में व्याख्या करता है
कई आदेशों के लिए, शेल फ़ॉर्क्स और चाइल्ड प्रोसेस कमांड लाइन पर शेष शब्दों के साथ कमांड के मापदंडों के रूप में नाम के साथ जुड़े कमांड को निष्पादित करता है।
खोल आदेशों के तीन प्रकार की अनुमति देता है। सबसे पहले, एक कमांड एक निष्पादन योग्य फ़ाइल हो सकती है जिसमें स्रोत कोड (उदाहरण के लिए एक सी प्रोग्राम) के संकलन द्वारा उत्पादित ऑब्जेक्ट कोड होता है । दूसरा, एक कमांड एक निष्पादन योग्य फ़ाइल हो सकती है जिसमें शेल कमांड लाइनों का एक अनुक्रम होता है। अंत में, एक कमांड एक आंतरिक शेल कमांड हो सकती है। (निष्पादन योग्य फ़ाइल के बजाय पूर्व-> सीडी , एलएस आदि)।