Gömülü Sistemlerde global değişkenlerin kullanımı


17

Ürünüm için ürün yazılımı yazmaya başladım ve burada bir çaylakım. Global değişkenler veya fonksiyonlar kullanmama konusunda birçok makaleden geçtim. 8 bitlik bir sistemde global değişkenlerin kullanımı için herhangi bir sınırlama var mıdır yoksa tam bir 'Hayır-Hayır' mıdır? Global değişkenleri sistemimde nasıl kullanmalıyım veya tamamen bunlardan kaçınmalıyım?

Ürün yazılımımı daha kompakt hale getirmek için sizden bu konuda değerli tavsiyeler almak istiyorum.


Bu soru gömülü sistemlere özgü değildir. Burada bir kopya bulabilirsiniz .
Lundin

@Lundin Bağlantınızdan: "Günümüzde yalnızca belleğin oldukça sınırlı olduğu gömülü ortamlarda önemlidir. Gömülü ortamın diğer ortamlarla aynı olduğunu ve programlama kurallarının tümüyle aynı olduğunu varsaymadan önce bilmeniz gereken bir şey."
endolith

@ endolith staticdosya kapsamı "global" ile aynı şey değildir, aşağıdaki cevabımı görün.
Lundin

Yanıtlar:


31

@ Phil kurallarını göz önünde bulundurduğunuz sürece global değişkenleri başarılı bir şekilde kullanabilirsiniz. Ancak, derlenmiş kodu daha az kompakt hale getirmeden sorunlarını önlemek için bazı güzel yollar.

  1. Yalnızca bir işlev içinde erişmek istediğiniz kalıcı durum için yerel statik değişkenleri kullanın.

    #include <stdint.h>
    void skipper()
    {
        static uint8_t skip_initial_cycles = 5;
        if (skip_initial_cycles > 0) {
            skip_initial_cycles -= 1;
            return;
        }
        /* ... */
    }
  2. İlgili değişkenleri bir arada tutmak, nerede kullanılmaları ve nerede kullanılmamaları gerektiğinin daha net olması için bir yapı kullanın.

    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. Değişkenleri yalnızca geçerli C dosyasında görünür hale getirmek için genel statik değişkenleri kullanın. Bu, ad çakışmaları nedeniyle diğer dosyalardaki kodlara yanlışlıkla erişilmesini önler.

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

Son bir not olarak, bir kesme rutini içinde bir global değişkeni değiştiriyorsanız ve başka bir yerde okuyorsanız:

  • Değişkeni işaretleyin volatile.
  • CPU için atomik olduğundan emin olun (yani 8 bit CPU için 8 bit).

VEYA

  • Değişkene erişimi korumak için bir kilitleme mekanizması kullanın.

uçucu ve / veya atomik değişkenler hatalardan kaçınmanıza yardımcı olmayacak, bir çeşit kilit / semafora ihtiyacınız olacak veya değişkene yazarken kesintileri kısaca maskeleyeceksiniz.
John U

3
Bu "iyi çalışıyor" un oldukça dar bir tanımı. Demek istediğim, geçici bir şey ilan etmenin çatışmaları engellememesiydi. Ayrıca, 3. örneğiniz harika bir fikir değildir - aynı ada sahip iki ayrı globalin olması en azından kodun anlaşılmasını / bakımını zorlaştırır.
John U

1
@JohnU Yarış koşullarından kaçınmak için uçucu kullanmamalısınız, bu gerçekten yardımcı olmaz. Gömülü sistem derleyicilerinde yaygın olan tehlikeli derleyici optimizasyon hatalarını önlemek için uçucu kullanmalısınız.
Lundin

2
@JohnU: volatileDeğişkenlerin normal kullanımı, bir yürütme bağlamında çalışan kodun başka bir yürütme bağlamındaki kodun bir şey olduğunu bilmesini sağlamaktır. 8 bitlik bir sistemde, 128'den büyük olmayan iki baytlık bir gücü bayt tutacak bir tampon, tampona konulan toplam ömür boyu bayt sayısını (mod 256) gösteren bir uçucu bayt ile yönetilebilir ve diğeri, yalnızca bir yürütme bağlamının arabelleğe veri koyması ve bunlardan yalnızca birinin veriyi çıkarması koşuluyla, çıkarılan ömür boyu bayt sayısını gösterir.
supercat

2
@JohnU: Arabelleği yönetmek için bir tür kilitleme kullanmak veya kesintileri geçici olarak devre dışı bırakmak mümkün olsa da, gerçekten gerekli veya yararlı değildir. Tamponun 128-255 bayt tutması gerekiyorsa, kodlamanın biraz değişmesi gerekir ve bundan daha fazlasını tutmak zorunda kalırsa, kesintileri devre dışı bırakmak gerekebilir, ancak 8 bitlik bir sistemde tamponlar küçük olmaya eğilimlidir; daha büyük tamponlu sistemler genellikle 8 bit'ten büyük atomik yazma işlemleri yapabilir.
supercat

24

8 bitlik bir sistemde global değişkenleri kullanmak istememenizin nedenleri, bunları başka bir sistemde kullanmak istemediğiniz ile aynıdır: programın davranışı hakkında akıl yürütmeyi zorlaştırır.

"Kötü değişkenler kullanma" gibi kurallara yalnızca kötü programcılar takılır. İyi programcılar kuralların arkasındaki nedeni anlar, daha sonra kurallara daha çok yönergeler gibi davranırlar.

Programınızı anlamak kolay mı? Davranışı öngörülebilir mi? Diğer parçaları kırmadan parçalarını değiştirmek kolay mı? Bu soruların her birinin cevabı evet ise , iyi bir programa geçiyorsunuz demektir.


1
@MichaelKaras ne dedi - bu şeylerin ne anlama geldiğini ve bir şeyleri nasıl etkilediğini (ve sizi nasıl ısırdıklarını) anlamak önemlidir.
John U

5

Global değişkenleri (kısaca "globaller") kullanmaktan tamamen kaçınmamalısınız. Ama onları akıllıca kullanmalısın. Aşırı küresel kullanımı ile ilgili pratik sorunlar:

  • Globaller derleme birimi boyunca görülebilir. Derleme birimindeki herhangi bir kod bir genel değiştirebilir. Bir modifikasyonun sonuçları, bu globalin değerlendirildiği her yerde ortaya çıkabilir.
  • Sonuç olarak, globaller kodu okumayı ve anlamayı zorlaştırır. Programcı her zaman küresel olanın değerlendirildiği ve atandığı tüm yerleri akılda tutmak zorundadır.
  • Globallerin aşırı kullanımı kodu daha kusurlu hale getirir.

g_Global değişkenlerin ismine önek eklemek iyi bir uygulamadır . Örneğin g_iFlags,. Kodda öneki olan değişkeni gördüğünüzde, bunun genel olduğunu hemen fark edersiniz.


2
Bayrak küresel olmak zorunda değil . ISR, örneğin statik değişkeni olan bir işlevi çağırabilir.
Phil Frost

+1 Daha önce böyle bir hile duymadım. Bu paragrafı yanıttan kaldırdım. staticBayrak nasıl görünür olur main()? Aynı işlevin daha sonra staticgeri dönebileceğini ima ediyor musunuz main()?
Nick Alexeev

Bunu yapmanın bir yolu bu. Belki de işlev ayarlanacak yeni durumu alır ve eski durumu döndürür. Başka birçok yol var. Bayrağı ayarlama işlevine sahip bir kaynak dosyanız ve onu almak için başka bir kaynak dosyanız var. Teknik olarak bu, C terminolojisi tarafından "global" olmasına rağmen, kapsamı yalnızca bu dosya ile sınırlıdır ve yalnızca bilmeniz gereken işlevleri içerir. Yani, kapsamı "küresel" değil, ki bu gerçekten sorun. C ++ ek kapsülleme mekanizmaları sağlar.
Phil Frost

4
Globallerden kaçınmanın bazı yöntemleri (donanıma yalnızca aygıt sürücüleri aracılığıyla erişmek gibi) ciddi bir kaynak açlığına sahip 8 bit ortam için çok verimsiz olabilir.
Spehro Pefhany

1
@SpehroPefhany İyi programcılar kuralların arkasındaki nedeni anlar, daha sonra kurallara daha çok yönergeler gibi davranırlar. Kurallar çeliştiğinde, iyi programcı dengeyi dikkatlice tartar.
Phil Frost

4

Gömülü çalışmada küresel veri yapılarının avantajı statik olmalarıdır. İhtiyacınız olan her değişken globalse, işlevler girildiğinde ve yığın üzerinde bunlar için yer açıldığında hiçbir zaman yanlışlıkla bellek tükenmez. Ama sonra, bu noktada neden fonksiyonlar var? Neden tüm mantığı ve süreçleri işleyen büyük bir işlev olmasın - GOSUB'a izin verilmeyen bir BASIC programı gibi. Bu fikri yeterince ele alırsanız, 1970'lerden itibaren tipik bir montaj dili programına sahip olacaksınız. Verimli ve bakımı imkansız ve ateş etmekte sorun.

Durum değişkenleri (örneğin, her fonksiyonun sistemin yorumlamada mı yoksa çalıştırma durumunda mı olduğunu bilmesi gerekiyorsa) ve birçok fonksiyon tarafından görülmesi gereken ve @PhilFrost'un dediği gibi diğer veri yapıları gibi globalleri akıllıca kullanın. fonksiyonlarınız tahmin edilebilir mi? Yığını hiç bitmeyen bir giriş dizesiyle doldurma olasılığı var mı? Bunlar algoritma tasarımı için önemlidir.

Statik fonksiyonun içinde ve dışında farklı anlamlara sahiptir. /programming/5868947/difference-between-static-variable-inside-and-outside-of-a-function

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


1
Birçok gömülü sistem derleyicisi otomatik değişkenleri statik olarak ayırır, ancak eşzamanlı olarak kapsamı içinde olmayan işlevler tarafından kullanılan bindirme değişkenleri; bu genellikle, statik olarak mümkün olan tüm çağrı dizilerinin gerçekte meydana gelebileceği, yığın tabanlı bir sistem için mümkün olan en kötü duruma eşit bellek kullanımı sağlar.
SuperCat

4

Global değişkenler sadece gerçek küresel durum için kullanılmalıdır. Örneğin, haritanın kuzey sınırının enlemi gibi bir şeyi temsil etmek için küresel bir değişken kullanılması, yalnızca haritanın yalnızca bir "kuzey sınırı" olabiliyorsa işe yarayacaktır. Gelecekte, kodun farklı kuzey sınırlarına sahip birden fazla harita ile çalışması gerekebiliyorsa, kuzey sınırı için genel bir değişken kullanan kodun yeniden işlenmesi gerekecektir.

Tipik bilgisayar uygulamalarında, hiçbir zaman bir şeyden daha fazla olmayacağını varsaymak için genellikle belirli bir neden yoktur. Bununla birlikte, gömülü sistemlerde, bu tür varsayımlar genellikle çok daha makuldür. Birden fazla eşzamanlı kullanıcıyı desteklemek için tipik bir bilgisayar programının çağrılması mümkün olsa da, tipik bir gömülü sistemin kullanıcı arabirimi, düğmeleri ve ekranı ile etkileşen tek bir kullanıcının çalışması için tasarlanacaktır. Bu nedenle, herhangi bir zamanda tek bir kullanıcı arabirimi durumuna sahip olacaktır. Sistemi, birden çok kullanıcının birden fazla klavye ve ekranla etkileşime girebileceği şekilde tasarlamak, tek bir kullanıcı için tasarlamaktan çok daha fazla karmaşıklık gerektirir ve uygulanması çok daha uzun sürer. Sistem asla birden fazla kullanıcıyı desteklemesi için çağrılmazsa, bu tür bir kullanımı kolaylaştırmak için harcanan her türlü ekstra çaba boşa gidecektir. Çok kullanıcılı desteğe ihtiyaç duyulmadığı sürece, çok kullanıcılı destek gerektiğinde tek kullanıcılı bir arayüz için kullanılan kodu atmak zorunda kalmak, büyük olasılıkla hiç gerekmeyecek kullanıcı desteği.

Gömülü sistemlerle ilgili bir faktör, birçok durumda (özellikle kullanıcı arayüzlerini içeren), bir şeyden birden fazlasına sahip olmayı desteklemenin tek pratik yolunun birden çok iş parçacığı kullanmak olmasıdır. Çoklu iş parçacığı için başka bir gereksinimin yokluğunda, basit bir tek iş parçacıklı tasarım kullanmak, muhtemelen gerçekten gerekli olmayacak çok iş parçacıklı sistem karmaşıklığını artırmaktan daha iyidir. Eğer bir şeyden daha fazlasını eklemek zaten büyük bir sistemin yeniden tasarlanmasını gerektiriyorsa, bazı global değişkenlerin kullanımının yeniden işlenmesini gerektirip gerektirmediği önemli olmayacaktır.


Dolayısıyla, birbiriyle çatışmayacak küresel değişkenleri korumak sorun olmayacaktır. . örn: Day_cntr, week_cntr vb sayma günleri ve hafta boyunca ben tek kasten hemen aynı amaca benzeyen ve açıkça ezici tepki için those.Thanks çok tanımlamalıdır küresel değişkenler kullanmamalısınız güven :) respectively.And
Rookie91

-1

Birçok insan bu konuda karışık. Global değişkenin tanımı:

Programınızın her yerinden erişilebilir bir şey.

Bu, anahtar kelime tarafından bildirilen dosya kapsamı değişkenleriyle aynı şey değildir static. Bunlar küresel değişkenler değil , yerel özel değişkenlerdir.

 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.

Global değişkenler mi kullanmalısınız? İyi olduğu birkaç durum vardır:

Diğer her durumda, global değişkenleri asla kullanmayacaksınız. Bunu yapmak için asla bir neden yoktur. Bunun yerine mükemmel şekilde dosya kapsamı değişkenlerini kullanın .

Belirli bir görevi yerine getirmek için tasarlanmış bağımsız, otonom kod modülleri yazmaya çalışmalısınız. Bu modüllerin içinde, dahili dosya kapsamı değişkenleri özel veri üyeleri olarak bulunmalıdır. Bu tasarım yöntemi, nesne yönelimi olarak bilinir ve yaygın olarak iyi tasarım olarak kabul edilir.


Neden inişli çıkışlı?
m.Alin

2
Ben düşünüyorum "küresel değişkeni" aynı zamanda küresel segmentin (değil metin, yığın veya yığın) için tahsisleri tanımlamak için kullanılabilir. Bu anlamda dosya statik ve işlev statik değişkenleri "global" olabilir / olabilir. Bu soru bağlamında, küreselin tahsis segmentine değil kapsama atıfta bulunduğu açıktır ( OP'nin bunu bilmemesi mümkündür ).
Paul A. Clayton

1
@ PaulA.Clayton Hiç "küresel bellek segmenti" olarak adlandırılan resmi bir terim duymadım. Değişkenleriniz şu yerlerden biriyle sonuçlanır: kayıtlar veya yığın (çalışma zamanı ayırma), yığın (çalışma zamanı ayırma), .data segmenti (açıkça başlatılmış statik depolama değişkenleri), .bss segmenti (sıfırlanmış statik depolama değişkenleri), .rodata (okuma -sadece sabitler) veya .text (kodun bir parçası). Başka bir yere giderse, bu projeye özgü bir ayardır.
Lundin

1
@ PaulA.Clayton "Global segment" olarak adlandırdığınız şeyin segment olduğundan şüpheleniyorum .data.
Lundin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.