SIGPIPE क्यों मौजूद है?


92

मेरी समझ से, SIGPIPEकेवल एक के परिणाम के रूप में हो सकता है write(), जो (और करता है) -1 लौट सकता है और सेट errnoकर सकता है EPIPE... तो हमारे पास सिग्नल का अतिरिक्त ओवरहेड क्यों है? हर बार जब मैं पाइप के साथ काम करता हूं SIGPIPEतो मैं अनदेखा कर देता हूं और परिणामस्वरूप कभी कोई दर्द महसूस नहीं होता है, क्या मुझे कुछ याद आ रहा है?

जवाबों:


111

मैं पहले से स्वीकार किए गए उत्तर को नहीं खरीदता। SIGPIPEवास्तव में तब उत्पन्न होता है जब पहले से writeविफल रहता है EPIPE, वास्तव में - SIGPIPEग्लोबल सिग्नल डिस्पोज को बदलने के बिना बचने का एक सुरक्षित तरीका यह है कि अस्थायी रूप से इसके साथ मुखौटा करें pthread_sigmask, प्रदर्शन करें write, फिर sigtimedwaitकिसी भी लंबित SIGPIPEसिग्नल का उपभोग करने के लिए (शून्य टाइमआउट के साथ) प्रदर्शन करें (जिसे भेजा जाता है इसे फिर से अनमास्क करने से पहले कॉलिंग थ्रेड (प्रक्रिया नहीं)।

मेरा मानना ​​है कि SIGPIPEमौजूद कारण बहुत सरल है: शुद्ध "फिल्टर" कार्यक्रमों के लिए सामान्य डिफ़ॉल्ट व्यवहार स्थापित करना जो लगातार इनपुट पढ़ते हैं, इसे किसी तरह बदलते हैं, और आउटपुट लिखते हैं। बिना SIGPIPE, जब तक कि ये कार्यक्रम स्पष्ट रूप से लेखन त्रुटियों को संभाल नहीं लेते हैं और तुरंत बाहर निकल जाते हैं (जो सभी लिखने की त्रुटियों के लिए वांछित व्यवहार नहीं हो सकता है, वैसे भी), वे तब तक जारी रखेंगे जब तक कि वे इनपुट से बाहर नहीं निकल जाते हैं, भले ही उनका आउटपुट पाइप बंद हो गया हो। यकीन है कि आप SIGPIPEस्पष्ट रूप से जाँच EPIPEऔर बाहर निकलने के द्वारा व्यवहार की नकल कर सकते हैं , लेकिन SIGPIPEप्रोग्रामर के आलसी होने पर डिफ़ॉल्ट रूप से इस व्यवहार को प्राप्त करना था।


15
+1। इस तथ्य में सुराग है कि SIGPIPE आपको डिफ़ॉल्ट रूप से मारता है - यह एक सिस्टम कॉल को बाधित करने के लिए डिज़ाइन नहीं किया गया है, यह आपके प्रोग्राम को समाप्त करने के लिए डिज़ाइन किया गया है! यदि आप सिग्नल हैंडलर में सिग्नल को संभालने में सक्षम हैं, तो आप बस के रिटर्न कोड को संभाल सकते हैं write
निकोलस विल्सन

2
आप सही हैं, मुझे नहीं पता कि मैंने इसे पहली जगह में क्यों स्वीकार किया। यह जवाब समझ में आता है, हालांकि IMO यह अजीब है कि लिनक्स पर जैसे कि यह आलस्य कर्नेल द्वारा प्राप्त किया गया है, न कि libc द्वारा।
शी लेवी

5
ऐसा लगता है कि यह उत्तर मूल रूप से उबलता है: "क्योंकि हमारे पास अपवाद नहीं थे"। हालाँकि, C में रिटर्न कोड को अनदेखा करने वाले लोग केवल लिखने () कॉल की तुलना में बहुत व्यापक मुद्दा है। क्या लिखता है इतना विशेष यह अपने स्वयं के संकेत की जरूरत है शायद शुद्ध फ़िल्टर प्रोग्राम एक बहुत अधिक सामान्य है जिसकी मैं कल्पना करता हूं।
अरविद

@ अरविद SIGPIPE का आविष्कार यूनिक्स के लोगों द्वारा किया गया था, एक समस्या को हल करने के लिए जो वे अपने वातावरण में कर रहे थे जिसमें फ़िल्टर प्रोग्राम बेहद आम हैं, हमें बस इतना करना है कि सिस्टम को लाने वाली बूट स्क्रिप्ट पढ़ें।
काज

@SheaLevy कौन से यूनिक्स सिस्टम SIGPIPE को विशुद्ध रूप से उनके परिवाद में लागू करते हैं?
काज

23

क्योंकि आपका कार्यक्रम I / O की प्रतीक्षा कर सकता है या अन्यथा निलंबित हो सकता है। SIGPIPE आपके प्रोग्राम को अतुल्यकालिक रूप से बाधित करता है, सिस्टम कॉल को समाप्त करता है, और इसलिए इसे तुरंत संभाला जा सकता है।

अपडेट करें

एक पाइपलाइन पर विचार करें A | B | C

निश्चितता के लिए, हम मान लेंगे कि B विहित प्रतिलिपि लूप है:

while((sz = read(STDIN,bufr,BUFSIZE))>=0)
    write(STDOUT,bufr,sz);

Bपर अवरुद्ध है पढ़ने (2) से डेटा के लिए कॉल प्रतीक्षा Aजब Cसमाप्त। यदि आप लिखने (2) से रिटर्न कोड की प्रतीक्षा करते हैं , तो B इसे कब देखेगा? उत्तर, निश्चित रूप से, तब तक नहीं है जब तक कि ए अधिक डेटा नहीं लिखता (जो एक लंबा इंतजार हो सकता है - क्या होगा अगर ए कुछ और द्वारा अवरुद्ध है?)। सूचना, जिस तरह से, यह भी हमें एक सरल, क्लीनर कार्यक्रम की अनुमति देता है। यदि आप लिखने से त्रुटि कोड पर निर्भर हैं, तो आपको कुछ इस तरह की आवश्यकता होगी:

while((sz = read(STDIN,bufr,BUFSIZE))>=0)
    if(write(STDOUT,bufr,sz)<0)
        break;

एक और अपडेट

अहा, आप लेखन के व्यवहार के बारे में भ्रमित हैं। आप देखें, जब लंबित लेखन के साथ फाइल डिस्क्रिप्टर बंद हो जाता है, तब SIGPIPE होता है। हालांकि लेखन -1 वापस आ जाएगा , आखिरकार संकेत का पूरा बिंदु आपको अतुल्यकालिक रूप से सूचित करना है कि अब लिखना संभव नहीं है। यह वह चीज है जो UNIX में पाइपों की पूरी सुरुचिपूर्ण सह-नियमित संरचना का काम करती है।

अब, मैं आपको कई UNIX सिस्टम प्रोग्रामिंग पुस्तकों में से किसी एक पर संपूर्ण चर्चा करने के लिए इंगित कर सकता हूं, लेकिन इसका एक बेहतर उत्तर है: आप इसे स्वयं सत्यापित कर सकते हैं। एक सरल Bकार्यक्रम लिखें [1] - आपको पहले से ही हिम्मत मिल गई है, आपको केवल एक की आवश्यकता है mainऔर कुछ में शामिल हैं - और के लिए एक सिग्नल हैंडलर जोड़ें SIGPIPE। एक पाइपलाइन की तरह चलाएँ

cat | B | more

और एक अन्य टर्मिनल विंडो में, बी को डिबगर संलग्न करें और बी सिग्नल हैंडलर के अंदर एक ब्रेकपॉइंट लगाएं।

अब, अधिक को मारें और बी को आपके सिग्नल हैंडलर में तोड़ देना चाहिए। स्टैक की जांच करें। आप पाएंगे कि रीड अभी भी लंबित है। सिग्नल हैंडलर को आगे बढ़ने दें और वापस लौटें, और परिणाम को लिखकर देखें - जो तब -1 होगा।

[१] स्वाभाविक रूप से, आप सी में अपना बी प्रोग्राम लिखेंगे। :-)


3
B SIGPIPE के साथ C की समाप्ति को जल्दी क्यों देखेगा? B तब तक रीड पर ब्लॉक रहेगा जब तक कि उसके STDIN पर कुछ लिखा नहीं जाता है, जिस बिंदु पर वह लिखने को बुलाएगा () और उसके बाद ही SIGPIPE को उठाया जाएगा / -1 वापस किया जाएगा।
शिया लेवी

2
मुझे वास्तव में जवाब पसंद है: SIGPIPE मृत्यु को तुरंत पाइपलाइन के आउटपुट अंत से वापस फैलने देता है। इसके बिना पाइप के एन तत्वों में से प्रत्येक को पाइप लाइन को मारने के लिए प्रतिलिपि प्रोग्राम के एक चक्र तक ले जाता है, और इनपुट साइड का कारण एन लाइनें उत्पन्न करता है जो कभी भी अंत तक नहीं पहुंचता है।
Yttrill

18
यह उत्तर गलत है। पढ़ने के दौरान नहीं दिया SIGPIPEजाता है , लेकिन के दौरान । आपको इसका परीक्षण करने के लिए C प्रोग्राम लिखने की ज़रूरत नहीं है, बस चलाएं , और एक अलग टर्मिनल में। आप देखेंगे कि जब आप कुछ टाइप करते हैं तो आप खुशी-खुशी उसके इंतजार में रहते हैं और एंटर दबाकर टूटे हुए पाइप से मर जाते हैं, ठीक उसी तरह क्योंकि इसने आउटपुट लिखने की कोशिश की थी। writecat | headpkill headcatread()cat
user4815162342

5
-1 SIGPIPEको वितरित नहीं किया जा सकता है B, जबकि Bपर अवरुद्ध है readक्योंकि SIGPIPEजब तक उत्पन्न नहीं कर रहा है Bप्रयास writewriteएक ही समय में कॉल करते समय कोई भी धागा "I / O या अन्यथा निलंबित" की प्रतीक्षा नहीं कर सकता है।
दान मोल्डिंग

3
क्या आप एक पूर्ण कार्यक्रम पोस्ट कर सकते हैं, जो SIGPIPEएक पर अवरुद्ध होने के दौरान उठाया जा रहा है read? मैं उस व्यवहार को बिल्कुल भी नहीं दोहरा सकता (और वास्तव में मुझे यकीन नहीं है कि मैंने इसे पहले स्थान पर क्यों स्वीकार किया)
शिया लेवी

7

https://www.gnu.org/software/libc/manual/html_mono/libc.html

यह लिंक कहता है:

एक पाइप या FIFO को दोनों सिरों पर एक साथ खोलना होगा। यदि आप किसी पाइप या FIFO फ़ाइल से पढ़ते हैं, जिसमें इसे लिखने की कोई प्रक्रिया नहीं है (शायद इसलिए कि उन्होंने सभी फ़ाइल बंद कर दी हैं, या बाहर निकल गए हैं), तो रीड-एंड-फ़ाइल लौटाती है। एक पाइप या एफआईएफओ के लिए लिखना जिसमें पढ़ने की प्रक्रिया नहीं है, उसे त्रुटि स्थिति माना जाता है; यह एक संकेत संकेत उत्पन्न करता है, और त्रुटि कोड EPIPE के साथ विफल रहता है अगर संकेत संभाला या अवरुद्ध है।

- मैक्रो: int SIGPIPE

टूटी हुई नली। यदि आप पाइप या एफआईएफओ का उपयोग करते हैं, तो आपको अपना आवेदन डिजाइन करना होगा ताकि एक प्रक्रिया शुरू होने से पहले पढ़ने के लिए पाइप को खोले। यदि पढ़ने की प्रक्रिया कभी शुरू नहीं होती है, या अप्रत्याशित रूप से समाप्त हो जाती है, तो पाइप या FIFO को लिखना एक SIGPIPE संकेत उठाता है। यदि SIGPIPE को अवरुद्ध, नियंत्रित या अनदेखा किया जाता है, तो इसके बजाय EPIPE के साथ अपमानजनक कॉल विफल हो जाती है।

पाइप्स और एफआईएफओ विशेष फाइलों पर पाइप्स और एफआईएफओ में अधिक विस्तार से चर्चा की गई है।


5

मुझे लगता है कि पाइप में सब कुछ लिखने में बहुत सारे कोड की आवश्यकता के बिना त्रुटि को ठीक करना है।

कुछ कार्यक्रमों के वापसी मूल्य को अनदेखा करते हैं write(); बिना SIGPIPEवे बेकार में सभी आउटपुट उत्पन्न करेंगे।

प्रोग्राम जो write()संभावना के वापसी मूल्य की जांच करते हैं यदि यह विफल हो जाता है तो एक त्रुटि संदेश प्रिंट करें; यह टूटी हुई पाइप के लिए अनुपयुक्त है क्योंकि यह वास्तव में पूरी पाइपलाइन के लिए त्रुटि नहीं है।


2

मशीन की जानकारी:

लिनक्स 3.2.0-53-जेनेरिक # 81-उबटन एसएमपी थू अगस्त 22 21:01:03 यूटीसी 2013 x86_64 x86_64 x86_64 GNU / लिनक्स

gcc संस्करण 4.6.3 (Ubuntu / Linaro 4.6.3-1ubuntu5)

मैंने यह कोड नीचे लिखा है:

// Writes characters to stdout in an infinite loop, also counts 
// the number of characters generated and prints them in sighandler
// writestdout.c

# include <unistd.h>
# include <stdio.h>
# include <signal.h>
# include <string.h>

int writeCount = 0;    
void sighandler(int sig) {
    char buf1[30] ;
    sprintf(buf1,"signal %d writeCount %d\n", sig, writeCount);
    ssize_t leng = strlen(buf1);
    write(2, buf1, leng);
    _exit(1);

}

int main() {

    int i = 0;
    char buf[2] = "a";

    struct sigaction ss;
    ss.sa_handler = sighandler;

    sigaction(13, &ss, NULL);

    while(1) {

        /* if (writeCount == 4) {

            write(2, "4th char\n", 10);

        } */

        ssize_t c = write(1, buf, 1);
        writeCount++;

    }

}

// Reads only 3 characters from stdin and exits
// readstdin.c

# include <unistd.h>
# include <stdio.h>

int main() {

    ssize_t n ;        
    char a[5];        
    n = read(0, a, 3);
    printf("read %zd bytes\n", n);
    return(0);

}

आउटपुट:

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 11486

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 429

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 281

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 490

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 433

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 318

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 468

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 11866

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 496

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 284

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 271

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 416

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 11268

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 427

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 8812

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 394

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 10937

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 10931

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 3554

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 499

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 283

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 11133

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 451

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 493

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 233

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 11397

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 492

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 547

$ ./writestdout | ./readstdin 
read 3 bytes
signal 13 writeCount 441

आप देख सकते हैं कि प्रत्येक उदाहरण SIGPIPEमें केवल 3 से अधिक अक्षर प्राप्त होने के बाद (लिखने की कोशिश की गई) लिखित प्रक्रिया द्वारा प्राप्त की जाती है।

क्या यह साबित नहीं होता है कि SIGPIPEपढ़ने की प्रक्रिया समाप्त होने के तुरंत बाद उत्पन्न नहीं होती है लेकिन बंद पाइप में कुछ और डेटा लिखने के प्रयास के बाद?

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.