लिनक्स ड्राइवर विकल्पों पर, या मैंने सप्ताहांत कैसे बिताया

"हम आलसी और उत्सुक हैं"




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

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

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

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

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

हमें जिस मॉड्यूल की आवश्यकता है उसे कॉन्फ़िगर करने के लिए:

  1. फ़ॉर्म (ठीक है, यह संकलन के स्तर पर है, आप इसे अपनी इच्छानुसार कर सकते हैं, हालाँकि यह अभी भी दिलचस्प है) और उपरोक्त सेटिंग्स की एक तालिका को संग्रहीत करें,
  2. इस तालिका के अनुसार पार्सिंग इनपुट पैरामीटर और
  3. वाक्य-रचना इकाई को पार्स करने के परिणाम के अनुसार स्मृति के कुछ क्षेत्रों में परिवर्तन करें।

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

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

इसलिए, एक निश्चित मानक पार्सर को स्वचालित रूप से मॉड्यूल के पाठ में शामिल किया जाना चाहिए, यह मैक्रो प्रतिस्थापन स्तर पर लागू करना आसान है।

इस समाधान में दो कमियां हैं:

  1. यह स्पष्ट नहीं है कि हमें इसकी आवश्यकता क्यों है। और, आप तुरंत कमांड लाइन से पैरामीटर के साथ मॉड्यूल को कॉल कर सकते हैं,
  2. मॉड्यूल कोड (आरंभीकरण भाग) में आवश्यक जानकारी के सभी तीन खंड शामिल होने चाहिए, और यह जानकारी केवल तब आवश्यक होती है जब मॉड्यूल शुरू किया जाता है और भविष्य में इसका उपयोग नहीं किया जाता है, और यह हमेशा जगह लेता है। तुरंत एक आरक्षण करें कि यह जानकारी आवश्यक रूप से फ़ाइल में जगह लेती है, लेकिन यह मॉड्यूल लोड करते समय मेमोरी में नहीं जा सकता है, अगर सब कुछ सावधानी से किया जाता है। ऐसा करने के लिए, हम निर्देशों को याद करते हैं _init और _inddata (वैसे, लेकिन वे कैसे काम करते हैं, हमें यह पता लगाना होगा - यह अगले पोस्ट का विषय है - क्या आप इसके लिए तत्पर होंगे?)। लेकिन बाद के मामले में, फ़ाइल में जानकारी के अनुभाग 2 और 3 स्पष्ट रूप से बेमानी हैं, क्योंकि एक ही कोड कई मॉड्यूल में मौजूद होगा, दुर्भावनापूर्ण रूप से DRY सिद्धांत का उल्लंघन कर रहा है।

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

विख्यात खामी नंबर 2 के बारे में आवश्यक विषयांतर - मुझे उन दिनों एक विशेषज्ञ के रूप में बनाया गया था जब 256 kbytes RAM 4 कार्यस्थानों को व्यवस्थित करने के लिए पर्याप्त था, 56 kbytes में एक दोहरी-टास्किंग OS था, और एक एकल-tas OS ने 16 kbytes पर काम करना शुरू किया था। खैर, 650 केबी, जो किसी भी कार्यक्रम के लिए पर्याप्त होना चाहिए, आम तौर पर अवैज्ञानिक कथा के क्षेत्र से कुछ था। इसलिए, मैं रैम को एक दुर्लभ संसाधन मानने का आदी हूं और अगर मैं आपातकाल (एक नियम के रूप में, प्रदर्शन की आवश्यकताओं), और इस मामले में ऐसी स्थिति का निरीक्षण नहीं करता हूं, तो मैं इसके बेकार उपयोग को रोकने में बेहद निराश हूं। चूंकि मेरे अधिकांश पाठकों ने विभिन्न वास्तविकताओं में गठन किया है, इसलिए आपके पास इस या उस विकल्प की प्राथमिकता के अपने आकलन हो सकते हैं।

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

दूसरे समाधान का एक उप-विकल्प निकाले गए मापदंडों को मॉड्यूल के प्रारंभ भाग में स्थानांतरित नहीं करना है, लेकिन सीधे इसके लोड किए गए कार्य भाग के लिए, उदाहरण के लिए, ioctl के माध्यम से - स्मृति की आवश्यकताएं समान हैं। हमारे पास "मक्खी पर" मापदंडों को बदलने का एक अनूठा अवसर है, जिसे अन्य संस्करणों में लागू नहीं किया गया है। यह बहुत स्पष्ट नहीं है कि हमें इस तरह की सुविधा की आवश्यकता क्यों है, लेकिन यह सुंदर दिखता है। नुकसान 1 है) आपको संभवतः अप्रयुक्त अनुरोध के लिए अग्रिम में फ़ंक्शन क्षेत्र का एक हिस्सा आरक्षित करने की आवश्यकता है, और 2) संशोधक कोड लगातार स्मृति में मौजूद होना चाहिए। कार्यान्वयन की संभावना का अनुमान - प्रतिशत 5।

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

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

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

शुरू करने के लिए, मैं ध्यान देता हूं कि, जाहिरा तौर पर, फाइलों में स्रोत ग्रंथों को वितरित करने की कला ओएस के साथ खो गई थी, जो कि 16 केबी में फिट होती है, क्योंकि निर्देशिकाओं की संरचना के बाद से, उनके नाम और फ़ाइल नाम कुछ भी नहीं की तुलना में सामग्री के साथ जुड़े हुए हैं। एम्बेडेड समावेशन की उपस्थिति को देखते हुए, एक संपादक की मदद से डाउनलोड किए गए स्रोतों का शास्त्रीय अध्ययन एक अजीब खोज में बदल जाता है और अनुत्पादक होगा। सौभाग्य से, ऑनलाइन उपलब्ध एक आकर्षक उपयोगिता अमृत है, जो आपको प्रासंगिक खोजों को पूरा करने की अनुमति देता है, और यहां इसके साथ यह प्रक्रिया बहुत अधिक रोचक और फलदायी हो जाती है। मैंने साइट elixir.bootlin.com पर अपना आगे का शोध किया। हां, यह साइट kernel.org के विपरीत कर्नेल चीज़ों का आधिकारिक संग्रह नहीं है, लेकिन चलो आशा करते हैं कि उनके लिए स्रोत कोड समान हो।

पहले, आइए मापदंडों को निर्धारित करने के लिए एक मैक्रो को देखें - सबसे पहले, हम इसका नाम जानते हैं, और दूसरी बात, यह आसान होना चाहिए (हाँ, अब)। यह मॉड्यूलपराम.ह फ़ाइल में स्थित है - यह काफी उचित है, लेकिन यह एक सुखद आश्चर्य है, जिसे देखते हुए हम बाद में देखेंगे। मैक्रो

{0}module_param(name,type,perm) 

एक रैपर है

  {0a}module_param_named(n,n,t,p) 

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

मैक्रो {0 ए} में तीन मैक्रोज़ के लिए एक कॉल है

 {1}param_check_##t(n,&v) 

(सभी मान्य प्रकारों के लिए मैक्रोज़ का एक सेट है)

 {2}module_param_cb(n,&op##t,&v,p) 

और

 {3}__MODULE_PARM_TYPE(n,t) 

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

तीन मैक्रोज़ {1} में से पहला, जैसा कि नाम से स्पष्ट है, पैरामीटर प्रकार और आवरण के पत्राचार की जाँच करता है

 __param_check(n,p,t) 

ध्यान दें कि रैपिंग के पहले चरण में, मैक्रो एब्सट्रैक्शन का स्तर कम हो जाता है, और दूसरे पर, यह संभवतः एक अलग तरीके से बढ़ता है, और यह केवल मुझे लगता है कि यह सरल और अधिक तार्किक हो सकता है, खासकर यह देखते हुए कि औसत मैक्रो का उपयोग कहीं और नहीं किया जाता है। ठीक है, चलो गुल्लक में मैक्रो मापदंडों की जांच करने और आगे बढ़ने का एक और तरीका डालते हैं।

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

 {2a}_module_param_call(MODULE_PARAM_PREFIX,n,ops,arg,p,-1,0) 

(यह हास्यास्पद है कि इस मैक्रो को सीधे 8250_core.c को छोड़कर कहीं भी नहीं बुलाया जाता है, जहां इसे समान अतिरिक्त मापदंडों के साथ कहा जाता है), लेकिन बाद वाला पहले से ही स्रोत कोड का उत्पादन करता है।

एक छोटी सी टिप्पणी - खोज के दौरान हम यह सुनिश्चित करते हैं कि पाठ नेविगेशन अच्छी तरह से काम करता है, लेकिन दो अप्रिय परिस्थितियां हैं: नाम खंड द्वारा खोज काम नहीं करती है (check_param_ नहीं मिली थी, हालांकि check_param_byte पाया गया था) और खोज केवल ऑब्जेक्ट घोषणाओं पर काम करती है (चर नहीं मिला है, तब ctrF द्वारा इस फ़ाइल में पाया गया है, लेकिन स्रोत द्वारा अंतर्निहित खोज का पता नहीं लगा है)। बहुत उत्साहजनक नहीं है, क्योंकि हमें वर्तमान फ़ाइल के बाहर एक वस्तु की खोज करने की आवश्यकता हो सकती है, लेकिन "अंत में, हमारे पास कोई अन्य नहीं है।"

निम्नलिखित दो पंक्तियों की उपस्थिति में संकलित मॉड्यूल के पाठ में {1} के काम के परिणामस्वरूप

 module_param_named(name, c, byte, 0x444); module_param_named(name1, i, int, 0x444); 

नीचे के प्रकार का एक टुकड़ा दिखाई देता है

 static const char __param_str_name[] = "MODULE" "." "name"; static struct kernel_param const __param_name \ __attribute__((__used__)) \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ = { __param_str_name, ((struct module *)0), &param_ops_byte, (0x444), -1, 0, { &c } }; static const char __UNIQUE_ID_nametype72[] \ __attribute__((__used__)) __attribute__((section(".modinfo"), unused, aligned(1))) \ = "parmtype" "=" "name" ":" "byte"; static const char __param_str_name1[] = "MODULE" "." "name1"; static struct kernel_param const __param_name1 \ __attribute__((__used__)) \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ = { __param_str_name1, ((struct module *)0), &param_ops_int, (0x444), -1, 0, { &i } }; static const char __UNIQUE_ID_name1type73[] __attribute__((__used__)) \ __attribute__((section(".modinfo"), unused, aligned(1))) \ = "parmtype" "=" "name1" ":" "int"; 

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

एक ही समय में __used__ और अप्रयुक्त विशेषताओं का संयोजन अंतिम उत्पन्न लाइन में अजीब लगता है, खासकर यदि आप मैक्रो कोड के अगले टुकड़े को देखते हैं

 #if GCC_VERSION < 30300 # define __used __attribute__((__unused__)) #else # define __used __attribute__((__used__)) #endif 

किस तरह की चपलता है कि ए धुएं के डेवलपर्स, उनके विचारों के दर्दनाक तरीके से कोड में सन्निहित है। मुझे पता है कि आप विशेषता लेखन के दोनों रूपों का उपयोग कर सकते हैं, लेकिन एक ही पंक्ति में ऐसा क्यों करते हैं - मुझे समझ में नहीं आता है।

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

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

हम कार्य को समझने में मैक्रोज़ की समझ को आगे नहीं बढ़ाएंगे, इसलिए हम एंड यूटिलिटी के स्रोत कोड की ओर मुड़ते हैं और यह समझने की कोशिश करते हैं कि यह क्या करता है।

सबसे पहले, हम यह देखकर चकित हैं कि कर्नेल स्रोतों में आवश्यक चीज़ों को शामिल नहीं किया गया है। हां, मैं इस बात से सहमत होने के लिए तैयार हूं कि मैं एक उपयोगिता हूं और मॉड्यूल को लोड करने के लिए प्रवेश बिंदु के माध्यम से कर्नेल के साथ बातचीत करता हूं, लेकिन एल ड्राइवरों पर कोई भी पुस्तक हमें इस उपयोगिता के बारे में बताती है, इसलिए कर्नेल के स्रोत के पास कहीं इसके स्रोत के "आधिकारिक" संस्करण की कमी मुझे गलत समझ रहे हैं। ठीक है, ठीक है, Google ने हमें निराश नहीं किया, और हम सभी पनीर पर निकल गए।

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

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

हमें स्रोत पाठ वाले दो पैकेज मिलते हैं। और, हम गितूब पर भी चीज़ों को खोजते हैं, हम देखते हैं कि वे समान हैं और विश्वास पर लेते हैं कि यह उपयोगिता का स्रोत कोड कैसे दिखता है। अगला, हम केवल git पर फ़ाइल का अध्ययन करते हैं, विशेष रूप से यहाँ से इसे सिर्फ insmod.c कहा जाता है, हम पाते हैं कि और एक शुरुआत के लिए, यह मापदंडों की सूची को एक लंबे अशक्त-समाप्त स्ट्रिंग में परिवर्तित करता है, जिसमें अलग-अलग तत्वों को रिक्त स्थान द्वारा अलग किया जाता है। इसके बाद, वह दो कार्यों को कॉल करता है, जिनमें से पहले को grub_file कहा जाता है और स्पष्ट रूप से बाइनरी को खोलता है, जबकि दूसरे में init_module नाम होता है और मॉड्यूल बाइनरी और मापदंडों के एक स्ट्रिंग के साथ खुली फ़ाइल के लिए एक पॉइंटर लेता है और इसे load_module कहा जाता है, जो इस फ़ंक्शन का उद्देश्य लोडिंग के रूप में बताता है मापदंडों में संशोधन के साथ।

हम दूसरे फ़ंक्शन के पाठ की ओर मुड़ते हैं, जो फ़ाइल में निहित है ... और यहां एक बमर है - गीट पर अध्ययन किए गए भंडार की किसी भी फाइल में नहीं है (ठीक है, यह सिर्फ तार्किक है, यह कर्नेल का हिस्सा है और इसका स्थान यहां नहीं है) यह नहीं है। Google फिर से मदद करने की जल्दी में है और हमें Elixir और mod.c.c फ़ाइल के अंतर्गत कर्नेल चीज़ों को लौटाता है। यह ध्यान दिया जाना चाहिए कि, आश्चर्यजनक रूप से, मॉड्यूल के साथ काम करने के लिए फ़ंक्शन युक्त फ़ाइल का नाम तर्कसंगत लगता है, मुझे यह भी समझ में नहीं आता कि यह कैसे समझा जाए, यह संभवतः दुर्घटना से हुआ।

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

नोट - कर्नेल पर नियंत्रण के हस्तांतरण के बारे में, मैं थोड़ा उत्साहित हो गया, अब हम निश्चित रूप से कर्नेल स्रोत में फ़ंक्शन का उपयोग देखते हैं, चाहे बाइनरी हिस्सा मॉड्यूल से जुड़ा होगा, या यदि यह कर्नेल छवि में ही पड़ा है, तो अभी भी अज्ञात है। तथ्य यह है कि इस फ़ंक्शन के प्रसंस्करण के लिए प्रवेश बिंदु SYSCALL_DEFINE3 के माध्यम से एक विशेष तरीके से तैयार किया गया है, अप्रत्यक्ष रूप से दूसरे विकल्प के पक्ष में गवाही देता है, लेकिन मैं लंबे समय से समझता हूं कि तार्किक और अतार्किक, स्वीकार्य और अस्वीकार्य, साथ ही अनुमेय और अस्वीकार्य के बारे में मेरे विचार बहुत महत्वपूर्ण हैं। एल के डेवलपर्स से विचलित

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

उदाहरण के लिए, मुझे समझ में नहीं आ रहा है कि ऑपरेटिंग सिस्टम (agrc, argv) के लिए मानक रूप से पैरामीटर को अनुवाद करने के लिए बाहरी उपयोगिता की आवश्यकता क्यों है, रिक्त स्थान के साथ एक रिक्त-स्ट्रिंग स्ट्रिंग के रूप में विभाजक के रूप में, जिसे सिस्टम मॉड्यूल द्वारा आगे संसाधित किया जाता है - यह दृष्टिकोण कुछ हद तक मेरा है। संज्ञानात्मक क्षमता। विशेष रूप से, इस तथ्य को देखते हुए कि उपयोगकर्ता एक पैरामीटर स्ट्रिंग को शून्य-टर्मिनेटेड स्ट्रिंग के रूप में विभाजक के रूप में रिक्त स्थान में प्रवेश करता है, और कर्नेल में उपयोगिता इसे एक रूप में परिवर्तित करती है (argc, argv)। पुराने मजाक की जोरदार याद दिलाती है "हम केतली को स्टोव से हटाते हैं, उसमें से पानी डालते हैं और एक समस्या प्राप्त करते हैं जिसका समाधान पहले से ही ज्ञात है।" और जब से मैं सिद्धांत का पालन करने की कोशिश करता हूं, "अपने वार्ताकार पर विचार करें जब तक वह खुद को इससे ज्यादा बेवकूफ न समझे, जब तक कि वह इसके विपरीत साबित न हो जाए।" और उसके बाद भी, आपसे गलती हो सकती है, ”और ए के डेवलपर्स के संबंध में, पहला वाक्यांश निश्चित रूप से मान्य है, इसका मतलब है कि मैं कुछ गलत समझता हूं, लेकिन मैं इसके लिए अभ्यस्त नहीं हूं। यदि कोई भी दोहरे रूपांतरण के कथित तथ्य का उचित स्पष्टीकरण दे सकता है, तो मैं टिप्पणी में पूछ सकता हूं। लेकिन हम जांच जारी रखते हैं।

विकल्प 1 और 2 के कार्यान्वयन के लिए संभावनाएं "बहुत कमजोर रूप से दिखाई देती हैं" (घरेलू उच्च गति एडीसी विकसित करने की संभावनाओं पर हाल के एक लेख से एक आकर्षक शब्द), क्योंकि कर्नेल फ़ंक्शन का उपयोग करके मेमोरी में मॉड्यूल लोड करना बहुत अजीब होगा, और फिर कर्नेल को लागू करने के लिए इसे नियंत्रित करें। उनके शरीर में निर्मित कार्य। और निश्चित रूप से, load_module फ़ंक्शन के पाठ में, हम पार्स_रग्स कॉल को बहुत जल्दी ढूंढ लेते हैं - ऐसा लगता है कि हम सही रास्ते पर हैं। अगला, हम जल्दी से कॉल की श्रृंखला के माध्यम से जाते हैं (हमेशा की तरह, हम आवरण कार्यों और आवरण मैक्रोज़ देखेंगे, लेकिन हम पहले से ही डेवलपर्स द्वारा इस तरह के प्यारे मज़ाक के लिए एक अंधे आंख को मोड़ने के लिए उपयोग किए जाते हैं) और हमें पार्स_ऑन फ़ंक्शन मिलता है, जो आवश्यक पैरामीटर को सही जगह पर रखता है।

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

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

सब कुछ स्पष्ट और समझने योग्य लगता है, केवल एक चीज जो चिंता का विषय है, उत्पन्न आंकड़ों पर __indata विशेषता की कमी है, क्या यह वास्तव में आरंभीकरण के बाद स्मृति में रह सकता है, शायद यह विशेषता सामान्य भाग में कहीं वर्णित है, उदाहरण के लिए, लिंकर डेटा में, ईमानदार होना , देखने में आलसी, एपिग्राफ देखें।

सारांश करना - सप्ताहांत उपयोगी था, यह एल के स्रोत कोड को समझने के लिए दिलचस्प था, कुछ को याद किया और कुछ सीखा, लेकिन ज्ञान कभी भी शानदार नहीं होता है।
खैर, मेरी धारणाओं में, मुझे अनुमान नहीं था, एल में एक विकल्प लागू किया गया था जो शेष 7 प्रतिशत में बदल गया, लेकिन यह स्पष्ट रूप से स्पष्ट नहीं था।

खैर, निष्कर्ष में, यारोस्लावना का रोना (इसके बिना कोई भी कैसे कर सकता है) मुझे आवश्यक जानकारी के लिए क्यों देखना है (मैं आंतरिक रसोई का मतलब नहीं हूं, लेकिन बाहरी प्रस्तुति) जिनके पास आधिकारिक स्थिति नहीं है, जहां पुस्तक के समान एक दस्तावेज है।
"एक कंप्यूटर का सॉफ्टवेयर। कार्यात्मक ऑपरेटिंग सिस्टम।
RAFOS। सिस्टम प्रोग्रामर गाइड। ”, या वे अब नहीं हैं?

Source: https://habr.com/ru/post/hi431860/


All Articles