2012-12-15 8 views
8

पर बाल आउटपुट पाइपिंग मैं प्रक्रिया प्रक्रिया के साथ संघर्ष कर रहा हूं और बाल प्रक्रिया 'आउटपुट को पैरेंट प्रक्रिया की एक स्ट्रिंग में पाइप कर रहा हूं। मुझे विंडोज़ पर काम करना पड़ा (CreatePipe और CreateProcess और ReadFile का उपयोग करके), लेकिन यूनिक्स पर काम करने के लिए सटीक एनालॉग नहीं लग रहा है।posix_spawnp और एक स्ट्रिंग

#include <spawn.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/wait.h> 
#include <iostream> 
#include <string> 
#include <vector> 
using namespace std; 

int main() 
{ 
    int exit_code; 
    int cout_pipe[2]; 
    int cerr_pipe[2]; 
    posix_spawn_file_actions_t action; 

    if(pipe(cout_pipe) || pipe(cerr_pipe)) 
    cout << "pipe returned an error.\n"; 

    posix_spawn_file_actions_init(&action); 
    posix_spawn_file_actions_addclose(&action, cout_pipe[0]); 
    posix_spawn_file_actions_addclose(&action, cerr_pipe[0]); 
    posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1); 
    posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2); 

    posix_spawn_file_actions_addclose(&action, cout_pipe[1]); 
    posix_spawn_file_actions_addclose(&action, cerr_pipe[1]); 

    vector<string> argmem = {"bla"}; 
    vector<char*> args = {&argmem[0][0], nullptr}; // I don't want to call new. 

    pid_t pid; 
    if(posix_spawnp(&pid, "echo", &action, NULL, &args[0], NULL) != 0) 
    cout << "posix_spawnp failed with error: " << strerror(errno) << "\n"; 
    //close(cout_pipe[0]); 
    //close(cerr_pipe[0]); 

    close(cout_pipe[1]); 
    close(cerr_pipe[1]); 

    waitpid(pid,&exit_code,0); 
    cout << "exit code: " << exit_code << "\n"; 

    // Read from pipes 
    const size_t buffer_size = 1024; 
    string buffer; 
    buffer.resize(buffer_size); 
    ssize_t bytes_read = read(cout_pipe[0], &buffer[0], buffer_size); 
    while ((bytes_read = read(cout_pipe[0], &buffer[0], buffer_size)) > 0) 
    { 
    cout << "read " << bytes_read << " bytes from stdout.\n"; 
    cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n"; 
    bytes_read = read(cout_pipe[0], &buffer[0], buffer_size); 
    } 
    if(bytes_read == -1) 
    cout << "Failure reading from stdout pipe.\n"; 
    while ((bytes_read = read(cerr_pipe[0], &buffer[0], buffer_size)) > 0) 
    { 
    cout << "read " << bytes_read << " bytes from stderr.\n"; 
    cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n"; 
    bytes_read = read(cout_pipe[0], &buffer[0], buffer_size); 
    } 
    if(bytes_read == -1) 
    cout << "Failure reading from stderr pipe.\n"; 

    posix_spawn_file_actions_destroy(&action); 
} 

उत्पादन होता है:

बाहर निकलें कोड: 0

तो मैं सब कुछ वास्तविक पाइपिंग को छोड़कर काम कर रहा है लगता है यह मेरा कोड है। यहाँ क्या गलत है? मुझे यह भी आश्चर्य है कि एक प्रतीक्षापिप लूप में पाइप बाइट्स पढ़ने का कोई तरीका है, लेकिन जब मैं कोशिश करता हूं, तो मूल प्रक्रिया असीम रूप से लटकती है।

+0

मैंने कभी 'posix_spawnp' के बारे में नहीं सुना था। अच्छे पुराने 'पॉपन' के साथ क्या गलत है? यह एक दिलचस्प syscall/पुस्तकालय समारोह क्या है। यह याद रखेगा :) – sehe

+1

'popen' में आवश्यक लचीलापन नहीं है: AFAICT मैं इसके साथ stderr को रीडायरेक्ट नहीं कर सकता। यह वह कार्य भी है जो 'CreateProcess' API जैसा दिखता है। हेक, एमएसडीएन में '_spawnvp' भी है जो 'posix_spawnp' जैसा दिखता है, लेकिन जैसा कि मैंने कहा, मेरा' CreateProcess' कोड मूल रूप से ठीक काम कर रहा है। यह यूनिक्स पक्ष है जो वर्तमान में सहयोग नहीं कर रहा है :( – rubenvb

+2

मैंने रुचि बढ़ाने के लिए सी टैग जोड़ा। हालांकि कोड तकनीकी रूप से सी ++ है, हालांकि महत्वपूर्ण बिट्स (यानी पाइपिंग और स्पॉन्गिंग) शुद्ध सी – rubenvb

उत्तर

13

posix_spawn दिलचस्प और उपयोगी है, जो इस सवाल को necromancing के लायक बनाता है - भले ही यह ओपी के लिए प्रासंगिक नहीं है।

पोस्ट किए गए कोड में कुछ महत्वपूर्ण बग हैं। मुझे लगता है कि इनमें से कुछ हताशा में हैकिंग के परिणाम थे, लेकिन मैं नहीं जानता कि जो मूल बग था:

  1. args सरणी argv[0] कि निष्पादन योग्य नाम का प्रतिनिधित्व करते हैं शामिल नहीं है। इसका परिणाम echo प्रोग्राम में argv[1] ("bla") को कभी भी नहीं देख रहा है।
  2. read() फ़ंक्शन को विभिन्न स्थानों से इस तरह से बुलाया जाता है कि यह समझ में नहीं आता है। ऐसा करने का एक सही तरीका while लूप के लिए नियंत्रण अभिव्यक्ति के हिस्से के रूप में केवल read पर कॉल करना होगा।
  3. waitpid() पाइप से पढ़ने से पहले कहा जाता है। यह I/O को पूरा करने से रोकता है (कम से कम गैर-मामूली मामलों में)।
  4. इस कोड के साथ एक और सूक्ष्म मुद्दा यह है कि stderr से कुछ भी पढ़ने से पहले सभी बच्चे के stdout को पढ़ने का प्रयास किया जाता है। सिद्धांत रूप में, यह stderr पर लिखने का प्रयास करते समय बच्चे को अवरुद्ध कर सकता है, इस प्रकार कार्यक्रम को पूरा करने से रोकता है। इसके लिए एक कुशल समाधान बनाना अधिक जटिल है क्योंकि इसकी आवश्यकता है कि आप जो भी पाइप उपलब्ध डेटा से पढ़ सकें। मैंने इसके लिए poll() का उपयोग किया। एक और दृष्टिकोण एकाधिक धागे का उपयोग करना होगा।

इसके अतिरिक्त, मैंने sh (कमांड खोल, यानी bash) बाल प्रक्रिया के रूप में उपयोग किया है। यह अतिरिक्त लचीलापन का एक बड़ा सौदा प्रदान करता है, जैसे एक निष्पादन योग्य के बजाय पाइपलाइन चलाना। विशेष रूप से, हालांकि, sh का उपयोग कमांड लाइन के पार्सिंग को प्रबंधित करने की सरल सुविधा प्रदान करता है।

/*BINFMTCXX: -std=c++11 -Wall -Werror 
*/ 

#include <spawn.h> // see manpages-posix-dev 
#include <poll.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/wait.h> 
#include <iostream> 
#include <string> 
#include <vector> 
using namespace std; 

int main() 
{ 
    int exit_code; 
    int cout_pipe[2]; 
    int cerr_pipe[2]; 
    posix_spawn_file_actions_t action; 

    if(pipe(cout_pipe) || pipe(cerr_pipe)) 
    cout << "pipe returned an error.\n"; 

    posix_spawn_file_actions_init(&action); 
    posix_spawn_file_actions_addclose(&action, cout_pipe[0]); 
    posix_spawn_file_actions_addclose(&action, cerr_pipe[0]); 
    posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1); 
    posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2); 

    posix_spawn_file_actions_addclose(&action, cout_pipe[1]); 
    posix_spawn_file_actions_addclose(&action, cerr_pipe[1]); 

//string command = "echo bla"; // example #1 
    string command = "pgmcrater -width 64 -height 9 |pgmtopbm |pnmtoplainpnm"; 
    string argsmem[] = {"sh","-c"}; // allows non-const access to literals 
    char * args[] = {&argsmem[0][0],&argsmem[1][0],&command[0],nullptr}; 

    pid_t pid; 
    if(posix_spawnp(&pid, args[0], &action, NULL, &args[0], NULL) != 0) 
    cout << "posix_spawnp failed with error: " << strerror(errno) << "\n"; 

    close(cout_pipe[1]), close(cerr_pipe[1]); // close child-side of pipes 

    // Read from pipes 
    string buffer(1024,' '); 
    std::vector<pollfd> plist = { {cout_pipe[0],POLLIN}, {cerr_pipe[0],POLLIN} }; 
    for (int rval; (rval=poll(&plist[0],plist.size(),/*timeout*/-1))>0;) { 
    if (plist[0].revents&POLLIN) { 
     int bytes_read = read(cout_pipe[0], &buffer[0], buffer.length()); 
     cout << "read " << bytes_read << " bytes from stdout.\n"; 
     cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n"; 
    } 
    else if (plist[1].revents&POLLIN) { 
     int bytes_read = read(cerr_pipe[0], &buffer[0], buffer.length()); 
     cout << "read " << bytes_read << " bytes from stderr.\n"; 
     cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n"; 
    } 
    else break; // nothing left to read 
    } 

    waitpid(pid,&exit_code,0); 
    cout << "exit code: " << exit_code << "\n"; 

    posix_spawn_file_actions_destroy(&action); 
} 
+0

है, मैंने इसे पूरी तरह से बटन नहीं दिया ।बेहतर त्रुटि जांच और संसाधन सफाई के लिए बहुत सारे कमरे हैं। – nobar

+1

आपने केवल तर्कों में क्यों नहीं पारित किया लेकिन [0] posix_spawn() के 5 वें तर्क के लिए तर्क दिया और तर्क दिया? – Dula

+1

@Dula: मेरे द्वारा उपयोग किया जाने वाला फॉर्म अधिक सामान्य और अधिक स्पष्ट है। यह पॉइंटर्स के लिए काम करता है, लेकिन क्लास-आधारित सरणी प्रबंधकों जैसे 'std :: vector' और' std :: string' के लिए भी काम करता है। मैं उस फॉर्म का उपयोग कई स्थानों पर करता हूं, जिसमें '& buffer [0]' शामिल है, जो कि 'बफर' के रूप में काम नहीं करता था। – nobar

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