खोज…


परिचय

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

बुनियादी निर्माण और उपयोग

बेनामी पाइप, या बस पाइप, कर्नेल-प्रबंधित ऑब्जेक्ट हैं जो फ़ाइल डिस्क्रिप्टर की एक जोड़ी के रूप में प्रक्रियाओं के संपर्क में आते हैं, एक पढ़ने के टर्मिनस के लिए और एक लिखने के टर्मिनस के लिए। वे pipe (2) फ़ंक्शन के माध्यम से बनाए जाते हैं:

int pipefds[2];
int result;

result = pipe(pipefds);

सफलता पर, pipe() प्रदान की गई सरणी के इंडेक्स 0 पर पाइप के रीड एंड के लिए डिस्क्रिप्टर रिकॉर्ड करता है, और इंडेक्स 1 पर राइट एंड के लिए डिस्क्रिप्टर; ये सूचकांक मानक धाराओं के लिए पारंपरिक फ़ाइल डिस्क्रिप्टर संख्या के अनुरूप हैं।

पाइप बनाने के बाद, व्यक्ति POSIX I / O फ़ंक्शन का उपयोग लिखने के लिए लिखने के लिए करता है या रीड एंड से पढ़ता है:

प्रक्रिया 1:

ssize_t bytes_written = write(pipefds[1], "Hello, World!", 14);

प्रक्रिया 2:

char buffer[256];
ssize_t bytes_read = read(pipefds[0], buffer, sizeof(buffer));

वैकल्पिक रूप से, एक इसके बजाय सी stdio कार्यों के साथ उपयोग करने के लिए FILE संरचना में दोनों पाइपों में से एक को समाप्त करने के लिए fdopen() का उपयोग कर सकता है:

FILE *write_end = fdopen(pipefds[1]);

if (write_end) {
    fputs("Hello, World!");
}

पाइप्स में परिमित I / O बफ़र्स होते हैं, और फुल बफ़र्स वाले पाइपों को साधारण लिखते हैं। पाइप इसलिए किसी थ्रेड के लिए स्वयं से संवाद करने के लिए एक सुरक्षित तंत्र नहीं है, जैसे कि एक पाइप को लिखने वाला धागा भी केवल वही है जो इससे पढ़ता है, फिर जैसे ही एक राइट ब्लॉक होता है उस थ्रेड का गतिरोध हो जाता है।

एक पाइप तब तक बना रहता है जब तक कि किसी भी प्रक्रिया में पाइप के दोनों छोरों के लिए एक खुली फाइल का विवरण होता है। फ़ाइल डिस्क्रिप्टर द्वारा दर्शाए गए पाइप एंड को बंद करने के लिए close (2) फ़ंक्शन का उपयोग किया जा सकता है, और fclose (3) का उपयोग पाइप के छोर को उसके चारों ओर लिपटे FILE माध्यम से बंद करने के लिए किया जा सकता है।

एक बच्चे की प्रक्रिया के लिए एक पाइप की स्थापना

फ़ाइल डिस्क्रिप्टर और FILE ऑब्जेक्ट्स प्रति-प्रक्रिया संसाधन हैं जो स्वयं को सामान्य I / O के माध्यम से प्रक्रियाओं के बीच आदान-प्रदान नहीं किया जा सकता है। इसलिए, दो अलग-अलग प्रक्रियाओं के लिए एक अनाम पाइप के माध्यम से संवाद करने के लिए, एक या दोनों भाग लेने वाली प्रक्रियाओं को पाइप बनाने वाली प्रक्रिया से एक खुला पाइप अंत प्राप्त करना होगा, जो कि मूल प्रक्रिया या अधिक दूर पूर्वज होना चाहिए। सबसे सरल मामला वह है जिसमें एक माता-पिता एक बच्चे की प्रक्रिया के साथ संवाद करना चाहते हैं।

क्योंकि बच्चे की प्रक्रिया को अपने माता-पिता से आवश्यक खुली फ़ाइल विवरण प्राप्त करना होगा, पहले पाइप बनाना होगा। जनक फिर कांटे लगाता है। आमतौर पर, प्रत्येक प्रक्रिया या तो सख्ती से एक पाठक होगी या कड़ाई से एक लेखक; उस स्थिति में, प्रत्येक को पाइप के छोर को बंद करना चाहिए जो इसे उपयोग करने का इरादा नहीं करता है।

void demo() {
    int pipefds[2];
    pid_t pid;

    // Create the pipe
    if (pipe(pipefds)) {
        // error - abort ...
    }

    switch (pid = fork()) {
        case -1:
            // error - abort ...
            break;
        case 0:   /* child */
            close(pipefds[0]);
            write(pipefds[1], "Goodbye, and thanks for all the fish!", 37);
            exit(0);
        default:  /* parent */
            close(pipefds[1]);

            char buffer[256];
            ssize_t nread = read(pipefds[0], sizeof(buffer) - 1);

            if (nread >= 0) {
                buffer[nread] = '\0';
                printf("My child said '%s'\n", buffer);
            }

            // collect the child
            wait(NULL);

            break;
    }
}

एक पाइप के माध्यम से दो बच्चे प्रक्रियाओं को जोड़ना

एक पाइप के माध्यम से दो बाल प्रक्रियाओं को जोड़ना एक ही पाइप के विभिन्न सिरों के माध्यम से प्रत्येक दो बच्चों को माता-पिता से जोड़कर किया जाता है। आमतौर पर, माता-पिता बच्चों के बीच बातचीत के लिए पार्टी नहीं करेंगे, इसलिए यह दोनों पाइपों की प्रतियां बंद कर देता है।

int demo() {
    int pipefds[2];
    pid_t child1, child2;

    if (pipe(pipefds)) {
        // error - abort ...
    }

    switch (child1 = fork()) {
        case -1:
            // error - abort
            break;
        case 0:   /* child 1 */
            close(pipefds[0]);
            write(pipefds[1], "Hello, brother!", 15);
            exit(0);
        default:  /* parent */
            // nothing
    }

    switch (child1 = fork()) {
        case -1:
            // error - abort
            break;
        case 0:   /* child 2 */
            char buffer[256];
            ssize_t nread;

            close(pipefds[1]);
            nread = read(pipefds[0], buffer, sizeof(buffer) - 1);
            if (nread < 0) {
                // handle error
            } else {
                buffer[nread] = '\0';
                printf("My brother told me '%s'\n", buffer);
            }
            exit(0);
        default:  /* parent */
            // nothing
    }

    // Only the parent reaches this point
    close(pipefds[0]);
    close(pipefds[1]);
    if (child1 >= 0) {
        wait(NULL);
        if (child2 >= 0) {
            wait(NULL);
        }
    }
}

शेल-स्टाइल पाइपलाइन बनाना

शेल-शैली की पाइपलाइन में दो या अधिक प्रक्रियाएं होती हैं, हर एक इसके मानक आउटपुट के साथ अगले के मानक इनपुट से जुड़ा होता है। आउटपुट-टू-इनपुट कनेक्शन पाइप पर बनाए गए हैं। एक पाइपलाइन-जैसी प्रक्रियाओं के सेट को स्थापित करने के लिए, एक पाइप-कनेक्टेड चाइल्ड प्रोसेस बनाता है जैसा कि एक अन्य उदाहरण में वर्णित है, और इसके प्रक्रिया के उपयुक्त मानक फ़ाइल डिस्क्रिप्टर पर प्रत्येक पाइप के अंत को डुप्लिकेट करने के लिए dup2 (2) फ़ंक्शन का उपयोग करता है। यह आम तौर पर मूल पाइप-एंड फ़ाइल डिस्क्रिप्टर को बंद करने के लिए एक अच्छा विचार है, खासकर यदि, जैसा कि अक्सर होता है, एक बच्चे को बाद में एक अलग कमांड को निष्पादित करने का इरादा रखता है।

// Most error handling omitted for brevity, including ensuring that pipe ends are
// always closed and child processes are always collected as needed; see other
// examples for more detail.
int demo() {
    int pipefds[2];
    pid_t child1 = -1, child2 = -1;

    pipe(pipefds);

    switch (child1 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 1 */
            close(pipefds[0]);
            dup2(pipefds[1], STDOUT_FILENO);
            close(pipefds[1]);
            execl("/bin/cat", "cat", "/etc/motd", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    switch (child2 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 2 */
            close(pipefds[1]);
            dup2(pipefds[0], STDIN_FILENO);
            close(pipefds[0]);
            execl("/bin/grep", "grep", "[Ww]ombat", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    // Only the parent reaches this point
    close(pipefds[0]);
    close(pipefds[1]);
    wait(NULL);
    wait(NULL);
}

पाइप लाइन को स्थापित करने में अधिक सामान्य त्रुटियों में से एक या एक से अधिक प्रक्रियाओं को भूल जाना है जिसमें पाइप समाप्त होता है जो इसका उपयोग नहीं कर रहा है। अंगूठे के एक नियम के रूप में, अंतिम परिणाम यह होना चाहिए कि प्रत्येक पाइप का अंत बिल्कुल एक प्रक्रिया के स्वामित्व में है, और केवल उस प्रक्रिया में खुला है।

उपरोक्त डेमो फ़ंक्शन जैसे किसी मामले में, दूसरे बच्चे या माता-पिता को pipefds[1] को बंद करने में pipefds[1] परिणामस्वरूप दूसरा बच्चा लटका हुआ होगा, क्योंकि grep इनपुट के लिए प्रतीक्षा करना जारी रखेगा जब तक कि यह ईओएफ को नहीं देखता है, और वह नहीं होगा जब तक पाइप का लेखन अंत किसी भी प्रक्रिया में खुला रहता है, जैसे कि मूल प्रक्रिया या स्वयं दूसरा बच्चा।



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow