निष्पादन के दौरान बाइनरी को संशोधित करना


10

मैं अक्सर विकसित होते समय स्थिति में आता हूं, जहां मैं एक बाइनरी फ़ाइल चला रहा हूं, a.outपृष्ठभूमि में कहो क्योंकि यह कुछ लंबा काम करता है। जब वह ऐसा कर रहा होता है, मैं सी कोड में बदलाव करता हूं जो फिर से उत्पादन a.outऔर संकलन करता है a.out। अब तक, मुझे इससे कोई समस्या नहीं है। जो प्रक्रिया चल a.outरही है वह सामान्य रूप से जारी है, कभी क्रैश नहीं होती है, और हमेशा पुराने कोड को चलाता है जिसमें से यह मूल रूप से शुरू हुआ था।

हालाँकि, कहना a.outएक बहुत बड़ी फ़ाइल थी, शायद रैम के आकार की तुलना में। इस मामले में क्या होगा? और कहें कि यह एक साझा ऑब्जेक्ट फ़ाइल से जुड़ा हुआ है libblas.so, क्या होगा, अगर मैं libblas.soरनटाइम के दौरान संशोधित हो? क्या हुआ होगा?

मेरा मुख्य सवाल यह है कि - ओएस गारंटी नहीं है कि जब मैं चलाने करता है a.out, तो मूल कोड हमेशा सामान्य रूप से चलाया जाएगा, के अनुसार मूल बाइनरी या के आकार के द्विआधारी, परवाह किए बिना .soफ़ाइलों इससे लिंक, तब भी जब उन .oऔर .soफ़ाइलों के दौरान modfied कर रहे हैं क्रम?

मुझे पता है कि ये ऐसे सवाल हैं जो समान मुद्दों को संबोधित करते हैं: /programming/8506865/when-a-binary-file-runs-does-it-copy-its-entire-binary-tata-into-memory -ट-एक बार यदि आप किसी स्क्रिप्ट को निष्पादन के दौरान संपादित करते हैं तो क्या होता है? किसी प्रोग्राम को चलाने के दौरान लाइव अपडेट करना कैसे संभव है?

जिसने मुझे इस बारे में थोड़ा और समझने में मदद की है, लेकिन मुझे नहीं लगता कि वे वास्तव में मुझसे क्या चाहते हैं, जो निष्पादन के दौरान एक बाइनरी को संशोधित करने के परिणामों के लिए एक सामान्य नियम है।


मेरे लिए, आपके द्वारा लिंक किए गए प्रश्न (विशेषकर स्टैक ओवरफ्लो वन) पहले से ही इन परिणामों (या इसके अभाव) को समझने में महत्वपूर्ण सहायता प्रदान करते हैं। चूंकि कर्नेल आपके प्रोग्राम को मेमोरी टेक्स्ट क्षेत्रों / खंडों में लोड करता है , इसलिए इसे फ़ाइल सबसिस्टम के माध्यम से किए गए परिवर्तनों से प्रभावित नहीं होना चाहिए।
जॉन डब्ल्यूएच स्मिथ

@JohnWHSmith Stackoverflow पर, शीर्ष उत्तर कहता है if they are read-only copies of something already on disc (like an executable, or a shared object file), they just get de-allocated and are reloaded from their source, इसलिए मुझे यह आभास हुआ कि यदि आपका बाइनरी बहुत बड़ा है, तो यदि आपके बाइनरी का एक भाग RAM से बाहर जाता है, लेकिन तब उसे फिर से "स्रोत से पुनः लोड" करने की आवश्यकता होती है - इसलिए इसमें कोई परिवर्तन .(s)oफ़ाइल निष्पादन के दौरान परिलक्षित होगा। लेकिन निश्चित रूप से मुझे गलतफहमी हो सकती है - यही कारण है कि मैं यह और अधिक विशिष्ट प्रश्न पूछ रहा हूं
टेक्ससफ्लड

@JohnWHSmith इसके अलावा दूसरा जवाब कहता है, No, it only loads the necessary pages into memory. This is demand paging.इसलिए मैं वास्तव में इस धारणा के तहत था कि मैंने जो मांगा, उसकी गारंटी नहीं दी जा सकती।
टेक्ससफ्लड

जवाबों:


11

हालांकि स्टैक ओवरफ्लो प्रश्न पहले ही पर्याप्त लग रहा था, मैं समझता हूं, आपकी टिप्पणियों से, आपको अभी भी इस बारे में संदेह क्यों हो सकता है। मेरे लिए, यह दो UNIX उपप्रणालियों (प्रक्रियाओं और फाइलों) को संप्रेषित करने पर ठीक उसी प्रकार की महत्वपूर्ण स्थिति है, जिसमें शामिल है।

जैसा कि आप जानते हैं, UNIX सिस्टम आमतौर पर दो सबसिस्टम में विभाजित होते हैं: फाइल सबसिस्टम, और प्रोसेस सबसिस्टम। अब, जब तक कि इसे निर्देश नहीं दिया जाता है अन्यथा एक सिस्टम कॉल के माध्यम से, कर्नेल में ये दोनों उपतंत्र एक दूसरे के साथ बातचीत नहीं करनी चाहिए। हालांकि एक अपवाद है: एक निष्पादन योग्य फ़ाइल को एक प्रक्रिया के पाठ क्षेत्रों में लोड करना । बेशक, कोई यह तर्क दे सकता है कि इस ऑपरेशन को सिस्टम कॉल ( execve) द्वारा भी ट्रिगर किया गया है , लेकिन यह आमतौर पर एक मामले के लिए जाना जाता है जहां प्रक्रिया सबसिस्टम फ़ाइल सबसिस्टम के लिए एक अंतर्निहित अनुरोध करता है।

क्योंकि प्रक्रिया सबसिस्टम में स्वाभाविक रूप से फ़ाइलों को संभालने का कोई तरीका नहीं है (अन्यथा पूरी चीज़ को दो में विभाजित करने का कोई मतलब नहीं होगा), इसमें फ़ाइलों का उपयोग करने के लिए फ़ाइल सबसिस्टम को प्रदान करने के लिए जो भी उपयोग करना होगा। इसका अर्थ यह भी है कि फ़ाइल उप संस्करण / विलोपन के बारे में फ़ाइल सबसिस्टम को मापने के लिए जो कुछ भी उपाय किया जाता है वह सब-सिस्टम सबमिशन प्रस्तुत करता है। इस बिंदु पर, मैं इस U & L प्रश्न के बारे में गिल्स के उत्तर को पढ़ने की सलाह दूंगा । मेरा बाकी का जवाब गिल्स के इस सामान्य से अधिक पर आधारित है।

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

यदि आपके पास गिल्स के उत्तर के निष्पादन योग्य भाग की जगह पर एक नज़र है , तो आप देखेंगे कि आप फ़ाइल को कैसे संपादित / हटाते हैं, इसके आधार पर कर्नेल अलग-अलग तरीके से प्रतिक्रिया / अनुकूलन करेगा, जो हमेशा फ़ाइल सबसिस्टम के भीतर कार्यान्वित तंत्र के माध्यम से होता है।

  • यदि आप एक रणनीति बनाते हैं ( शून्य / लिखना / खोलना / लिखना या खोलना / लिखना / नए आकार में छोटा करना ), तो आप देखेंगे कि कर्नेल आपके अनुरोध को संभालने में परेशान नहीं करेगा। आपको एक त्रुटि 26 मिलेगी: पाठ फ़ाइल व्यस्त ( ETXTBSY)। चाहे कोई भी परिणाम हो।
  • यदि आप दो रणनीति की कोशिश करते हैं, तो पहला कदम आपके निष्पादन योग्य को हटाना है। हालाँकि, चूंकि यह एक प्रक्रिया द्वारा उपयोग किया जा रहा है, फ़ाइल सबसिस्टम डिस्क में से फ़ाइल को (और इसके इनसाइड) को सही मायने में डिलीट करने से रोक देगा । इस बिंदु से, पुरानी फ़ाइल की सामग्री तक पहुंचने का एकमात्र तरीका यह है कि इसे अपने इनोड के माध्यम से किया जाए, यही प्रक्रिया सबसिस्टम तब करता है जब उसे नए डेटा को पाठ अनुभागों में लोड करने की आवश्यकता होती है (आंतरिक रूप से, रास्तों का उपयोग करने का कोई मतलब नहीं है, सिवाय जब उन्हें इनोड में अनुवाद करते हुए)। भले ही आपने अनलिंक किया होफ़ाइल (उसके सभी पथ हटा दिए गए), प्रक्रिया अभी भी इसका उपयोग कर सकती है जैसे कि आपने कुछ नहीं किया। पुराने पथ के साथ एक नई फ़ाइल बनाने से कुछ भी नहीं बदलता है: नई फ़ाइल को पूरी तरह से नया इनकोड दिया जाएगा, जिसके चलने की प्रक्रिया का कोई ज्ञान नहीं है।

रणनीतियाँ 2 और 3 निष्पादनयोग्य के लिए भी सुरक्षित हैं: यद्यपि निष्पादन योग्य (और गतिशील रूप से भरी हुई लाइब्रेरी) फ़ाइल डिस्क्रिप्टर होने के अर्थ में फाइलें नहीं खुली हैं, वे बहुत ही समान तरीके से व्यवहार करते हैं। जब तक कोई प्रोग्राम कोड चला रहा होता है, तब तक फ़ाइल बिना डिस्क प्रविष्टि के भी डिस्क पर रहती है।

  • रणनीति तीन काफी समान है क्योंकि mvऑपरेशन एक परमाणु है। यह संभवतः renameसिस्टम कॉल के उपयोग की आवश्यकता होगी , और चूंकि कर्नेल मोड में प्रक्रियाओं को बाधित नहीं किया जा सकता है, इस ऑपरेशन के पूरा होने तक कुछ भी हस्तक्षेप नहीं कर सकता (सफलतापूर्वक या नहीं)। फिर से, पुरानी फ़ाइल के इनकोड में कोई परिवर्तन नहीं होता है: एक नया बनाया जाता है, और पहले से चल रही प्रक्रियाओं को इसका कोई ज्ञान नहीं होगा, भले ही यह पुराने इनोड के लिंक में से एक के साथ जुड़ा हो।

रणनीति 3 के साथ, नई फ़ाइल को मौजूदा नाम पर ले जाने का चरण पुरानी सामग्री के लिए निर्देशिका प्रविष्टि को हटाता है और नई सामग्री के लिए निर्देशिका प्रविष्टि बनाता है। यह एक परमाणु संचालन में किया जाता है, इसलिए इस रणनीति का एक बड़ा फायदा होता है: यदि किसी भी समय फ़ाइल एक प्रक्रिया को खोलती है, तो यह या तो पुरानी सामग्री या नई सामग्री को देखेगा - मिश्रित सामग्री या फ़ाइल नहीं होने का कोई जोखिम नहीं है मौजूदा।

किसी फ़ाइल को पुनःप्रमाणित करना : जब उपयोग करना gcc(और व्यवहार संभवतः कई अन्य संकलक के लिए समान है), आप रणनीति 2 का उपयोग कर रहे हैं। आप देख सकते हैं कि straceआपके कंपाइलर की प्रक्रियाओं को चलाकर :

stat("a.out", {st_mode=S_IFREG|0750, st_size=8511, ...}) = 0
unlink("a.out") = 0
open("a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
chmod("a.out", 0750) = 0
  • कंपाइलर पता लगाता है कि फ़ाइल पहले से मौजूद है statऔर lstatसिस्टम कॉल के माध्यम से ।
  • फाइल अनलिंक है । यहां, जबकि यह अब नाम के माध्यम से सुलभ नहीं है a.out, इसकी इनोड और सामग्री डिस्क पर बनी हुई है, जब तक कि वे पहले से चल रही प्रक्रियाओं द्वारा उपयोग किए जा रहे हैं।
  • नाम के तहत एक नई फ़ाइल बनाई और निष्पादित की जाती है a.out। यह एक बिलकुल नया आईनोड है, और एकदम नई सामग्री, जो पहले से चल रही प्रक्रियाओं की परवाह नहीं करती है।

अब, जब साझा लाइब्रेरी की बात आती है, तो वही व्यवहार लागू होगा। जब तक एक लाइब्रेरी ऑब्जेक्ट का उपयोग एक प्रक्रिया द्वारा किया जाता है, तब तक इसे डिस्क से हटाया नहीं जाएगा, भले ही आप इसके लिंक को कैसे बदलें। जब भी किसी चीज़ को मेमोरी में लोड करना होता है, तो कर्नेल इसे फाइल के इनकोड के माध्यम से करेगा, और इसलिए आप इसके लिंक में किए गए परिवर्तनों को अनदेखा करेंगे (जैसे कि उन्हें नई फ़ाइलों के साथ जोड़ना)।


शानदार, विस्तृत जवाब। यह मेरा भ्रम समझाता है। तो क्या मैं यह मानने में सही हूं कि क्योंकि इनोड अभी भी उपलब्ध है, मूल बाइनरी फ़ाइल से डेटा अभी भी डिस्क पर है और इसलिए dfडिस्क पर फ्री बाइट की संख्या को काम करने के लिए उपयोग करना गलत है क्योंकि यह इनोड नहीं लेता है क्या सभी फ़ाइल सिस्टम लिंक को ध्यान में रखा गया है? तो मुझे उपयोग करना चाहिए df -i? (यह सिर्फ एक तकनीकी जिज्ञासा है, मुझे वास्तव में सटीक डिस्क उपयोग जानने की आवश्यकता नहीं है!)
टेक्ससफ्लड

1
भविष्य के पाठकों के लिए स्पष्ट करने के लिए - मेरा भ्रम यह था कि मैंने सोचा था कि निष्पादन पर, पूरे बाइनरी को रैम में लोड किया जाएगा, इसलिए यदि रैम छोटा था, तो बाइनरी का हिस्सा रैम को छोड़ देगा और डिस्क से पुनः लोड करना होगा - जो यदि आपने फ़ाइल बदली है तो समस्याएँ उत्पन्न करें। लेकिन उत्तर ने यह स्पष्ट कर दिया है कि द्विआधारी को डिस्क से वास्तव में कभी भी हटाया नहीं जाता है भले ही आप rmया mvयह मूल फ़ाइल के लिए इनोड के रूप में हटाया न जाए, जब तक कि सभी प्रक्रियाएं उस इनोड से अपने लिंक को हटा नहीं देती हैं।
टेक्ससफ्लड

@texasflood बिल्कुल। एक बार सभी रास्तों को हटा दिए जाने के बाद, कोई भी नई प्रक्रिया ( dfशामिल) इनोड के बारे में जानकारी प्राप्त नहीं कर सकती है। आपको जो भी नई जानकारी मिलती है, वह नई फ़ाइल से संबंधित होती है, और नई इनकोड के लिए। यहां मुख्य बिंदु यह है कि प्रक्रिया उपतंत्र को इस समस्या में कोई दिलचस्पी नहीं है, इसलिए स्मृति प्रबंधन (मांग पृष्ठन, प्रक्रिया स्वैपिंग, पृष्ठ दोष, ...) की धारणाएं पूरी तरह से अप्रासंगिक हैं। यह एक फ़ाइल सबसिस्टम समस्या है, और यह फ़ाइल सबसिस्टम द्वारा ध्यान रखा जाता है। प्रक्रिया सबसिस्टम उस के साथ परेशान नहीं करता है, यही वह नहीं है जिसके लिए वह यहां है।
जॉन डब्ल्यूएच स्मिथ

@texasflood ए नोट के बारे में df -i: यह उपकरण संभवतः fs के सुपरब्लॉक, या इसके कैश से जानकारी प्राप्त करता है, जिसका अर्थ है कि इसमें पुराने बाइनरी के इनकोड शामिल हो सकते हैं (जिसके लिए सभी लिंक हटा दिए गए हैं)। इसका मतलब यह नहीं है कि नई प्रक्रियाएं उस पुराने डेटा का उपयोग करने के लिए स्वतंत्र हैं, हालांकि।
जॉन डब्ल्यूएच स्मिथ

2

मेरी समझ यह है कि रनिंग प्रक्रिया की मेमोरी मैपिंग के कारण, कर्नेल मैप किए गए फ़ाइल के आरक्षित हिस्से को अपडेट करने की अनुमति नहीं देगा। मुझे लगता है कि यदि कोई प्रक्रिया चल रही है, तो उसकी सभी फ़ाइल आरक्षित है, इसलिए इसे अपडेट करना क्योंकि आपने अपने स्रोत का एक नया संस्करण संकलित किया है जिसके परिणामस्वरूप वास्तव में एक नया सेट बनाया जाता है। संक्षेप में, पृष्ठ निष्पादन की घटनाओं के माध्यम से डिस्क पर आपके निष्पादन योग्य पुराने संस्करण मौजूद हैं। इसलिए यदि आप एक बड़ी फ़ाइल को अपडेट करते हैं, तो भी इसे एक्सेस किया जाना चाहिए और कर्नेल को अभी भी अनछुए संस्करण को तब तक देखना चाहिए जब तक प्रक्रिया चल रही है। जब तक प्रक्रिया चल रही है, तब तक मूल फ़ाइल इनकोड का पुन: उपयोग नहीं किया जाना चाहिए

इस पाठ्यक्रम की पुष्टि करनी होगी।


2

.Jar फ़ाइल को प्रतिस्थापित करते समय हमेशा ऐसा नहीं होता है। जार संसाधन और कुछ रनटाइम प्रतिबिंब क्लास लोडर डिस्क से नहीं पढ़े जाते हैं जब तक कि कार्यक्रम स्पष्ट रूप से जानकारी का अनुरोध नहीं करता है।

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

इसलिए निष्पादनयोग्य के लिए: हाँ। जार फ़ाइलों के लिए: हो सकता है (कार्यान्वयन के आधार पर)।

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