एंबेडेड सिस्टम में वैश्विक चर का उपयोग


17

मैंने अपने उत्पाद के लिए फर्मवेयर लिखना शुरू कर दिया है और मैं यहां एक बदमाश हूं। मैं वैश्विक चर या कार्यों का उपयोग नहीं करने के बारे में कई लेखों के माध्यम से चला गया। क्या 8 बिट सिस्टम में वैश्विक चर का उपयोग करने के लिए कोई सीमा है या यह एक पूर्ण 'नहीं-नहीं' है। मुझे अपने सिस्टम में वैश्विक चर का उपयोग कैसे करना चाहिए या क्या मुझे पूरी तरह से उनसे बचना चाहिए?

मैं अपने फर्मवेयर को अधिक कॉम्पैक्ट बनाने के लिए इस विषय पर आप लोगों से मूल्यवान सलाह लेना चाहूंगा।


यह सवाल एम्बेडेड सिस्टम के लिए अद्वितीय नहीं है। एक डुप्लिकेट यहां पाया जा सकता है
लुंडिन

@Lundin आपके लिंक से: "इन दिनों जो केवल एम्बेडेड वातावरण में मायने रखता है जहाँ मेमोरी काफी सीमित होती है। आपको यह मानने से पहले कुछ पता होना चाहिए कि एम्बेडेड अन्य वातावरणों के समान है और मान लें कि प्रोग्रामिंग नियम पूरे बोर्ड में समान हैं।"
20

@endolith staticfile गुंजाइश "वैश्विक" जैसी नहीं है, नीचे मेरा उत्तर देखें।
लुंडिन

जवाबों:


31

आप वैश्विक चर का सफलतापूर्वक उपयोग कर सकते हैं, जब तक आप @ फिल के दिशानिर्देशों को ध्यान में रखते हैं। हालांकि, संकलित कोड को कम कॉम्पैक्ट किए बिना उनके मुद्दों से बचने के कुछ अच्छे तरीके यहां दिए गए हैं।

  1. लगातार राज्य के लिए स्थानीय स्थिर चर का उपयोग करें जिसे आप केवल एक फ़ंक्शन के अंदर एक्सेस करना चाहते हैं।

    #include <stdint.h>
    void skipper()
    {
        static uint8_t skip_initial_cycles = 5;
        if (skip_initial_cycles > 0) {
            skip_initial_cycles -= 1;
            return;
        }
        /* ... */
    }
  2. संबंधित चर रखने के लिए एक संरचना का उपयोग करें, यह स्पष्ट करने के लिए कि उनका उपयोग कहां किया जाना चाहिए और कहां नहीं।

    struct machine_state {
         uint8_t level;
         uint8_t error_code;
    } machine_state;
    
    struct led_state {
        uint8_t red;
        uint8_t green;
        uint8_t blue;
    } led_state;
    
    void machine_change_state()
    {
        machine_state.level += 1;
        /* ... */
        /* We can easily remember not to use led_state in this function. */
    }
    
    void machine_set_io()
    {
        switch (machine_state.level) {
        case 1:
            PIN_MACHINE_IO_A = 1;
            /* ... */
        }
    }
  3. केवल वर्तमान C फ़ाइल के भीतर दृश्यमान चर बनाने के लिए वैश्विक स्थिर चर का उपयोग करें। यह नामकरण संघर्ष के कारण अन्य फ़ाइलों में कोड द्वारा आकस्मिक पहुंच को रोकता है।

    /* time_machine.c */
    static uint8_t current_time;
    /* ... */
    
    /* delay.c */
    static uint8_t current_time; /* A completely separate variable for this C file only. */
    /* ... */

अंतिम नोट के रूप में, यदि आप किसी रूकावट के भीतर एक वैश्विक चर को संशोधित कर रहे हैं और इसे कहीं और पढ़ रहे हैं:

  • चर को चिह्नित करें volatile
  • सुनिश्चित करें कि यह सीपीयू के लिए परमाणु है (यानी 8-बिट सीपीयू के लिए 8-बिट)।

या

  • चर तक पहुंच की सुरक्षा के लिए एक लॉकिंग तंत्र का उपयोग करें।

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

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

1
@ जॉन आपको दौड़ की परिस्थितियों से बचने के लिए अस्थिरता का उपयोग नहीं करना चाहिए, वास्तव में यह मदद नहीं करेगा। आपको एम्बेडेड सिस्टम कंपाइलर्स में खतरनाक कंपाइलर ऑप्टिमाइज़िंग बग्स को रोकने के लिए अस्थिर का उपयोग करना चाहिए।
लंडिन

2
@ जॉन: volatileचर का सामान्य उपयोग एक निष्पादन संदर्भ में कोड को चलाने की अनुमति देना है ताकि कोड को किसी अन्य निष्पादन संदर्भ में पता चल सके कि कुछ हुआ है। 8-बिट सिस्टम पर, एक बफर जो 128 बाइट्स की पावर-टू-नंबर रखने जा रहा है, उसे एक अस्थिर बाइट के साथ प्रबंधित किया जा सकता है जो बाइट्स में कुल जीवनकाल संख्या को दर्शाता है (बफर 256)। एक और बाइट्स की जीवनकाल संख्या को इंगित करता है, बशर्ते कि केवल एक निष्पादन संदर्भ बफर में डेटा डालता है और केवल एक ही डेटा को बाहर निकालता है।
सुपरकैट

2
@ जॉन: यद्यपि यह संभव है कि बफर के प्रबंधन के लिए लॉकिंग के कुछ रूप का उपयोग किया जाए या अस्थायी रूप से निष्क्रिय किया जाए, यह वास्तव में आवश्यक या सहायक नहीं है। यदि बफर को 128-255 बाइट्स रखना होता है, तो कोडिंग को थोड़ा बदलना होगा, और यदि इसे अधिक से अधिक पकड़ना था, तो इंटरप्ट को अक्षम करना संभवतः आवश्यक होगा, लेकिन 8-बिट सिस्टम पर बफ़र छोटे होने के लिए उपयुक्त नहीं हैं; बड़े बफ़र्स वाले सिस्टम आम तौर पर 8 बिट्स से बड़े परमाणु लिख सकते हैं।
19

24

जिन कारणों से आप 8-बिट सिस्टम में वैश्विक चर का उपयोग नहीं करना चाहेंगे, वही आप किसी अन्य प्रणाली में उनका उपयोग नहीं करना चाहेंगे: वे कार्यक्रम के व्यवहार के बारे में तर्क करना मुश्किल बनाते हैं।

केवल खराब प्रोग्रामर ही "ग्लोबल वैरिएबल का उपयोग नहीं करते" जैसे नियमों से त्रस्त हो जाते हैं। अच्छे प्रोग्रामर नियमों के पीछे के कारण को समझते हैं, फिर नियमों को दिशानिर्देशों की तरह व्यवहार करते हैं।

क्या आपका कार्यक्रम समझना आसान है? क्या इसका व्यवहार पूर्वानुमेय है? क्या अन्य भागों को तोड़े बिना इसके हिस्सों को संशोधित करना आसान है? यदि इनमें से प्रत्येक प्रश्न का उत्तर हां है , तो आप एक अच्छे कार्यक्रम के रास्ते पर हैं।


1
@MichaelKaras ने क्या कहा - यह समझना कि इन चीजों का क्या मतलब है और वे चीजों को कैसे प्रभावित करते हैं (और वे आपको कैसे काट सकते हैं) महत्वपूर्ण बात है।
जॉन यू

5

आपको पूरी तरह से वैश्विक चर ("ग्लोबल्स" का उपयोग करने से बचना चाहिए)। लेकिन, आपको उनका इस्तेमाल विवेकपूर्ण तरीके से करना चाहिए। ग्लोबल्स के अत्यधिक उपयोग के साथ व्यावहारिक समस्याएं:

  • संकलन इकाई भर में ग्लोबल्स दिखाई देते हैं। संकलन इकाई में कोई भी कोड किसी वैश्विक को संशोधित कर सकता है। एक संशोधन के परिणाम कहीं भी इस वैश्विक मूल्यांकन कर सकते हैं।
  • परिणामस्वरूप ग्लोबल्स कोड को पढ़ने और समझने में कठिन बनाते हैं। प्रोग्रामर को हमेशा उन सभी जगहों को ध्यान में रखना पड़ता है जहाँ वैश्विक मूल्यांकन और असाइन किया गया है।
  • ग्लोबल्स का अत्यधिक उपयोग कोड को अधिक दोष-प्रवण बनाता है।

g_वैश्विक चर के नाम के साथ एक उपसर्ग जोड़ना एक अच्छा अभ्यास है । उदाहरण के लिए, g_iFlags। जब आप कोड में उपसर्ग के साथ चर को देखते हैं, तो आप तुरंत पहचान लेते हैं कि यह एक वैश्विक है।


2
ध्वज का वैश्विक होना आवश्यक नहीं है। ISR एक फ़ंक्शन को कह सकता है जिसमें एक स्थिर चर है, उदाहरण के लिए।
फिल फ्रॉस्ट

+1 मैंने पहले ऐसी ट्रिक के बारे में नहीं सुना है। मैंने उस पैराग्राफ को उत्तर से हटा दिया है। staticझंडा कैसे दिखाई देगा main()? क्या आप यह बता रहे हैं कि वही फ़ंक्शन जो बाद में staticइसे वापस कर सकता है main()?
निक एलेक्सीव

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

4
ग्लोबल्स से बचने के कुछ तरीके (जैसे केवल डिवाइस ड्राइवरों के माध्यम से हार्डवेयर तक पहुंचना) गंभीर रूप से संसाधन-युक्त 8-बिट वातावरण के लिए बहुत अक्षम हो सकते हैं।
स्पेरो पेफेनी

1
@ShhroPefhany अच्छे प्रोग्रामर नियमों के पीछे का कारण समझते हैं, फिर नियमों को दिशानिर्देशों की तरह व्यवहार करते हैं। जब दिशानिर्देश संघर्ष में होते हैं, तो अच्छा प्रोग्रामर संतुलन को सावधानी से तौलता है।
फिल फ्रॉस्ट

4

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

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

ध्यान दें कि किसी फ़ंक्शन के अंदर और बाहर स्थैतिक का अलग अर्थ होता है। /programming/5868947/difference-between-static-variable-inside-and-outside-of-a-function

/programming/5033627/static-variable-inside-of-a-function-in-c


1
कई एम्बेडेड-सिस्टम कंपाइलर स्वचालित रूप से स्वचालित चर आवंटित करते हैं, लेकिन कार्यों द्वारा उपयोग किए जाने वाले ओवरले चर जो एक साथ दायरे में नहीं हो सकते हैं; यह आम तौर पर मेमोरी उपयोग का उत्पादन करता है जो स्टैक-आधारित सिस्टम के लिए सबसे खराब स्थिति के उपयोग के बराबर है जिसमें सभी सांख्यिकीय रूप से संभव कॉल अनुक्रम वास्तव में हो सकते हैं।
सुपरकाट

4

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

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

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


तो वैश्विक चर रखने कि एक दूसरे के साथ संघर्ष नहीं होगा एक समस्या सही नहीं होगा। उदाहरण: क्रमशः दिन और सप्ताह गिनने के लिए Day_cntr, week_cntr आदि। और मुझे विश्वास है कि किसी को जानबूझकर वैश्विक चर का अधिक उपयोग नहीं करना चाहिए जो समान उद्देश्य से मिलते हैं और स्पष्ट रूप से उन लोगों को परिभाषित करना चाहिए। भारी प्रतिक्रिया के लिए बहुत धन्यवाद। :)
Rookie91

-1

इस विषय को लेकर बहुत सारे लोग भ्रमित हैं। वैश्विक चर की परिभाषा है:

कुछ जो आपके कार्यक्रम में हर जगह से सुलभ है।

यह फ़ाइल स्कोप वैरिएबल के समान नहीं है , जिसे कीवर्ड द्वारा घोषित किया जाता है static। वे वैश्विक चर नहीं हैं , वे स्थानीय निजी चर हैं।

 int x; // global variable
 static int y; // file scope variable

 void some_func (void) {...} // added to demonstrate that the variables above are at file scope.

क्या आपको वैश्विक चर का उपयोग करना चाहिए? कुछ मामले हैं जहां यह ठीक है:

हर दूसरे मामले में, आप कभी भी वैश्विक चर का उपयोग नहीं करेंगे। ऐसा करने का कभी कोई कारण नहीं है। इसके बजाय फ़ाइल स्कोप वैरिएबल का उपयोग करें , जो पूरी तरह से ठीक है।

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


क्यों होता है पतन?
m.Alin

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

1
@ पॉलए.केल्टन मैंने कभी "ग्लोबल मेमोरी सेगमेंट" नामक एक औपचारिक शब्द नहीं सुना है। आपके चर निम्नलिखित स्थानों में से एक को समाप्त करेंगे: रजिस्टर या स्टैक (रनटाइम आवंटन), ढेर (रनटाइम आवंटन), .डेटा खंड (स्पष्ट रूप से प्रारंभिक स्थिर भंडारण चर), .bs खंड (शून्य स्थिर भंडारण चर), इरोडा (पढ़ें। -सामान्य रूप से स्थिर) या .text (कोड का हिस्सा)। यदि वे कहीं और समाप्त होते हैं, तो यह कुछ परियोजना-विशिष्ट सेटिंग है।
लुंडिन

1
@ पाउला.केल्टन मुझे संदेह है कि आप "वैश्विक खंड" के रूप में जो उल्लेख करते हैं वह .dataखंड है।
लुंडिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.