प्रतिनिधि इंटरव्यू विषय

Operating Systems इंटरव्यू: File Descriptor क्या है, और Too Many Open Files को कैसे Debug करें?

सामान्यकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

एक Linux API सर्विस कई घंटों बाद EMFILE: Too many open files लौटाना शुरू कर देती है, और रीस्टार्ट करने पर यह केवल अस्थायी रूप से ठीक होती है। फाइल डिस्क्रिप्टर्स के पीछे के कर्नेल मॉडल को समझाएं, प्रोसेस और सिस्टम लिमिट्स में अंतर बताएं, फिर दिखाएं कि आप लीक और अपर्याप्त कैपेसिटी के बीच कैसे निर्णय लेंगे, कारण का पता लगाएंगे और फिक्स को सत्यापित करेंगे।

Prompt और लागू होने वाले Roles

एक Linux API सर्विस कई घंटों तक स्थिर ट्रैफिक के तहत चलती है, फिर नए कनेक्शन्स को अस्वीकार करना शुरू कर देती है। अपस्ट्रीम सर्विसेज के लिए कॉल्स और लॉग फाइल्स खोलने के प्रयास भी विफल होने लगते हैं। आप जानते हैं कि:

  • सर्विस प्रोसेस की सॉफ्ट RLIMIT_NOFILE 8,192 और हार्ड लिमिट 65,536 है;
  • यह लगभग 400 ओपन फाइल डिस्क्रिप्टर्स के साथ शुरू होती है, फिर लगभग 25 प्रति मिनट की दर से बढ़ती है;
  • इंसिडेंट से ठीक पहले, /proc/<pid>/fd में लगभग 8,192 एंट्रीज होती हैं और लॉग्स में EMFILE: Too many open files शामिल होता है;
  • प्रोसेस को रीस्टार्ट करने से सर्विस तुरंत बहाल हो जाती है, लेकिन वृद्धि फिर से शुरू हो जाती है;
  • होस्ट का /proc/sys/fs/file-nr, /proc/sys/fs/file-max से काफी नीचे बना रहता है।

एक फाइल डिस्क्रिप्टर, प्रोसेस फाइल-डिस्क्रिप्टर टेबल और एक ओपन फाइल डिस्क्रिप्शन के बीच के संबंध को समझाएं। बताएं कि रेगुलर फाइल्स, सॉकेट्स, पाइप्स और epoll सभी डिस्क्रिप्टर्स का उपभोग क्यों करते हैं, और EMFILE तथा ENFILE में अंतर स्पष्ट करें। फिर एक प्रोडक्शन-सुरक्षित डायग्नोस्टिक क्रम दें, निर्णय लें कि क्या यह एक FD लीक है या कैपेसिटी से परे वैध कन्करेंसी है, मिटिगेशन और स्थायी समाधान प्रस्तावित करें, और समझाएं कि आप कैसे साबित करेंगे कि इंसिडेंट हल हो गया है।

यह प्रश्न बैकएंड, SRE, इंफ्रास्ट्रक्चर, सिस्टम्स और सामान्य सॉफ्टवेयर-इंजीनियरिंग इंटरव्यूज के लिए उपयुक्त है। 8,192, 65,536, 400 और 25 प्रति मिनट के मान काल्पनिक इंटरव्यू इनपुट्स हैं, यूनिवर्सल सिफारिशें नहीं। एक वास्तविक उत्तर में सर्विस मैनेजर, कंटेनर रनटाइम, प्रोसेस अनुमतियों और एप्लिकेशन के कन्करेंसी मॉडल को भी ध्यान में रखा जाना चाहिए।

इंटरव्यूअर क्या टेस्ट कर रहा है

पहला, क्या उम्मीदवार यह समझा सकता है कि एक फाइल डिस्क्रिप्टर प्रोसेस के अंदर एक छोटा पूर्णांक (integer) इंडेक्स है, न कि पाथनेम, इनोड या कर्नेल ऑब्जेक्ट स्वयं? एक मजबूत उत्तर “प्रोसेस FD टेबल → ओपन फाइल डिस्क्रिप्शन → फाइल, सॉकेट, पाइप, या अन्य ऑब्जेक्ट” की श्रृंखला बनाता है और जानता है कि dup द्वारा बनाए गए या fork के माध्यम से इनहेरिट किए गए डिस्क्रिप्टर्स एक ही ओपन फाइल डिस्क्रिप्शन को संदर्भित कर सकते हैं।

दूसरा, क्या उम्मीदवार लिमिट स्कोप्स को अलग कर सकता है? EMFILE का अर्थ है कि वर्तमान प्रोसेस RLIMIT_NOFILE तक पहुँच गया है; ENFILE का अर्थ है कि सिस्टम-व्यापी ओपन-फाइल लिमिट तक पहुँच गया है। न तो अकेले fs.file-max और न ही किसी असंबंधित शेल में ulimit -n विफल होने वाली प्रोसेस की प्रभावी लिमिट को साबित करता है।

तीसरा, क्या डायग्नोसिस में काउंट, संरचना और ट्रेंड शामिल हैं? केवल एक lsof स्नैपशॉट केवल एक पल दिखाता है। एक लीक स्थापित करने के लिए स्थिर ट्रैफिक के तहत कुल और ऑब्जेक्ट प्रकारों का सैंपलिंग आवश्यक है, फिर कनेक्शन पूल्स, रिक्वेस्ट लाइफसाइकल्स, लॉग रोटेशन, चाइल्ड प्रोसेसेज और विफलता पाथ्स के साथ वृद्धि को सहसंबद्ध करना होता है।

चौथा, क्या उम्मीदवार अस्थायी कैपेसिटी को मूल-कारण सुधार से अलग कर सकता है? लिमिट बढ़ाना एक इंसिडेंट-रिस्पॉन्स विंडो बना सकता है या वैध कैपेसिटी प्लानिंग का हिस्सा हो सकता है, लेकिन यह उन संसाधनों को बंद नहीं करता जिनका स्वामित्व खो गया था। एक निश्चित धनात्मक स्लोप (positive slope) अंततः बड़ी लिमिट को भी समाप्त कर देगा।

अंत में, क्या कोई वेरिफिकेशन लूप है? “एरर आना बंद हो गया” पर्याप्त नहीं है। एक मजबूत उत्तर लक्षित पीक ट्रैफिक और इंजेक्ट की गई विफलताओं के तहत FD उपयोग और स्लोप, ऑब्जेक्ट प्रकार, रिक्वेस्ट एरर्स, टेल लेटेंसी, पूल स्थिति और बार-बार होने वाले लाइफसाइकिल इवेंट्स को मान्य करता है।

उत्तर देने से पहले स्पष्ट करने योग्य प्रश्न

  • सटीक errno क्या है? मानव-पठनीय संदेश से स्कोप का अनुमान लगाने के बजाय एप्लिकेशन या सिस्टम-कॉल एरर से EMFILE या ENFILE की पुष्टि करें।
  • कौन सा PID विफल हो रहा है? एक सुपरवाइजर, वर्कर, साइडकार और अल्पकालिक चाइल्ड अलग-अलग लिमिट्स इनहेरिट कर सकते हैं और विभिन्न संसाधनों को रोक सकते हैं।
  • सर्विस को क्या लॉन्च करता है? एक इंटरैक्टिव शेल, systemd, एक कंटेनर रनटाइम और एक प्रोसेस मैनेजर अलग-अलग सॉफ्ट और हार्ड लिमिट्स स्थापित कर सकते हैं। एक शेल कमांड चल रही सर्विस को पूर्वव्यापी रूप से नहीं बदल सकता है।
  • FD वृद्धि के साथ क्या बदलता है? इसे कन्करेंट कनेक्शन्स, अपस्ट्रीम रिक्वेस्ट्स, कतार की गहराई, लॉग रोटेशन, रीलोड्स, चाइल्ड काउंट और एरर रेट के साथ संरेखित करें।
  • किस ऑब्जेक्ट का प्रकार बढ़ रहा है? सॉकेट्स, रेगुलर फाइल्स, पाइप्स, anon_inode:[eventpoll], inotify ऑब्जेक्ट्स और हटाई गई फाइल्स अलग-अलग ओनरशिप पाथ्स की ओर इशारा करते हैं।
  • वैध कैपेसिटी मॉडल क्या है? एक उच्च-कन्करेंसी प्रॉक्सी को वैध रूप से कई सॉकेट्स की आवश्यकता हो सकती है। एक बड़ी संख्या स्वचालित रूप से एक लीक नहीं है; इसे कॉन्फ़िगर की गई कन्करेंसी से मेल खाना चाहिए और लोड कम होने पर व्यवस्थित होना चाहिए।
  • क्या /proc का सुरक्षित रूप से निरीक्षण किया जा सकता है? किसी अन्य उपयोगकर्ता के डिस्क्रिप्टर्स को पढ़ना अनुमतियों और ptrace नियमों द्वारा प्रतिबंधित हो सकता है। प्रोडक्शन जांच में न्यूनतम आवश्यक विशेषाधिकार का उपयोग किया जाना चाहिए और लंबे समय तक चलने वाली, उच्च-ओवरहेड ट्रेसिंग से बचना चाहिए।

30-सेकंड का उत्तर

“एक फाइल डिस्क्रिप्टर एक प्रोसेस की FD टेबल में एक गैर-ऋणात्मक पूर्णांक (nonnegative integer) इंडेक्स होता है। टेबल एंट्री एक सिस्टम-व्यापी ओपन फाइल डिस्क्रिप्शन को संदर्भित करती है, जो फाइल ऑफसेट और स्टेटस फ्लैग्स को स्टोर करती है और फिर एक रेगुलर फाइल, सॉकेट, पाइप या किसी अन्य कर्नेल I/O ऑब्जेक्ट की ओर इशारा करती है। EMFILE का अर्थ है कि यह प्रोसेस RLIMIT_NOFILE तक पहुँच गया है; ENFILE का अर्थ है कि होस्ट अपनी सिस्टम-व्यापी ओपन-फाइल लिमिट तक पहुँच गया है।

मैं विफल होने वाले PID और errno की पुष्टि करूँगा, /proc/<pid>/limits को पढ़ूँगा, /proc/<pid>/fd को गिनूँगा और वर्गीकृत करूँगा, समय के साथ श्रेणियों का सैंपल लूँगा और /proc/sys/fs/file-nr की जाँच करूँगा। स्थिर ट्रैफिक के तहत एक निश्चित धनात्मक स्लोप, जो रीस्टार्ट द्वारा रीसेट हो जाता है और एक ऑब्जेक्ट प्रकार में केंद्रित होता है, एक लीक को इंगित करता है। एक काउंट जो कन्करेंसी को ट्रैक करता है, एक परिभाषित पठार (plateau) तक पहुँचता है और बाद में गिरता है, कैपेसिटी के दबाव को इंगित करता है। मैं कैपेसिटी वैलिडेशन के बाद रेट-लिमिट, रोल इंस्टेंसेस और वास्तविक सर्विस लिमिट बढ़ा सकता हूँ, लेकिन स्थायी समाधान रिसोर्स ओनरशिप, विफलता-पाथ क्लीनअप, बाउंडेड पूल्स और सही इनहेरिटेंस है। मैं इसे FD उपयोग और स्लोप, ऑब्जेक्ट रिलीज और पीक लोड पर शून्य संबंधित एरर्स के साथ साबित करता हूँ।”

चरण-दर-चरण गहन विश्लेषण

चरण 1: तीन-स्तरीय संदर्भ मॉडल (Reference Model) बनाएं

सफलता पर, open() एक गैर-ऋणात्मक पूर्णांक लौटाता है। Linux सामान्य रूप से सबसे कम डिस्क्रिप्टर नंबर का चयन करता है जो वर्तमान में उस प्रोसेस द्वारा उपयोग में नहीं है। परिपाटी के अनुसार, 0, 1 और 2 मानक इनपुट, मानक आउटपुट और मानक त्रुटि हैं। बाद के नंबर अभी भी उस प्रोसेस में केवल इंडेक्स हैं।

मूल संबंध है:

process FD-table entry → open file description → underlying object

परतें अलग-अलग स्थिति (state) रखती हैं:

लेयरयह क्या रखती हैमहत्वपूर्ण गुण
प्रोसेस FD-टेबल एंट्रीडिस्क्रिप्टर नंबर और डिस्क्रिप्टर फ्लैग्स जैसे close-on-execनंबर केवल उस प्रोसेस के FD-टेबल संदर्भ में सार्थक है
ओपन फाइल डिस्क्रिप्शनवर्तमान फाइल ऑफसेट और ओपन-फाइल स्टेटस फ्लैग्सएक सिस्टम-व्यापी ऑब्जेक्ट जिसे कई डिस्क्रिप्टर्स साझा कर सकते हैं
अंतर्निहित ऑब्जेक्टइनोड, सॉकेट, पाइप, डिवाइस, या अनाम (anonymous) कर्नेल ऑब्जेक्टवास्तविक I/O व्यवहार को परिभाषित करता है

एक ही पाथ पर open() के दो स्वतंत्र कॉल्स सामान्य रूप से अलग-अलग ओपन फाइल डिस्क्रिप्शन्स बनाते हैं। dup() द्वारा लौटाया गया एक डिस्क्रिप्टर उसी ओपन फाइल डिस्क्रिप्शन को संदर्भित करता है, इसलिए दोनों डिस्क्रिप्टर्स ऑफसेट और फाइल-स्टेटस फ्लैग्स साझा करते हैं। fork() के बाद, संबंधित पैरेंट और चाइल्ड डिस्क्रिप्टर्स भी उन्हीं ओपन फाइल डिस्क्रिप्शन्स को संदर्भित करते हैं। डिस्क्रिप्टर-विशिष्ट फ्लैग्स जैसे close-on-exec उस साझा स्थिति से अलग होते हैं।

यही कारण है कि “FD 42” का कोई स्थिर क्रॉस-प्रोसेस अर्थ नहीं है और केवल पाथनेम द्वारा समूहीकृत करने से समस्या छिप सकती है। एक ही पाथ को स्वतंत्र रूप से कई बार खोला जा सकता है, जबकि विभिन्न डिस्क्रिप्टर नंबर एक ओपन स्टेट साझा कर सकते हैं।

चरण 2: समझें कि कौन से संसाधन डिस्क्रिप्टर्स का उपभोग करते हैं

यूनिक्स-शैली के इंटरफेस कई I/O संसाधनों को पठनीय (readable), लिखने योग्य (writable), या प्रतीक्षा योग्य (waitable) डिस्क्रिप्टर्स के माध्यम से उजागर करते हैं:

  • रेगुलर फाइल्स और डायरेक्टरीज;
  • TCP, UDP और Unix-domain सॉकेट्स;
  • अनाम पाइप्स और नामित FIFOs;
  • टर्मिनल्स, डिवाइसेज और कुछ स्यूडो-फाइल्स (pseudo-files);
  • अनाम कर्नेल ऑब्जेक्ट्स जैसे epoll, eventfd, timerfd, signalfd और inotify।

/proc/<pid>/fd में प्रोसेस में खुले प्रति डिस्क्रिप्टर एक सिम्बोलिक लिंक होता है। एक रेगुलर फाइल आमतौर पर एक पाथ दिखाती है। सॉकेट्स और पाइप्स आमतौर पर socket:[inode] और pipe:[inode] के रूप में दिखाई देते हैं। बिना संबंधित इनोड वाले ऑब्जेक्ट्स anon_inode:[eventpoll] या किसी अन्य anon_inode प्रकार के रूप में दिखाई दे सकते हैं।

एक इवेंट लूप के लिए एक epoll डिस्क्रिप्टर बनाने का मतलब यह नहीं है कि हजारों मॉनिटर किए गए कनेक्शन्स डिस्क्रिप्टर्स का उपभोग करना बंद कर देते हैं। प्रत्येक मॉनिटर किए गए सॉकेट का अभी भी अपना FD होता है। इसके विपरीत, कुछ anon_inode:[eventpoll] एंट्रीज स्वयं एक इवेंट-लूप लीक साबित नहीं करती हैं; उस श्रेणी की पहचान करें जिसकी संख्या वास्तव में बढ़ रही है।

चरण 3: EMFILE, ENFILE और लिमिट सीलिंग्स को अलग करें

RLIMIT_NOFILE की एक सॉफ्ट लिमिट और एक हार्ड लिमिट होती है। कर्नेल सॉफ्ट लिमिट को लागू करता है। हार्ड लिमिट वह सीमा (ceiling) है जिस तक एक गैर-विशेषाधिकार प्राप्त (unprivileged) प्रोसेस सॉफ्ट लिमिट को बढ़ा सकता है। Linux इस मान को सबसे बड़े डिस्क्रिप्टर नंबर से एक अधिक के रूप में परिभाषित करता है जिसे प्रोसेस खोल सकता है।

प्राथमिक सीमाएँ हैं:

सिग्नलअर्थनिरीक्षण करने के लिए पहला साक्ष्य
EMFILEइस प्रोसेस ने RLIMIT_NOFILE सीमा पार कर ली है/proc/<pid>/limits और /proc/<pid>/fd
ENFILEहोस्ट सिस्टम-व्यापी ओपन-फाइल लिमिट तक पहुँच गया है/proc/sys/fs/file-nr, file-max और कर्नेल लॉग्स
/proc/sys/fs/nr_openRLIMIT_NOFILE बढ़ाने के लिए कर्नेल सीमाचेक करें जब हार्ड लिमिट बढ़ाना विफल हो जाए

/proc/sys/fs/file-nr में पहला फील्ड आवंटित फाइल हैंडल की संख्या है; तीसरा file-max से मेल खाता है। यह सिस्टम-व्यापी ओपन फाइल डिस्क्रिप्शन्स की एक संख्या है, इसलिए इसे प्रत्येक प्रोसेस के FD काउंट के योग के बराबर होने की आवश्यकता नहीं है: एकाधिक डिस्क्रिप्टर्स एक ओपन फाइल डिस्क्रिप्शन को साझा कर सकते हैं।

प्रॉम्प्ट स्पष्ट रूप से EMFILE की रिपोर्ट करता है, जबकि सिस्टम-व्यापी उपयोग file-max से बहुत नीचे रहता है। इसलिए प्राथमिक पाथ प्रति-प्रोसेस लिमिट है। fs.file-max को बदलने से इस विफलता का समाधान नहीं होता है।

चरण 4: पहले कम जोखिम वाले, तुलनीय साक्ष्य एकत्र करें

PID और अनुमतियों की पुष्टि करने के बाद, कम-ओवरहेड /proc स्नैपशॉट से शुरुआत करें:

bash
pid=12345

grep 'Max open files' /proc/"$pid"/limits
find /proc/"$pid"/fd -maxdepth 1 -type l 2>/dev/null | wc -l
find /proc/"$pid"/fd -maxdepth 1 -type l -exec readlink {} \; 2>/dev/null |
  sed -E 's/socket:\[[0-9]+\]/socket:[id]/; s/pipe:\[[0-9]+\]/pipe:[id]/' |
  sort | uniq -c | sort -nr | head -20
cat /proc/sys/fs/file-nr
cat /proc/sys/fs/file-max

/proc एक लाइव दृश्य है। ट्रैवर्सल के दौरान प्रोसेस डिस्क्रिप्टर्स को खोल या बंद कर सकता है, और अल्पकालिक एंट्रीज गायब हो सकती हैं। इन कमांड्स को ट्रेंड डायग्नोस्टिक्स के रूप में समझें, न कि एक एटॉमिक ऑडिट के रूप में। टाइमस्टैम्प के साथ निश्चित अंतराल पर कुल और श्रेणियों का सैंपल लें, फिर उन्हें एप्लिकेशन मेट्रिक्स के साथ संरेखित करें।

यदि वृद्धि में सॉकेट्स का वर्चस्व है, तो कनेक्शन दिशा, गंतव्य और TCP स्थिति का निरीक्षण करें। यदि रेगुलर फाइल्स बढ़ती हैं, तो लॉग्स, अस्थायी फाइलों या कॉन्फ़िगरेशन रीलोड्स के आसपास पाथ्स को समूहीकृत करें। यदि पाइप्स बढ़ते हैं, तो चाइल्ड प्रोसेसेज और IPC लाइफसाइकल्स का निरीक्षण करें। यदि anon_inode ऑब्जेक्ट्स बढ़ते हैं, तो इवेंट-लूप, वॉचर, या टाइमर रजिस्ट्रेशन और निपटान (disposal) का पता लगाएं।

उपकरण जैसे lsof -p <pid>, ss -tanp, या बाउंडेड सिस्टम-कॉल ट्रेसिंग /proc और एप्लिकेशन मेट्रिक्स द्वारा खोज को संकीर्ण करने के बाद विवरण जोड़ सकते हैं। लंबे समय तक चलने वाली ट्रेसिंग प्रोडक्शन ओवरहेड जोड़ सकती है और पर्याप्त अनुमति के बिना अभी भी अधूरी हो सकती है।

चरण 5: समस्या को वर्गीकृत करने के लिए काउंट, संरचना, स्लोप और रिकवरी का उपयोग करें

अकेले एक उच्च वर्तमान काउंट एक लीक स्थापित नहीं करता है। चार प्रश्न पूछें:

  1. क्या काउंट अपेक्षित कन्करेंसी से मेल खाता है? बजट में लिसनिंग सॉकेट्स, स्वीकृत कनेक्शन्स, आउटबाउंड पूल्स, फाइल्स, पाइप्स, इवेंट ऑब्जेक्ट्स और सुरक्षा हेडरूम शामिल हैं।
  2. क्या संरचना आर्किटेक्चर से मेल खाती है? यदि एक अपस्ट्रीम पूल 500 पर सीमित है लेकिन उस गंतव्य के लिए डिस्क्रिप्टर्स 5,000 तक पहुँचते हैं, तो रिटर्न और क्लोज पाथ्स की जांच करें।
  3. क्या स्थिर लोड के तहत स्लोप धनात्मक बना रहता है? यदि रिक्वेस्ट रेट और कन्करेंसी स्थिर हैं लेकिन FDs अभी भी 25 प्रति मिनट की दर से बढ़ते हैं, तो लीक का साक्ष्य मजबूत है।
  4. लोड कम होने पर क्या उपयोग एक पठार (plateau) पर लौटता है? रिक्वेस्ट-स्कोप फाइल्स, छोटे कनेक्शन्स और अस्थायी पाइप्स को रिलीज किया जाना चाहिए। लंबे समय तक चलने वाले पूल कनेक्शन्स बने रह सकते हैं, लेकिन एक स्पष्ट सीमा पर।

प्रॉम्प्ट में, सर्विस लगभग 400 से शुरू होती है, एक निश्चित दर से बढ़ती है, रीस्टार्ट पर रीसेट हो जाती है और दोहराती है। यह पैटर्न दृढ़ता से एक लीक के पक्ष में है। ऑब्जेक्ट वर्गीकरण अभी भी आवश्यक है ताकि प्राकृतिक कनेक्शन-पूल वार्म-अप को बिना बंद की गई फाइल समझने की गलती न की जाए।

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

चरण 6: सामान्य लीक पाथ्स के माध्यम से रिसोर्स ओनरशिप को ट्रेस करें

एक मजबूत उत्तर केवल कमांड्स पर रुकने के बजाय पूछता है कि “इसे कौन बनाता है, इसका मालिक कौन है, और विफलता के बाद इसे कौन बंद करता है?” सामान्य कारणों में शामिल हैं:

  • एक HTTP क्लाइंट रिस्पॉन्स बॉडी को बंद नहीं करता है, इसलिए कनेक्शन को न तो पुन: उपयोग किया जा सकता है और न ही तुरंत रिलीज किया जा सकता है;
  • एक डेटाबेस, कैश, या अपस्ट्रीम कनेक्शन चेक आउट किया जाता है लेकिन टाइमआउट या एक्सेप्शन के बाद वापस नहीं किया जाता है;
  • लॉग रोटेशन या कॉन्फ़िगरेशन रीलोड पुराने हैंडल को बंद किए बिना बार-बार एक नई फाइल खोलता है;
  • प्रत्येक रिक्वेस्ट एक टाइमर, वॉचर, पाइप, या इवेंट ऑब्जेक्ट बनाता है लेकिन रद्दीकरण (cancellation) क्लीनअप को छोड़ देता है;
  • पैरेंट और चाइल्ड प्रोसेसेज स्टैंडर्ड स्ट्रीम्स या IPC पाइप्स के अप्रयुक्त सिरों को खुला रखते हैं;
  • एक डिस्क्रिप्टर अप्रत्याशित रूप से exec से बच जाता है, इसलिए एक अन्य प्रोसेस संसाधन को जीवित रखता है;
  • पुनः प्रयास (retry) तर्क एक नया कनेक्शन बनाता है जबकि पिछला प्रयास पेंडिंग रहता है।

कोड संरचना में लाइफटाइम ओनरशिप को दृश्यमान बनाएं। defer, finally, RAII, या फ्रेमवर्क के स्कोप्ड रिसोर्स मैनेजमेंट का उपयोग करें। अधिग्रहण के तुरंत बाद क्लीनअप स्थापित करें। पूल्स, कन्करेंसी, कतारों और पुनः प्रयासों को सीमित करें। रद्दीकरण, टाइमआउट और अर्ली-रिटर्न पाथ्स को उसी क्लीनअप लॉजिक के माध्यम से रूट करें।

एक मल्टीथ्रेडेड प्रोग्राम में, open() को कॉल करना और बाद के ऑपरेशन में FD_CLOEXEC सेट करना एक विंडो बनाता है जिसमें एक अन्य थ्रेड fork और exec कर सकता है। जहाँ समर्थित हो, निर्माण के समय ही O_CLOEXEC को एटॉमिक रूप से सेट करें। यह एक इनहेरिटेंस रेस को रोकता है; यह सामान्य close() ओनरशिप की जगह नहीं लेता है।

चरण 7: इंसिडेंट मिटिगेशन को स्थायी समाधान (Durable Fix) से अलग करें

जोखिम के आधार पर, इंसिडेंट मिटिगेशन में शामिल हो सकते हैं:

  • नए काम को रेट-लिमिट करना या प्रति-इंस्टेंस कन्करेंसी को कम करना इससे पहले कि प्रोसेस लॉग्स, कंट्रोल कनेक्शन्स और कॉन्फ़िगरेशन फाइल्स खोलने की क्षमता खो दे;
  • लीक करने वाले इंस्टेंसेस को रोल करना जबकि कम से कम एक डायग्नोस्टिक सैंपल को संरक्षित रखना और एक साथ रीस्टार्ट से बचना;
  • मेमोरी, कर्नेल ओवरहेड और डाउनस्ट्रीम कैपेसिटी की जाँच के बाद वास्तविक सर्विस प्रोसेस की सॉफ्ट और हार्ड लिमिट्स को बढ़ाना;
  • इंस्टेंसेस जोड़ना ताकि वैध कन्करेंसी अधिक प्रोसेसेज में वितरित हो सके।

वर्तमान टर्मिनल में ulimit -n को बदलने से पहले से चल रही सर्विस प्रभावित नहीं होती है। सर्विस मैनेजर, कंटेनर या रनटाइम को संशोधित करें जो वास्तव में प्रोसेस बनाता है, इसे रीस्टार्ट करें और /proc/<new-pid>/limits को सत्यापित करें। केवल एक कॉन्फ़िगरेशन फाइल संपादन इस बात का प्रमाण नहीं है कि नई लिमिट सक्रिय है।

स्थायी समाधान को पुष्ट बढ़ते ऑब्जेक्ट को लक्षित करना चाहिए: क्लोज पाथ्स को पूरा करें, पूल्स और कन्करेंसी को सीमित करें, रोटेशन या वॉचर लाइफसाइकल्स की मरम्मत करें, उपयोगी टाइमआउट सेट करें, अनपेक्षित इनहेरिटेंस को रोकें, और वर्तमान काउंट्स के साथ-साथ प्रमुख संसाधन श्रेणियों के लिए क्रिएट/रिलीज काउंटर्स को उजागर करें।

चरण 8: कैपेसिटी बजट और स्लोप के साथ फिक्स को साबित करें

प्रति-इंस्टेंस FD बजट बनाएं:

baseline FDs + peak inbound connections + peak outbound connections + pools and files + IPC/event objects + safety headroom

बजट वास्तविक आर्किटेक्चर और लोड टेस्ट्स से आना चाहिए। प्रत्येक सर्विस के लिए कोई सार्वभौमिक उपयोग प्रतिशत नहीं है। लिमिट को एक इंसिडेंट के दौरान डायग्नोस्टिक्स, हेल्थ चेक, लॉगिंग और कंट्रोल कनेक्शन्स के लिए भी जगह रखनी चाहिए।

सत्यापन में कम से कम शामिल होना चाहिए:

  1. लक्षित पीक ट्रैफिक और इंजेक्ट की गई विफलताओं के तहत, कुल FD उपयोग एक स्थिर पठार तक बढ़ता है;
  2. लोड कम होने और टाइमआउट समाप्त होने के बाद, अल्पकालिक डिस्क्रिप्टर्स अपेक्षित बेसलाइन पर लौट आते हैं;
  3. पूर्व में बढ़ने वाले ऑब्जेक्ट प्रकार में अब लगातार धनात्मक स्लोप नहीं है;
  4. EMFILE, ENFILE, कनेक्शन विफलताएं, और फाइल-ओपन विफलताएं शून्य पर बनी रहती हैं;
  5. अत्यधिक आक्रामक लिमिट्स के कारण रिक्वेस्ट p95/p99, पूल प्रतीक्षा समय, और रिट्राई वॉल्यूम ख़राब नहीं होते हैं;
  6. बार-बार डिप्लॉयमेंट, लॉग रोटेशन, कॉन्फ़िगरेशन रीलोड्स और चाइल्ड-प्रोसेस लॉन्च सीढ़ीदार (staircase) वृद्धि नहीं बनाते हैं;
  7. मॉनिटरिंग में process_open_fds, process_max_fds, उपयोग, वृद्धि दर और प्रमुख ऑब्जेक्ट पूल्स शामिल हैं।

“दस मिनट का लोड टेस्ट पास हो गया” एक धीमे लीक को मिस कर सकता है। मूल इंसिडेंट का कारण बनने वाली वृद्धि की मात्रा को कवर करने के लिए पर्याप्त समय तक चलाएं, या संदिग्ध पाथ को प्रवर्धित (amplify) करें और साबित करें कि क्रिएट और रिलीज काउंटर्स संतुलित हैं।

मजबूत नमूना उत्तर

“मैं सबसे पहले पुष्टि करूँगा कि errno EMFILE है और API वर्कर स्वयं विफल हो रहा है। एक फाइल डिस्क्रिप्टर प्रोसेस FD टेबल में एक गैर-ऋणात्मक इंडेक्स है। टेबल एंट्री एक सिस्टम-व्यापी ओपन फाइल डिस्क्रिप्शन को संदर्भित करती है, जो ऑफसेट और फाइल-स्टेटस फ्लैग्स को स्टोर करती है और फिर एक रेगुलर फाइल, सॉकेट, पाइप, या अनाम कर्नेल ऑब्जेक्ट को संदर्भित करती है। dup और fork कई डिस्क्रिप्टर्स को एक ओपन फाइल डिस्क्रिप्शन साझा करा सकते हैं, इसलिए प्रोसेस FD काउंट और सिस्टम-व्यापी फाइल-हैंडल काउंट अलग-अलग मेट्रिक्स हैं।

EMFILE का अर्थ है कि यह प्रोसेस RLIMIT_NOFILE तक पहुँच गया है; ENFILE का अर्थ है कि होस्ट file-max तक पहुँच गया है। यहाँ, सॉफ्ट लिमिट 8,192 है, घटना से पहले की संख्या उस मान के करीब है, और file-nr, file-max से बहुत नीचे है, इसलिए सिस्टम-व्यापी लिमिट को बदलना लक्षित नहीं है।

मैं /proc/<pid>/limits को पढ़ूँगा, फिर /proc/<pid>/fd में काउंट और सिमलिंक लक्ष्यों का सैंपल लूँगा, जिन्हें सॉकेट्स, पाइप्स, रेगुलर फाइल्स और anon_inode ऑब्जेक्ट्स में समूहीकृत किया जाएगा। मैं प्रत्येक श्रेणी के स्लोप को इनबाउंड कनेक्शन्स, अपस्ट्रीम पूल्स, फाइल रोटेशन, चाइल्ड प्रोसेसेज और एरर्स के साथ संरेखित करूँगा। 400 के करीब शुरू होना और स्थिर ट्रैफिक के तहत 25 प्रति मिनट की दर से बढ़ना, रीस्टार्ट पर रीसेट होना, दृढ़ता से एक लीक का संकेत देता है। यदि किसी एक अपस्ट्रीम के सॉकेट्स हावी हैं, तो मैं यह मानने के बजाय कि प्रत्येक कनेक्शन वैध ट्रैफिक को दर्शाता है, रिस्पॉन्स-बॉडी क्लोज, टाइमआउट कैंसिलेशन और पूल-रिटर्न पाथ्स का निरीक्षण करूँगा।

मिटिगेशन के लिए, मैं एक डायग्नोस्टिक सैंपल को संरक्षित करते हुए इंस्टेंसेस को रेट-लिमिट और रोल करूँगा। यदि कैपेसिटी विश्लेषण अनुमति देता है, तो मैं वास्तविक सर्विस मैनेजर या कंटेनर कॉन्फ़िगरेशन में लिमिट को अस्थायी रूप से बढ़ा सकता हूँ, लेकिन मुझे नई प्रोसेस के /proc/<pid>/limits को सत्यापित करना होगा। स्थायी समाधान रिसोर्स ओनरशिप और क्लीनअप को संरचनात्मक बनाता है, पूल्स, रिट्राई, टाइमर और वॉचर्स को सीमित करता है, और close-on-exec के साथ अनपेक्षित इनहेरिटेंस को रोकता है।

मैं लक्षित पीक लोड और विफलता पाथ्स के तहत सत्यापित करूँगा। FD उपयोग को नियोजित पठार तक पहुँचना चाहिए और लोड गिरने पर बेसलाइन की ओर लौटना चाहिए। पूर्व में बढ़ने वाली श्रेणी का संचय रुकना चाहिए, EMFILE शून्य पर रहना चाहिए, और टेल लेटेंसी और पूल वेट्स में गिरावट नहीं आनी चाहिए। वे परिणाम एक फिक्स साबित करते हैं; एक रीस्टार्ट या बड़ी लिमिट केवल यह साबित करती है कि थकावट (exhaustion) में देरी हुई थी।”

सामान्य गलतियाँ

  • एक FD को एक पाथ या इनोड मानना → यह एक प्रोसेस टेबल में एक इंडेक्स है → FD टेबल, ओपन फाइल डिस्क्रिप्शन और अंतर्निहित ऑब्जेक्ट को समझाएं।
  • यह मान लेना कि केवल डिस्क फाइल्स ही FDs का उपभोग करती हैं → सॉकेट्स, पाइप्स, epoll, टाइमर और वॉचर्स भी उनका उपयोग करते हैं → /proc/<pid>/fd लक्ष्यों को वर्गीकृत करें।
  • प्रत्येक Too many open files एरर के लिए fs.file-max को बदलना → EMFILE और ENFILE के अलग-अलग स्कोप हैं → पहले errno और विफल PID की पुष्टि करें।
  • वर्तमान शेल के ulimit -n को सर्विस लिमिट के रूप में उपयोग करना → चल रही सर्विस कहीं और लॉन्च की गई हो सकती है → /proc/<pid>/limits को पढ़ें।
  • केवल इसलिए लीक घोषित करना क्योंकि संख्या अधिक है → वैध उच्च कन्करेंसी एक उच्च पठार बना सकती है → कैपेसिटी मॉडल, प्रकार मिश्रण, स्लोप और लोड के बाद रिकवरी की तुलना करें।
  • केवल 8,192 को बढ़ाकर 65,536 करना → एक निश्चित लीक स्लोप नई लिमिट को भी समाप्त कर देगा → वृद्धि को मान्य कैपेसिटी या अस्थायी हेडरूम के रूप में समझें।
  • केवल सामान्य रिटर्न पाथ की जाँच करना → टाइमआउट, कैंसिलेशन, रिट्राई और अर्ली रिटर्न्स आमतौर पर क्लीनअप छोड़ देते हैं → प्रत्येक संसाधन के लिए क्रिएटर, ओनर और विफलता-पाथ क्लीनअप को परिभाषित करें।
  • केवल एक lsof स्नैपशॉट का उपयोग करना → एक स्नैपशॉट संचय (accumulation) साबित नहीं कर सकता → निश्चित अंतराल पर सैंपल लें और वर्कलोड और पूल्स के साथ सहसंबद्ध करें।
  • अस्थायी एरर गायब होने को स्वीकार करना → रीस्टार्ट और उच्च लिमिट्स पुनरावृत्ति में देरी कर सकते हैं → ऑब्जेक्ट प्रकार, स्लोप, रिकवरी और दोहराए गए लाइफसाइकिल इवेंट्स को सत्यापित करें।

फॉलो-अप प्रश्न

अनुवर्ती प्रश्न 1: ulimit -n 65,536 क्यों दिखाता है जबकि सर्विस अभी भी 8,192 पर विफल होती है?

ulimit सामान्य रूप से वर्तमान शेल की रिपोर्ट करता है और उसके बाद बनाए गए वंशजों (descendants) को प्रभावित करता है। systemd, एक कंटेनर रनटाइम, या किसी अन्य प्रोसेस मैनेजर द्वारा पहले से लॉन्च की गई सर्विस पूर्वव्यापी रूप से नहीं बदली जाती है। एक सुपरवाइजर और उसके वर्कर्स की भी अलग-अलग सेटिंग्स हो सकती हैं। विफल होने वाले PID के /proc/<pid>/limits को पढ़ें, वास्तविक लॉन्च सीमा को अपडेट करें और नए PID को सत्यापित करें।

अनुवर्ती प्रश्न 2: एक ही फाइल के दो डिस्क्रिप्टर्स एक-दूसरे की रीड पोजीशन को क्यों प्रभावित कर सकते हैं?

यदि वे dup से आए हैं, या fork से पहले एक ही डिस्क्रिप्टर से आए हैं, तो वे एक ही ओपन फाइल डिस्क्रिप्शन को संदर्भित करते हैं और इसलिए ऑफसेट और फाइल-स्टेटस फ्लैग्स साझा करते हैं। यदि प्रोग्राम दो बार open() को कॉल करता है, तो वही पाथ सामान्य रूप से स्वतंत्र ऑफसेट के साथ दो स्वतंत्र ओपन फाइल डिस्क्रिप्शन्स बनाता है। एक साझा पाथनेम साझा ओपन स्थिति को साबित नहीं करता है।

अनुवर्ती प्रश्न 3: फाइल हटाए जाने के बाद भी कोई प्रोसेस FD के माध्यम से फाइल का उपयोग क्यों कर सकता है?

डिस्क्रिप्टर एक ओपन फाइल डिस्क्रिप्शन को संदर्भित करता है; I/O हर बार पाथनेम को फिर से हल नहीं करता है। डायरेक्टरी एंट्री को हटाने से मौजूदा संदर्भ अमान्य नहीं होते हैं। अंतर्निहित ऑब्जेक्ट तब तक बना रह सकता है जब तक कि अंतिम संदर्भ बंद न हो जाए। लॉग रोटेशन जो प्रोसेस को बंद किए बिना एक पुरानी फाइल को अनलिंक करता है, एक डिस्क्रिप्टर और डिस्क स्पेस दोनों का उपभोग कर सकता है।

अनुवर्ती प्रश्न 4: RLIMIT_NOFILE को बहुत बड़ी संख्या में बढ़ाना क्यों विफल हो सकता है?

एक गैर-विशेषाधिकार प्राप्त प्रोसेस सॉफ्ट लिमिट को हार्ड लिमिट से ऊपर नहीं बढ़ा सकता है या अपनी हार्ड लिमिट को स्वतंत्र रूप से नहीं बढ़ा सकता है। Linux RLIMIT_NOFILE को /proc/sys/fs/nr_open के साथ भी सीमित करता है। एक सर्विस मैनेजर, कंटेनर या अनुमति सीमा बाधाएं जोड़ सकती है। हर बदलाव के बाद स्टार्टअप एरर्स और नई प्रोसेस की प्रभावी लिमिट्स की जाँच करें।

अनुवर्ती प्रश्न 5: यदि एक epoll इंस्टेंस कई कनेक्शन्स को देखता है, तो प्रोसेस अभी भी FDs को समाप्त क्यों कर सकता है?

epoll इंस्टेंस स्वयं एक FD का उपभोग करता है और anon_inode:[eventpoll] के रूप में दिखाई देता है। प्रत्येक मॉनिटर किया गया सॉकेट एक अलग FD बना रहता है। epoll कई कनेक्शन्स पर प्रतीक्षा को कुशल बनाता है; यह हजारों सॉकेट्स को एक डिस्क्रिप्टर में संयोजित नहीं करता है या एप्लिकेशन के लिए उन्हें बंद नहीं करता है।

अनुवर्ती प्रश्न 6: आप FD उपयोग पर अलर्ट कैसे सेट करेंगे?

वर्तमान में खुले डिस्क्रिप्टर्स, प्रोसेस अधिकतम, उपयोग (utilization) और प्रति इंस्टेंस वृद्धि दर की निगरानी करें। उपयोग थकावट की निकटता का पता लगाता है; स्लोप धीमे लीक्स का पहले पता लगाता है। कनेक्शन पूल्स, फाइल्स और वॉचर्स को भी अपनी वर्तमान संख्या को उजागर करना चाहिए। एक सार्वभौमिक प्रतिशत की नकल करने के बजाय पीक बजट, स्केलिंग समय और इंसिडेंट-रिस्पॉन्स लीड समय से थ्रेशोल्ड सेट करें।

सार्वजनिक स्रोत

संबंधित प्रश्न