क्या है exec()
कार्य और उसका परिवार ? इस फ़ंक्शन का उपयोग क्यों किया जाता है और इसका काम कैसे होता है?
कृपया किसी को भी इन कार्यों की व्याख्या करें।
क्या है exec()
कार्य और उसका परिवार ? इस फ़ंक्शन का उपयोग क्यों किया जाता है और इसका काम कैसे होता है?
कृपया किसी को भी इन कार्यों की व्याख्या करें।
जवाबों:
सरल रूप से, UNIX में, आपके पास प्रक्रियाओं और कार्यक्रमों की अवधारणा है। एक प्रक्रिया एक वातावरण है जिसमें एक कार्यक्रम निष्पादित होता है।
UNIX "निष्पादन मॉडल" के पीछे सरल विचार यह है कि आपके द्वारा किए जाने वाले दो ऑपरेशन हैं।
पहला है fork()
, जो अपने राज्य सहित मौजूदा कार्यक्रम के डुप्लिकेट (ज्यादातर) युक्त एक नई प्रक्रिया बनाता है। दो प्रक्रियाओं के बीच कुछ अंतर हैं जो उन्हें यह पता लगाने की अनुमति देते हैं कि कौन सा माता-पिता है और कौन सा बच्चा है।
दूसरा है exec()
, जो वर्तमान प्रक्रिया में कार्यक्रम को एक नए कार्यक्रम के साथ बदल देता है।
उन दो सरल कार्यों से, पूरे यूनिक्स निष्पादन मॉडल का निर्माण किया जा सकता है।
उपरोक्त कुछ और विवरण जोड़ने के लिए:
यूनिक्स की भावना का उपयोग fork()
और exec()
इसमें छूट है कि यह नई प्रक्रियाओं को शुरू करने के लिए एक बहुत ही सरल तरीका प्रदान करता है।
fork()
कॉल मौजूदा प्रक्रिया के एक के पास डुप्लीकेट, लगभग हर तरह से (नहीं में समान बनाता है सब कुछ खत्म हो गया नकल की जाती है, उदाहरण के लिए, कुछ कार्यान्वयन में संसाधन सीमा है, लेकिन विचार के रूप में करीब संभव के रूप में प्रतिलिपि बनाने के लिए है)। केवल एक प्रक्रिया कॉल fork()
लेकिन दो प्रक्रियाएं उस कॉल से लौटती हैं - विचित्र लगती हैं लेकिन यह वास्तव में काफी सुरुचिपूर्ण है
नई प्रक्रिया (जिसे बच्चा कहा जाता है) को एक अलग प्रक्रिया आईडी (पीआईडी) मिलती है और उसके माता-पिता पीआईडी (पीपीआईडी) के रूप में पुरानी प्रक्रिया (माता-पिता) की पीआईडी होती है।
क्योंकि दो प्रक्रियाएं अब एक ही कोड के साथ चल रही हैं, उन्हें यह बताने में सक्षम होना चाहिए कि कौन सा है - fork()
यह जानकारी प्रदान करने का रिटर्न कोड - बच्चे को 0 मिलता है, अभिभावक को बच्चे का पीआईडी मिलता है (यदिfork()
असफल हो, तो नहीं बच्चा बनाया जाता है और माता-पिता को एक त्रुटि कोड मिलता है)।
इस तरह, माता-पिता बच्चे के पीआईडी को जानते हैं और इसके साथ संवाद कर सकते हैं, इसे मार सकते हैं, इसके लिए इंतजार कर सकते हैं और इसी तरह (बच्चा हमेशा कॉल के साथ अपनी मूल प्रक्रिया पा सकता है getppid()
)।
exec()
कॉल एक नए कार्यक्रम के साथ प्रक्रिया के पूरे वर्तमान सामग्री को बदल देता। यह कार्यक्रम को वर्तमान प्रक्रिया स्थान में लोड करता है और इसे प्रवेश बिंदु से चलाता है।
तो, fork()
और exec()
वर्तमान प्रक्रिया के एक बच्चे के रूप में चल रहे एक नए कार्यक्रम को प्राप्त करने के लिए अक्सर अनुक्रम में उपयोग किया जाता है। शैल आमतौर पर ऐसा करते हैं जब भी आप किसी प्रोग्राम को चलाने की कोशिश करते हैं जैसे find
- शेल फॉर्क्स, तो बच्चा find
प्रोग्राम को मेमोरी में लोड करता है , सभी कमांड लाइन तर्क, मानक I / O और इसके आगे की स्थापना करता है।
लेकिन उन्हें एक साथ उपयोग करने की आवश्यकता नहीं है। यह एक कार्यक्रम के लिए fork()
निम्नलिखित के बिना कॉल करने के लिए पूरी तरह से स्वीकार्य है exec()
यदि, उदाहरण के लिए, कार्यक्रम में माता-पिता और बच्चे के कोड दोनों शामिल हैं (आपको सावधान रहना होगा कि आप क्या करते हैं, प्रत्येक कार्यान्वयन में प्रतिबंध हो सकते हैं)।
यह डेमों के लिए काफी उपयोग किया जाता था (और अभी भी है) जो केवल एक टीसीपी पोर्ट पर सुनते हैं और एक विशिष्ट अनुरोध को संसाधित करने के लिए खुद की एक प्रति खरीदते हैं, जबकि माता-पिता वापस सुनने के लिए जाते हैं। इस स्थिति के लिए, कार्यक्रम में माता - पिता और बाल कोड दोनों शामिल हैं ।
इसी तरह, ऐसे कार्यक्रम जो जानते हैं कि वे समाप्त हो चुके हैं और बस एक और कार्यक्रम चलाना चाहते हैं fork()
, exec()
और फिर wait()/waitpid()
बच्चे के लिए नहीं। वे सिर्फ बच्चे को सीधे अपनी वर्तमान प्रक्रिया में लोड कर सकते हैं exec()
।
कुछ UNIX कार्यान्वयन में एक अनुकूलित होता है fork()
जो कॉपी-ऑन-राइट को कॉल करता है। यह fork()
प्रोग्राम में उस स्थान पर कुछ बदलने की कोशिश करने तक प्रक्रिया स्थान की प्रतिलिपि बनाने में देरी करने की एक चाल है। यह केवल उन्हीं कार्यक्रमों का उपयोग करने के लिए उपयोगी है, fork()
न exec()
कि इसमें उन्हें एक संपूर्ण प्रक्रिया स्थान की प्रतिलिपि बनाने की आवश्यकता नहीं है। लिनक्स के तहत, fork()
केवल पृष्ठ तालिकाओं और एक नई कार्य संरचना की एक प्रति बनाता है exec()
, दो प्रक्रियाओं की स्मृति को "अलग" करने का गंभीर काम करेगा।
अगर exec
है निम्नलिखित कहा जाता fork
है (और यह क्या ज्यादातर होता है), उस प्रक्रिया को अंतरिक्ष के लिए एक लिखने का कारण बनता है और यह तब है, बच्चे की प्रक्रिया के लिए नकल की जाती है संशोधनों की अनुमति है पहले।
लिनक्स में एक vfork()
और भी अधिक अनुकूलित है, जो दो प्रक्रियाओं के बीच हर चीज के बारे में साझा करता है। इस कारण से, वहाँ बच्चे क्या कर सकते हैं में कुछ प्रतिबंध, और बच्चे कॉल जब तक माता-पिता हाल्ट हैं exec()
या _exit()
।
माता-पिता को रोकना होगा (और बच्चे को वर्तमान फ़ंक्शन से वापस जाने की अनुमति नहीं है) क्योंकि दो प्रक्रियाएं समान स्टैक को साझा करती हैं। यह fork()
तुरंत बाद के क्लासिक उपयोग के मामले के लिए थोड़ा अधिक कुशल है exec()
।
नोट के एक पूरे परिवार है कि वहाँ 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
exec
वर्तमान प्रक्रिया के IO को पुनर्निर्देशित करने के लिए उपयोगिता का उपयोग क्यों किया जाता है? बिना तर्क के चल रहे "नल" मामले को इस अधिवेशन के लिए कैसे इस्तेमाल किया गया?
exec
इस प्रक्रिया में वर्तमान प्रोग्राम (शेल) को दूसरे के साथ बदलने के साधन के रूप में सोचते हैं , तो यह निर्दिष्ट न करें कि इसे बदलने के लिए अन्य प्रोग्राम का अर्थ केवल यह हो सकता है कि आप इसे बदलना नहीं चाहते हैं।
exec
बिना किसी प्रोग्राम के बुलाए जाने के पतित मामले में बचे हुए इस व्यवहार को देख सकता हूं । लेकिन एक नए कार्यक्रम के लिए पुनर्निर्देशन की मूल उपयोगिता के बाद से इस परिदृश्य में यह थोड़ा अजीब है - एक ऐसा कार्यक्रम जो वास्तव में exec
मिट जाएगा - गायब हो जाता है और आपके पास एक उपयोगी कलाकृति है, जो वर्तमान कार्यक्रम को पुनर्निर्देशित कर रही है - जिसका उपयोग exec
या शुरुआत नहीं की जा रही है किसी भी तरह से - बजाय।
निष्पादन में कार्य () परिवार के विभिन्न व्यवहार हैं:
आप उन्हें मिला सकते हैं, इसलिए आपके पास है:
उन सभी के लिए प्रारंभिक तर्क एक फ़ाइल का नाम है जिसे निष्पादित किया जाना है।
अधिक जानकारी के लिए अमल पढ़ें (3) मैन पेज :
man 3 exec # if you are running a UNIX system
execve()
अपनी सूची से चूक गए , जिसे पोसिक्स द्वारा परिभाषित किया गया है, और आपने जोड़ा execvpe()
, जो कि पॉसिक्स द्वारा परिभाषित नहीं है (ज्यादातर ऐतिहासिक मिसाल के कारणों के लिए; यह कार्यों के सेट को पूरा करता है)। अन्यथा, परिवार के लिए नामकरण सम्मेलन की एक सहायक व्याख्या - पैक्सिडाब्लो का एक उपयोगी सहायक 'एक जवाब जो कार्यों के कामकाज के बारे में अधिक बताता है।
execvpe()
(एट अल) के लिए लिनक्स मैन पेज सूचीबद्ध नहीं है execve()
; इसका अपना एक अलग मैन पेज है (कम से कम उबंटू 16.04 एलटीएस पर) - यह अंतर यह है कि अन्य exec()
पारिवारिक कार्यों को खंड 3 (फ़ंक्शन) execve()
में सूचीबद्ध किया गया है जबकि खंड 2 (सिस्टम कॉल) में सूचीबद्ध किया गया है। मूल रूप से, परिवार में अन्य सभी कार्यों को एक कॉल के संदर्भ में लागू किया जाता है execve()
।
exec
कार्यों का परिवार अपनी प्रक्रिया एक अलग कार्यक्रम क्रियान्वित, पुराने कार्यक्रम यह चल रहा था की जगह बनाते हैं। यानी, अगर आप फोन करते हैं
execl("/bin/ls", "ls", NULL);
फिर ls
प्रोग्राम को प्रक्रिया आईडी, वर्तमान कामकाजी डीआईआर और प्रक्रिया के उपयोगकर्ता / समूह (एक्सेस अधिकार) के साथ निष्पादित किया जाता हैexecl
। बाद में, मूल कार्यक्रम अब नहीं चल रहा है।
एक नई प्रक्रिया शुरू करने के लिए, fork
सिस्टम कॉल का उपयोग किया जाता है। मूल की जगह के बिना एक कार्यक्रम को निष्पादित करने के लिए, आपको fork
तब की आवश्यकता है exec
।
निष्पादन समारोह और उसके परिवार क्या है।
exec
समारोह परिवार के रूप में एक फ़ाइल निष्पादित करने के लिए उपयोग किए गए सभी काम करता है, है execl
, execlp
, execle
, execv
, और execvp
.वे के लिए सभी फ़्रंटएंड्स हैं execve
और यह बुला के विभिन्न तरीकों प्रदान करते हैं।
इस फ़ंक्शन का उपयोग क्यों किया जाता है
जब आप किसी फ़ाइल (प्रोग्राम) को निष्पादित (लॉन्च) करना चाहते हैं तो एक्सक फंक्शन का उपयोग किया जाता है।
और यह कैसे काम करता है।
वे आपके द्वारा लॉन्च की गई वर्तमान प्रक्रिया की छवि को ओवरराइट करके काम करते हैं। वे वर्तमान में चल रही प्रक्रिया को समाप्त करने (समाप्त करने के लिए) (जिसे निष्पादन कमांड कहा जाता है) को लॉन्च की गई नई प्रक्रिया के साथ बदल देते हैं।
अधिक जानकारी के लिए: इस लिंक को देखें ।
exec
अक्सर के साथ संयोजन के रूप में प्रयोग किया जाता है fork
, जिसे मैंने देखा कि आपने भी इसके बारे में पूछा था, इसलिए मैं इस बारे में ध्यान से चर्चा करूंगा।
exec
वर्तमान प्रक्रिया को एक अन्य कार्यक्रम में बदल देता है। यदि आपने कभी डॉक्टर कौन देखा, तो यह तब होता है जब वह पुनर्जीवित होता है - उसके पुराने शरीर को एक नए शरीर के साथ बदल दिया जाता है।
जिस तरह से यह आपके प्रोग्राम के साथ होता है और exec
यह है कि बहुत सारे संसाधन जो ओएस कर्नेल यह देखने के लिए जांचते हैं कि क्या आप जिस फ़ाइल exec
को प्रोग्राम तर्क (प्रथम तर्क) के रूप में पास कर रहे हैं वह मौजूदा उपयोगकर्ता (प्रक्रिया के उपयोगकर्ता आईडी) द्वारा निष्पादन योग्य है बनाने exec
कॉल) और यदि ऐसा है तो यह नई प्रक्रिया और प्रतियां एक आभासी स्मृति के साथ मौजूदा प्रक्रिया के आभासी स्मृति मानचित्रण बदल देता है argv
और envp
डेटा है कि में पारित किए गए exec
कॉल इस नए आभासी स्मृति नक्शे के किसी क्षेत्र में। कई अन्य चीजें भी यहां हो सकती हैं, लेकिन जो फाइलें प्रोग्राम के लिए खुली थीं, जिन्हें कॉल exec
किया गया है, नए प्रोग्राम के लिए अभी भी खुली होंगी और वे उसी प्रोसेस आईडी को साझा करेंगी, लेकिन जो प्रोग्राम कहा जाता है exec
वह बंद हो जाएगा (जब तक कि फेल नहीं हो जाता )।
कारण यह है कि इस इस तरह से किया जाता है कि अलग करके है चल रहा है एक नया कार्यक्रम इस तरह दो चरणों में आप दो कदम के बीच कुछ कर सकते हैं। सबसे आम बात यह है कि यह सुनिश्चित करें कि नए प्रोग्राम में कुछ फाइल डिस्क्रिप्टर के रूप में खोली गई हैं। (यहां याद रखें कि फ़ाइल डिस्क्रिप्टर समान नहीं हैं FILE *
, लेकिन ऐसे int
मान हैं जिनके बारे में कर्नेल को पता है)। आप यह कर सकते हैं:
int X = open("./output_file.txt", O_WRONLY);
pid_t fk = fork();
if (!fk) { /* in child */
dup2(X, 1); /* fd 1 is standard output,
so this makes standard out refer to the same file as X */
close(X);
/* I'm using execl here rather than exec because
it's easier to type the arguments. */
execl("/bin/echo", "/bin/echo", "hello world");
_exit(127); /* should not get here */
} else if (fk == -1) {
/* An error happened and you should do something about it. */
perror("fork"); /* print an error message */
}
close(X); /* The parent doesn't need this anymore */
यह चल रहा है:
/bin/echo "hello world" > ./output_file.txt
कमांड शेल से।
जब कोई प्रक्रिया कांटा () का उपयोग करती है, तो यह खुद की एक डुप्लिकेट कॉपी बनाता है और यह डुप्लिकेट प्रक्रिया का बच्चा बन जाता है। कांटा () क्लोन में () सिस्टम कॉल का उपयोग करके कार्यान्वित किया जाता है जो कर्नेल से दो बार लौटता है।
आइए इसे एक उदाहरण से समझते हैं:
pid = fork();
// Both child and parent will now start execution from here.
if(pid < 0) {
//child was not created successfully
return 1;
}
else if(pid == 0) {
// This is the child process
// Child process code goes here
}
else {
// Parent process code goes here
}
printf("This is code common to parent and child");
उदाहरण में, हमने मान लिया है कि निष्पादन () का उपयोग बाल प्रक्रिया के अंदर नहीं किया जाता है।
लेकिन एक अभिभावक और बच्चा पीसीबी (प्रोसेस कंट्रोल ब्लॉक) विशेषताओं में से कुछ में भिन्न होता है। य़े हैं:
लेकिन बच्चे की याददाश्त का क्या? क्या एक बच्चे के लिए एक नया पता स्थान बनाया गया है?
जवाब नहीं में। कांटा () के बाद, माता-पिता और बच्चे दोनों माता-पिता के मेमोरी एड्रेस स्पेस को साझा करते हैं। लिनक्स में, इन एड्रेस स्पेस को कई पेजों में विभाजित किया गया है। जब बच्चा माता-पिता के किसी एक मेमोरी पेज पर लिखता है, तो बच्चे के लिए उस पेज का एक डुप्लिकेट बनाया जाता है। इसे लिखने पर कॉपी के रूप में भी जाना जाता है (माता-पिता को केवल तभी कॉपी करें जब बच्चा इसे लिखता है)।
आइए कॉपी पर एक उदाहरण के साथ लिखें।
int x = 2;
pid = fork();
if(pid == 0) {
x = 10;
// child is changing the value of x or writing to a page
// One of the parent stack page will contain this local variable. That page will be duplicated for child and it will store the value 10 in x in duplicated page.
}
else {
x = 4;
}
लेकिन कॉपी पर लिखना आवश्यक क्यों है?
कांटा () - निष्पादन () संयोजन के माध्यम से एक विशिष्ट प्रक्रिया निर्माण होता है। आइए पहले समझते हैं कि निष्पादन () क्या करता है।
एक्ज़ेक () फ़ंक्शन का समूह एक नए कार्यक्रम के साथ बच्चे के पते की जगह की जगह लेता है। एक बार निष्पादन () को एक बच्चे के भीतर कहा जाता है, बच्चे के लिए एक अलग पता स्थान बनाया जाएगा जो कि माता-पिता से बिल्कुल अलग है।
यदि fork () से जुड़े लेखन तंत्र पर कोई प्रतिलिपि नहीं होती, तो बच्चे के लिए डुप्लिकेट पृष्ठ बनाए जाते और सभी डेटा बच्चे के पृष्ठों पर कॉपी किए गए होते। नई मेमोरी आवंटित करना और डेटा कॉपी करना एक बहुत महंगी प्रक्रिया है (प्रोसेसर के समय और अन्य सिस्टम संसाधन लेता है)। हम यह भी जानते हैं कि ज्यादातर मामलों में, बच्चा निष्पादन () कॉल करने जा रहा है और वह बच्चे की याददाश्त को एक नए कार्यक्रम के साथ बदल देगा। इसलिए जो पहली कॉपी हमने की थी वह बेकार होती अगर लिखने पर कॉपी नहीं होती।
pid = fork();
if(pid == 0) {
execlp("/bin/ls","ls",NULL);
printf("will this line be printed"); // Think about it
// A new memory space will be created for the child and that memory will contain the "/bin/ls" program(text section), it's stack, data section and heap section
else {
wait(NULL);
// parent is waiting for the child. Once child terminates, parent will get its exit status and can then continue
}
return 1; // Both child and parent will exit with status code 1.
माता-पिता बच्चे की प्रक्रिया का इंतजार क्यों करते हैं?
निष्पादन () सिस्टम कॉल आवश्यक क्यों है?
कांटा () के साथ निष्पादन () का उपयोग करना आवश्यक नहीं है। यदि बच्चा जिस कोड को निष्पादित करेगा, वह माता-पिता से जुड़े कार्यक्रम के भीतर है, तो निष्पादन () की आवश्यकता नहीं है।
लेकिन उन मामलों के बारे में सोचें जब बच्चे को कई कार्यक्रम चलाने होते हैं। आइए शेल प्रोग्राम का उदाहरण लेते हैं। यह खोज, mv, cp, दिनांक आदि जैसे कई आदेशों का समर्थन करता है। क्या इन कार्यक्रमों से जुड़े प्रोग्राम कोड को एक प्रोग्राम में शामिल करना सही होगा या आवश्यकता पड़ने पर इन कार्यक्रमों को बच्चे को मेमोरी में लोड करना होगा?
यह सब आपके उपयोग के मामले पर निर्भर करता है। आपके पास एक वेब सर्वर है जिसने एक इनपुट x दिया है जो क्लाइंट को 2 ^ x लौटाता है। प्रत्येक अनुरोध के लिए, वेब सर्वर एक नया बच्चा बनाता है और इसे गणना करने के लिए कहता है। क्या आप इसकी गणना करने और निष्पादन () का उपयोग करने के लिए एक अलग कार्यक्रम लिखेंगे? या आप मूल कार्यक्रम के अंदर सिर्फ कम्प्यूटेशन कोड लिखेंगे?
आमतौर पर, एक प्रक्रिया निर्माण में कांटा (), निष्पादन (), प्रतीक्षा () और निकास () कॉल का संयोजन शामिल होता है।
exec(3,3p)
कार्यों की जगह दूसरे के साथ मौजूदा प्रक्रिया। यही है, वर्तमान कार्यक्रम बंद हो जाता है , और इसके बजाय एक और रन होता है, जो मूल कार्यक्रम के कुछ संसाधनों को ले रहा था।