2009-10-18 11 views
10

मैं नियमित रूप से काम करते हैं:फोर्क() के बाद execvp (...) त्रुटियों को कैसे संभालें?

  • कांटा()
  • execvp (cmd,) बच्चे में

तो execvp विफल रहता है, क्योंकि कोई cmd पाया जाता है, मैं कैसे माता पिता में इस त्रुटि देख सकते हैं प्रक्रिया?

+0

नहीं, क्योंकि फोर्कड बच्चा अपनी खुद की पता स्थान और इरनो की अपनी स्वतंत्र प्रति के साथ एक अलग प्रक्रिया है। माता-पिता बच्चे की इरनो नहीं देख सकते हैं। –

+0

उस बेवकूफ टिप्पणी को हटा दिया गया। धन्यवाद एंड्रयू मेडिको और रो। – dirkgently

+0

क्या यह कुछ साधारण आईपीसी के लिए नौकरी नहीं है? ऐसा करने से आपको बाहर निकलने की स्थिति के 8-बिट से अधिक प्रदान किया जाता है। और आप यह भी सुनिश्चित कर सकते हैं कि आपकी execv विफल हो गई है, क्योंकि विफल प्रक्रिया के विपरीत विफल रहा है। मुझे लगता है कि यह इस बात पर निर्भर करता है कि आप अपने बच्चे के बारे में कितना ख्याल रखते हैं वास्तव में फायरिंग नहीं करते हैं। – mrduclaw

उत्तर

15

इस उद्देश्य के लिए प्रसिद्ध self-pipe trickadapted हो सकता है।

#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <string.h> 
#include <sys/wait.h> 
#include <sysexits.h> 
#include <unistd.h> 

int main(int argc, char **argv) { 
    int pipefds[2]; 
    int count, err; 
    pid_t child; 

    if (pipe(pipefds)) { 
     perror("pipe"); 
     return EX_OSERR; 
    } 
    if (fcntl(pipefds[1], F_SETFD, fcntl(pipefds[1], F_GETFD) | FD_CLOEXEC)) { 
     perror("fcntl"); 
     return EX_OSERR; 
    } 

    switch (child = fork()) { 
    case -1: 
     perror("fork"); 
     return EX_OSERR; 
    case 0: 
     close(pipefds[0]); 
     execvp(argv[1], argv + 1); 
     write(pipefds[1], &errno, sizeof(int)); 
     _exit(0); 
    default: 
     close(pipefds[1]); 
     while ((count = read(pipefds[0], &err, sizeof(errno))) == -1) 
      if (errno != EAGAIN && errno != EINTR) break; 
     if (count) { 
      fprintf(stderr, "child's execvp: %s\n", strerror(err)); 
      return EX_UNAVAILABLE; 
     } 
     close(pipefds[0]); 
     puts("waiting for child..."); 
     while (waitpid(child, &err, 0) == -1) 
      if (errno != EINTR) { 
       perror("waitpid"); 
       return EX_SOFTWARE; 
      } 
     if (WIFEXITED(err)) 
      printf("child exited with %d\n", WEXITSTATUS(err)); 
     else if (WIFSIGNALED(err)) 
      printf("child killed by %d\n", WTERMSIG(err)); 
    } 
    return err; 
} 

यहां एक पूरा कार्यक्रम है।

 
$ ./a.out foo 
child's execvp: No such file or directory 
$ (sleep 1 && killall -QUIT sleep &); ./a.out sleep 60 
waiting for child... 
child killed by 3 
$ ./a.out true 
waiting for child... 
child exited with 0 

यह कैसे काम करता है:

एक पाइप बनाएँ, और लिखने endpoint CLOEXEC बनाने: यह ऑटो बंद कर देता है जब एक exec सफलतापूर्वक किया जाता है।

बच्चे में, exec पर आज़माएं। यदि यह सफल होता है, तो हमारे पास अब नियंत्रण नहीं है, लेकिन पाइप बंद है। यदि यह विफल हो जाता है, तो पाइप में विफलता कोड लिखें और बाहर निकलें।

माता-पिता में, अन्य पाइप एंडपॉइंट से पढ़ने का प्रयास करें। यदि read शून्य लौटाता है, तो पाइप बंद कर दिया गया था और बच्चे को सफलतापूर्वक exec होना चाहिए। यदि read डेटा लौटाता है, तो यह हमारे बच्चे द्वारा लिखे गए विफलता कोड है।

+2

यह समाधान, काफी सरल, चट्टानों। – caf

+0

स्विच स्टेटमेंट के अंदर ब्रांड्स का एक अतिरिक्त सेट होना चाहिए, यह सुनिश्चित करने के लिए कि यह एक असाइनमेंट स्टेटमेंट – tay10r

+0

@TaylorFlores के रूप में है ??? संकलक सिंटैक्स को ठीक समझता है। कई कंपाइलर्स * चेतावनी * को 'if' के अंदर एक नंगे असाइनमेंट पर चेतावनी देते हैं, क्योंकि समानता जांच अधिक आम होती है, लेकिन 'स्विच' में ऐसा करने का कोई कारण नहीं है। – ephemient

6

आप बच्चे को समाप्त करते हैं (_exit() पर कॉल करके) और फिर माता-पिता इसे देख सकते हैं (उदाहरण के लिए waitpid())। उदाहरण के लिए, आपका बच्चा निष्पादित करने में विफलता को इंगित करने के लिए -1 की निकास स्थिति से बाहर निकल सकता है। इसके साथ एक चेतावनी यह है कि अपने माता-पिता से यह कहना असंभव है कि क्या बच्चा अपनी मूल स्थिति में (यानी निष्पादन से पहले) वापस लौटा है या यदि यह नई निष्पादित प्रक्रिया थी।

जैसा कि नीचे दी गई टिप्पणियों में सुझाव दिया गया है, "असामान्य" रिटर्न कोड का उपयोग करके आपकी विशिष्ट त्रुटि और निष्पादन() 'ed प्रोग्राम से एक को अलग करना आसान बनाने के लिए अपरिपक्व होगा। आम 1, 2, 3 इत्यादि हैं जबकि उच्च संख्या 99, 100, आदि अधिक असामान्य हैं। पोर्टेबिलिटी बढ़ाने के लिए आपको अपनी संख्या 255 (हस्ताक्षरित) या 127 (हस्ताक्षरित) से नीचे रखना चाहिए।

चूंकि प्रतीक्षापिड आपके एप्लिकेशन को ब्लॉक करता है (या बल्कि, इसे थ्रेड कॉल करने के बाद) आपको या तो इसे पृष्ठभूमि थ्रेड पर रखना होगा या पीओएसईक्स में सिग्नलिंग तंत्र का उपयोग बाल प्रक्रिया समाप्ति के बारे में जानकारी प्राप्त करने के लिए करना होगा। श्रोता को हुक करने के लिए SIGCHLD सिग्नल और sigaction फ़ंक्शन देखें।

आप फोर्किंग से पहले कुछ त्रुटि जांच भी कर सकते हैं, जैसे निष्पादन योग्य मौजूद है।

यदि आप Glib जैसे कुछ का उपयोग करते हैं, तो ऐसा करने के लिए उपयोगिता कार्य हैं, और वे बहुत अच्छी त्रुटि रिपोर्टिंग के साथ आते हैं। मैनुअल के "spawning processes" खंड पर एक नज़र डालें।

+0

बाहर निकलने की स्थिति 8 बिट हस्ताक्षरित नहीं है? (-1 -> 255) – falstro

+1

यह डेटा के केवल 8 बिट्स है। इससे कोई फर्क नहीं पड़ता कि आप उन्हें हस्ताक्षरित हस्ताक्षर के रूप में समझते हैं या नहीं। (बस सुसंगत रहें) मुझे यकीन नहीं है कि पॉज़िक्स मानक क्या कहता है, लेकिन त्रुटि –

+0

त्रुटि इंगित करने के लिए मुख्य मान से नकारात्मक मान वापस करना आम बात है() मेरी मूल प्रक्रिया को अवरुद्ध नहीं करेगा? –

0

ठीक है, आप पेरेंट प्रक्रिया में wait/waitpid फ़ंक्शंस का उपयोग कर सकते हैं। आप status वेरिएबल निर्दिष्ट कर सकते हैं जो कि समाप्त होने वाली प्रक्रिया की स्थिति के बारे में जानकारी रखता है। नकारात्मकता यह है कि जब तक बच्चे की प्रक्रिया निष्पादन समाप्त नहीं हो जाती तब तक माता-पिता की प्रक्रिया अवरुद्ध होती है।

+3

अपडेट की गई है अगर आप निष्पादन को अवरुद्ध नहीं करना चाहते हैं तो सिग्चल्ड पर एक नज़र डालें। – falstro

1

नहीं आपको आश्चर्य चाहिए कि कैसे आप कर सकते हैं माता पिता की प्रक्रिया में यह नोटिस, लेकिन यह भी ध्यान में रखना चाहिए कि आप चाहिए नोटिस माता पिता की प्रक्रिया में त्रुटि। यह बहुप्रचारित अनुप्रयोगों के लिए विशेष रूप से सच है।

execvp के बाद किसी भी मामले में प्रक्रिया को समाप्त करने वाले फ़ंक्शन पर कॉल करें। आपको किसी भी जटिल कार्य को कॉल नहीं करना चाहिए जो सी लाइब्रेरी (जैसे कि stdio) से सहभागिता करता है, क्योंकि उनके प्रभाव मूल प्रक्रिया की libc कार्यक्षमता के pthreads के साथ मिलकर मिल सकते हैं। तो आप बच्चे की प्रक्रिया में printf() के साथ एक संदेश मुद्रित नहीं कर सकते हैं और इसके बजाय माता-पिता को त्रुटि के बारे में सूचित करना होगा।

दूसरा, सबसे आसान तरीका, रिटर्न कोड पास कर रहा है। _exit() फ़ंक्शन (नीचे नोट देखें) पर nonzero तर्क की आपूर्ति करें, आप बच्चे को समाप्त करने के लिए उपयोग करते हैं और फिर माता-पिता में रिटर्न कोड की जांच करते हैं।यहाँ उदाहरण है:

int pid, stat; 
pid = fork(); 
if (pid == 0){ 
    // Child process 
    execvp(cmd); 
    if (errno == ENOENT) 
    _exit(-1); 
    _exit(-2); 
} 

wait(&stat); 
if (!WIFEXITED(stat)) { // Error happened 
... 
} 

_exit() के बजाय

, आप exit() समारोह के बारे में सोच सकता है, लेकिन यह गलत है, के बाद से इस समारोह सी पुस्तकालय सफाई का एक हिस्सा है कि केवल जब माता-पिता से किया जाना चाहिए क्या करेंगे प्रक्रिया समाप्त हो जाती है। इसके बजाय, _exit() फ़ंक्शन का उपयोग करें, जो ऐसा क्लीनअप नहीं करता है।

+1

कांटा के बाद निकास() का उपयोग न करें, _exit() का उपयोग करें। –

+0

@ डगलस लीडर, इस विशाल दोष को इंगित करने के लिए धन्यवाद। मैंने पोस्ट तय किया। –

1

1) का प्रयोग करें _exit() नहीं exit() - देख http://opengroup.org/onlinepubs/007908775/xsh/vfork.html - एनबी: fork() के साथ-साथ vfork() पर लागू होता है।

2) बाहर निकलने की स्थिति की तुलना में अधिक जटिल आईपीसी करने में समस्या यह है कि आपके पास एक साझा मेमोरी मैप है, और यदि आप कुछ भी जटिल करते हैं तो कुछ बुरा राज्य प्राप्त करना संभव है - उदा। मल्टीथ्रेड कोड में, मारे गए धागे (बच्चे में) में से एक लॉक हो सकता था।

0

किसी भी समय निष्पादन एक उपप्रोसेसर में विफल रहता है, तो आपको मार (getpid(), सिगकिल) का उपयोग करना चाहिए और माता-पिता को हमेशा एसआईजीसीएलडी के लिए सिग्नल हैंडलर होना चाहिए और कार्यक्रम के उपयोगकर्ता को उचित तरीके से बताएं कि प्रक्रिया थी सफलतापूर्वक शुरू नहीं हुआ।

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