সিগপাইপ কেন বিদ্যমান?


94

আমার বোধগম্যতা থেকে, SIGPIPEকেবলমাত্র এ-এর ফলাফল হিসাবে ঘটতে পারে write(), যা -1 ফিরে আসতে পারে (এবং কী করে) সেট errnoকরতে পারে EPIPE... সুতরাং কেন আমাদের সংকেতের অতিরিক্ত ওভারহেড থাকবে? প্রতিবার পাইপগুলি নিয়ে কাজ করার সময় আমি উপেক্ষা করি SIGPIPEএবং ফলস্বরূপ কোনও ব্যথা অনুভব করি না, আমি কি কিছু মিস করছি?

উত্তর:


112

আমি পূর্বে গৃহীত উত্তর কিনব না। SIGPIPEঠিক তখনই writeব্যর্থ হয় যখন ব্যর্থ হয় EPIPE, পূর্বে নয় - প্রকৃতপক্ষে SIGPIPEবিশ্বব্যাপী সংকেত বিন্যাস পরিবর্তন না করেই এড়ানোর একটি নিরাপদ উপায় হ'ল সাময়িকভাবে এটির সাথে মাস্ক করা pthread_sigmask, সঞ্চালন করা write, তারপরে sigtimedwait(শূন্য সময়সীমা সহ) কোনও বিচ্যুত SIGPIPEসংকেত (যা প্রেরণ করা হয় সেগুলি গ্রাহ্য করতে) কলিং থ্রেড, প্রক্রিয়া নয়) এটি আবার আনমাস্ক করার আগে।

আমি বিশ্বাস করি কারণটির SIGPIPEউপস্থিতিটি অনেক সহজ: খাঁটি "ফিল্টার" প্রোগ্রামগুলির জন্য বুদ্ধিমান ডিফল্ট আচরণ স্থাপন করা যা নিরন্তর ইনপুট পড়ে, কোনওভাবে রূপান্তরিত করে এবং আউটপুট লেখায় write ছাড়া SIGPIPE, যদি না এই প্রোগ্রামের স্পষ্টভাবে লেখার ত্রুটি এবং অবিলম্বে প্রস্থান (যা সব লেখার ত্রুটির জন্য আকাঙ্ক্ষিত আচরণ, যাহাই হউক না কেন নাও হতে পারে) হ্যান্ডেল, তারা যতক্ষণ না তারা ইনপুট এমনকি যদি তাদের আউটপুট নল বন্ধ করে দেয়া হয়েছে ফুরিয়ে চলমান অব্যাহত থাকবে। অবশ্যই আপনি SIGPIPEস্পষ্টভাবে পরীক্ষা করে EPIPEএবং বেরিয়ে এসে এর আচরণটির সদৃশ করতে পারবেন SIGPIPEতবে প্রোগ্রামারটি অলস অবস্থায় ডিফল্টরূপে এই আচরণটি অর্জনের পুরো উদ্দেশ্য ছিল।


15
+1 ক্লুটি সত্য যে সাইনপাইপ আপনাকে ডিফল্টরূপে হত্যা করে - এটি কোনও সিস্টেম কলকে বাধা দেওয়ার জন্য ডিজাইন করা হয়নি, এটি আপনার প্রোগ্রামটি বন্ধ করার জন্য তৈরি করা হয়েছে! আপনি যদি কোনও সিগন্যাল হ্যান্ডলারে সিগন্যালটি পরিচালনা করতে সক্ষম হন তবে আপনি ঠিক সেইসাথে রিটার্ন কোডটিও পরিচালনা করতে পারবেন write
নিকোলাস উইলসন

4
আপনি ঠিক বলেছেন, কেন আমি প্রথমে এটি গ্রহণ করেছি তা আমি জানি না। এই উত্তরটি বোধগম্য হয়, যদিও আইএমও এটি অদ্ভুত eg যেমন লিনাক্সে এই অলসতা কার্নেল দ্বারা অর্জন করা হয়, libc দ্বারা নয়।
শেয়া লেভি

4
মনে হচ্ছে এই উত্তরটি মূলত এটিকে ফুটিয়ে তোলে: "কারণ আমাদের ব্যতিক্রম ছিল না"। তবে লোকেরা সিতে রিটার্ন কোডগুলি উপেক্ষা করে কেবল কল () কল লেখার চেয়ে অনেক বেশি বিস্তৃত সমস্যা issue রচনা এত বিশেষ করে তোলে যার নিজস্ব সংকেত প্রয়োজন? সম্ভবত খাঁটি ফিল্টার প্রোগ্রামগুলি আমি কল্পনা করেছি common
অরভিদ

ইউরিক্সের লোকেরা @ অ্যারভিড সিগপিপ আবিষ্কার করেছিলেন, তাদের পরিবেশে যে সমস্যা ছিল তার সমাধানের জন্য, ফিল্টার প্রোগ্রামগুলি অত্যন্ত সাধারণ, আমাদের যা করতে হবে তা হ'ল বুট স্ক্রিপ্টগুলি যা সিস্টেমটি নিয়ে আসে read
কাজ

@ শিয়ালিভি কোন ইউনিক্স সিস্টেমগুলি তাদের লিবিসিতে খাঁটিভাবে SIGPIPE প্রয়োগ করে?
কাজ

23

কারণ আপনার প্রোগ্রামটি I / O এর জন্য অপেক্ষা করছে বা অন্যথায় স্থগিত হতে পারে। একটি সিগপাইপ আপনার প্রোগ্রামকে সংবিধানে বাধা দেয়, সিস্টেম কলটি সমাপ্ত করে, এবং তাত্ক্ষণিকভাবে পরিচালনা করা যায়।

হালনাগাদ

একটি পাইপলাইন বিবেচনা করুন A | B | C

কেবলমাত্র নির্ধারণের জন্য, আমরা ধরে নেব যে বি হ'ল প্রামাণ্য অনুলিপি:

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

Bসমাপ্তির সময় থেকে ডেটার জন্য অপেক্ষা করতে পঠিত (2) কলটিতে অবরুদ্ধ । আপনি যদি লিখনের (2) থেকে রিটার্ন কোডের জন্য অপেক্ষা করেন , বি কখন এটি দেখতে পাবে? উত্তরটি অবশ্যই, যতক্ষণ না এ আরও ডেটা লেখেন (তবে এটি দীর্ঘ প্রতীক্ষার কারণ হতে পারে - যদি অন্য কোনও কিছু দ্বারা অবরুদ্ধ থাকে তবে কী হবে?)। লক্ষ্য করুন, যাইহোক, এটি আমাদের আরও সহজ, ক্লিনার প্রোগ্রামেরও অনুমতি দেয়। আপনি যদি লেখার থেকে ত্রুটি কোডের উপর নির্ভর করে থাকেন তবে আপনার এমন কিছু প্রয়োজন হবে:AC

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

আরেকটি আপডেট

আহা, আপনি লেখার আচরণ সম্পর্কে বিভ্রান্ত হয়ে পড়েছেন। আপনি দেখুন, যখন মুলতুবি লেখার সাথে ফাইল বর্ণনাকারীটি বন্ধ হয়ে যায়, ঠিক তখনই সাইনপাইপ ঘটে। লেখাগুলি শেষ পর্যন্ত -1 ফিরে আসার পরে , সংকেতের পুরো পয়েন্টটি আপনাকে সংকীর্ণভাবে জানানো হয় যে লেখাটি আর সম্ভব নয়। এটি পাইপগুলির পুরো মার্জিত সহ-রুটিন কাঠামোটিকে ইউনিক্সে কাজ করার এক অংশ।

এখন, আমি আপনাকে বেশ কয়েকটি ইউনিক্স সিস্টেম প্রোগ্রামিং বইয়ের একটি সম্পূর্ণ আলোচনার দিকে নির্দেশ করতে পারি, তবে এর থেকে আরও ভাল উত্তর পাওয়া যায়: আপনি নিজে এটি যাচাই করতে পারেন। একটি সাধারণ Bপ্রোগ্রাম লিখুন [1] - আপনি ইতিমধ্যে সাহস পেয়ে গেছেন, আপনার যা দরকার তা হ'ল একটি mainএবং এর মধ্যে কিছু রয়েছে - এবং এর জন্য একটি সিগন্যাল হ্যান্ডলার যুক্ত করুন SIGPIPE। একটি পাইপলাইন চালান

cat | B | more

এবং অন্য একটি টার্মিনাল উইন্ডোতে, বিতে একটি ডিবাগার সংযুক্ত করুন এবং বি সিগন্যাল হ্যান্ডলারের ভিতরে একটি ব্রেকপয়েন্ট রাখুন।

এখন, আরও মারুন এবং বি আপনার সিগন্যাল হ্যান্ডলারের মধ্যে বিরতি দেওয়া উচিত। স্ট্যাক পরীক্ষা করুন। আপনি বুঝতে পারবেন, পঠিত এখনও হয় নি। সিগন্যাল হ্যান্ডলারটি এগিয়ে চলুন এবং ফিরে আসুন, এবং লেখার মাধ্যমে ফিরে আসা ফলাফলটি দেখুন - যা পরে -1 হবে।

[1] স্বাভাবিকভাবেই, আপনি আপনার বি প্রোগ্রামটি সিতে লিখবেন :-)


4
কেন সিপিসিপি দিয়ে সি এর সমাপ্তিটি তাড়াতাড়ি দেখতে পাবে? তার স্টাডিআইএন-তে কিছু লেখা না পাওয়া পর্যন্ত বি রিডে অবরুদ্ধ থাকবে, যারপরে এটি লিখিত কলটি কল করবে () এবং তারপরেই সাইনপাইপ উত্থাপন / -১ ফিরিয়ে দেওয়া হবে।
শেয়া লেভি

4
উত্তরটি আমি সত্যিই পছন্দ করি: পাইপলাইনটির আউটপুট প্রান্তটি তাত্ক্ষণিকভাবে মৃত্যুর পিছনে ছড়িয়ে পড়ার অনুমতি দেয় P এটি ব্যতীত পাইপলাইনটি নষ্ট করতে পাইপের প্রতিটি এন উপাদানগুলির জন্য অনুলিপি প্রোগ্রামের এক চক্র পর্যন্ত লাগে এবং ইনপুট পার্শ্বটিকে এন লাইন তৈরি করে দেয় যা কখনও শেষ পর্যন্ত পৌঁছায় না।
Yttrill

18
এই উত্তরটি ভুল। পড়ার সময় বিতরণ করা SIGPIPEহয় না , তবে write। এটি পরীক্ষা করতে, চালানো cat | headএবং pkill headএকটি পৃথক টার্মিনালে আপনাকে কোনও সি প্রোগ্রাম লেখার দরকার নেই । আপনি দেখতে পাবেন যে catআনন্দের সাথে কেবলমাত্র তার অপেক্ষা read()করতে থাকে, যখন আপনি কোনও টাইপ করেন এবং এন্টার টিপুন catকোনও ভাঙা পাইপ দিয়ে মারা যায়, কারণ এটি আউটপুট লেখার চেষ্টা করেছিল।
ব্যবহারকারী 4815162342

4
-1 অবরুদ্ধ থাকাকালীন ডেলিভারি করা SIGPIPEযাবে না কারণ চেষ্টা না করা পর্যন্ত উত্পন্ন হয় না । কোনও থ্রেড একই সাথে কল করার সময় "I / O এর জন্য অপেক্ষা করতে বা অন্যথায় স্থগিত করা" হতে পারে না । BBreadSIGPIPEBwritewrite
ড্যান মোল্ডিং

4
আপনি কি এমন একটি সম্পূর্ণ প্রোগ্রাম পোস্ট করতে পারেন SIGPIPEযা একটিতে অবরুদ্ধ অবস্থায় উত্থাপিত দেখায় read? আমি এই আচরণটি মোটেই পুনরুত্পাদন করতে পারি না (এবং আমি কেন এটিকে প্রথম স্থানে গ্রহণ করেছি তা আসলে নিশ্চিত নয়)
শেয়া লেভি

7

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

এই লিঙ্কটি বলে:

একটি পাইপ বা ফিফো একই সাথে উভয় প্রান্তে খোলা থাকতে হবে। আপনি যদি কোনও পাইপ বা ফিফো ফাইল থেকে পড়ে থাকেন তবে এতে কোনও প্রসেস লেখা থাকে না (সম্ভবত তারা সমস্ত ফাইলটি বন্ধ করে দিয়েছেন বা বেরিয়ে গেছে), পড়ুন ফাইলের শেষে-ফাইলটি returns কোনও পাইপ বা ফিফোতে লিখনকে পাঠের প্রক্রিয়া নেই এমন একটি ত্রুটি শর্ত হিসাবে গণ্য করা হয়; এটি একটি সিগপাইপ সিগন্যাল জেনারেট করে, এবং সিগন্যালটি পরিচালনা বা অবরুদ্ধ করা থাকলে ত্রুটি কোড EPIPE সহ ব্যর্থ হয়।

- ম্যাক্রো: অন্তর্নির্মিত এস.পি.

ভাঙ্গা পাইপ. আপনি যদি পাইপ বা FIFO ব্যবহার করেন তবে আপনাকে আপনার অ্যাপ্লিকেশনটি ডিজাইন করতে হবে যাতে অন্য প্রক্রিয়াটি লেখার শুরু করার আগে একটি প্রক্রিয়া পড়ার জন্য পাইপটি খোলায়। যদি পঠন প্রক্রিয়াটি কখনই শুরু না হয় বা অপ্রত্যাশিতভাবে শেষ হয় তবে পাইপ বা ফিফোতে লিখিতভাবে একটি সিপাইপিস সিগন্যাল উত্থাপন করা হয়। যদি SIGPIPE কে অবরুদ্ধ করা হয়, পরিচালনা করা হয় বা উপেক্ষা করা হয় তবে আপত্তিজনক কলটি পরিবর্তে EPIPE এর সাথে ব্যর্থ হয়।

পাইপ এবং ফিফোর বিশেষ ফাইলগুলি পাইপ এবং ফিফগুলিতে আরও বিশদে আলোচনা করা হয়।


5

আমি মনে করি কোনও পাইপে লেখার ক্ষেত্রে প্রচুর কোডের প্রয়োজন ছাড়াই ত্রুটিটি পরিচালনা করা সঠিক হওয়া correct

কিছু প্রোগ্রামের ফেরতের মূল্য উপেক্ষা করে write(); SIGPIPEতারা ছাড়া অযথা সমস্ত আউটপুট উত্পাদন করতে পারে।

যে প্রোগ্রামগুলি write()ব্যর্থ হয় যদি সম্ভবত কোনও ত্রুটি বার্তা মুদ্রণের সম্ভাব্যতার ফেরতের মান পরীক্ষা করে ; এটি একটি ভাঙ্গা পাইপের পক্ষে অনুপযুক্ত কারণ এটি পুরো পাইপলাইনের পক্ষে আসলে ত্রুটি নয়।


2

যন্ত্রের তথ্য:

লিনাক্স 3.2.0-53-জেনেরিক # 81-উবুন্টু এসএমপি থু আগস্ট 21 21:01:03 ইউটিসি 2013 x86_64 x86_64 x86_64 জিএনইউ / লিনাক্স

জিসিসি সংস্করণ 4.6.3 (উবুন্টু / লিনারো 4.6.3-1ুবুন্টু 5)

আমি এই কোডটি নীচে লিখেছি:

// 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পড়ার প্রক্রিয়াটি সমাপ্ত হওয়ার পরে অবিলম্বে উত্পন্ন হয়নি তবে বন্ধ পাইপটিতে আরও কিছু ডেটা লেখার চেষ্টা করার পরে?

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.