Önleyici olmayan bir işletim sisteminin faydaları nelerdir? ve bu faydalar için fiyat?


14

Bared metal MCU için, arka plan döngü artı zamanlayıcı kesme mimarisi ile ev yapımı kodla karşılaştırıldığında, önleyici olmayan bir işletim sisteminin faydaları nelerdir? Bu avantajlar arasında, bir projenin arka plan döngü mimarisiyle ev yapımı kod kullanmak yerine önleyici olmayan bir işletim sistemini benimsemesi için yeterince çekici olan nedir?
.

Soruya Açıklama:

Bütün bunların soruma cevap verdiğini gerçekten takdir ediyorum. Cevabın neredeyse orada olduğunu hissediyorum. Bu açıklamayı kendi düşüncemi gösteren ve soruyu daraltmaya veya daha kesin hale getirmeye yardımcı olabilecek soruma buraya ekliyorum.

Yapmaya çalıştığım şey, genel olarak bir proje için en uygun RTOS'un nasıl seçileceğini anlamak.
Bunu başarmak için, temel kavramların daha iyi anlaşılması ve farklı RTOS türlerinden ve ilgili fiyattan en cazip faydalar yardımcı olacaktır, çünkü tüm uygulamalar için en iyi RTOS yoktur.
Birkaç yıl önce işletim sistemi hakkında kitaplar okudum ama artık yanımda değilim. Sorumu buraya göndermeden önce internette arama yaptım ve bu bilginin en yararlı olduğunu gördüm: http://www.ustudy.in/node/5456 .
Farklı RTOS'un web sitesindeki tanıtımlar, önleyici zamanlama ve önleyici olmayan zamanlamayı karşılaştıran makaleler vb.Gibi diğer birçok yararlı bilgi vardır.
Ancak, preemptif olmayan bir RTOS seçildiğinde ve daha iyi olduğunda sadece zamanlayıcı kesme ve arka plan döngüsünü kullanarak kendi kodunuzu yazdığınız herhangi bir konu bulamadım.
Kendi cevaplarımdan eminim ama onlardan yeterince memnun değilim.
Özellikle endüstri pratiğinde olmak üzere, daha deneyimli insanlardan gelen cevabı veya görüşü gerçekten bilmek istiyorum.

Şimdiye kadar anladığım kadarıyla:
bir işletim sistemi kullanın ya da kullanmayın, belirli tür zamanlama kodları her zaman gereklidir, hatta aşağıdaki gibi kod biçimindedir:

    in the timer interrupt which occurs every 10ms  
    if(it's 10ms)  
    {  
      call function A / execute task A;  
    }  
    if(it's 50ms)  
    {  
      call function B / execute task B;  
    }  

Avantaj 1:
Önleyici olmayan bir işletim sistemi, programlama kodu için yolu / programlama stilini belirler, böylece mühendisler daha önce aynı projede olmasalar bile aynı görünümü paylaşabilirler. Daha sonra konsept görevi hakkında aynı görüşle, mühendisler farklı görevler üzerinde çalışabilir ve bunları test edebilir, mümkün olduğunca bağımsız olarak profil oluşturabilir.
Ama bundan gerçekten ne kadar kazanabiliriz? Mühendisler aynı projede çalışıyorsa, önleyici olmayan bir işletim sistemi kullanmadan aynı görüşü paylaşmanın yolunu bulabilirler.
Bir mühendis başka bir projeden veya şirketten geliyorsa, işletim sistemini daha önce biliyorsa fayda elde edecektir. Ama eğer yapmadıysa, o zaman, yeni bir işletim sistemi veya yeni bir kod parçası öğrenmesi için büyük bir fark yaratmıyor gibi görünüyor.

Avantaj 2:
OS kodu iyi test edilmişse, hata ayıklamadan zaman kazandırır. Bu gerçekten iyi bir fayda.
Ancak uygulamanın sadece yaklaşık 5 görevi varsa, zamanlayıcı kesme ve arka plan döngüsünü kullanarak kendi kodunuzu yazmanın gerçekten dağınık olmadığını düşünüyorum.

Burada önleyici olmayan bir işletim sistemi, önleyici olmayan bir zamanlayıcıya sahip ticari / serbest / eski bir işletim sistemine atıfta bulunur.
Bu soruyu gönderdiğimde, esas olarak aşağıdaki gibi bazı işletim sistemlerini düşünüyorum:
(1) KISS Çekirdeği (Küçük Önleyici Olmayan RTOS - web sitesi tarafından talep edildi)
http://www.frontiernet.net/~rhode/kisskern.html
(2) uSmartX (hafif RTOS - web sitesi tarafından talep edildi)
(3) FreeRTOS (Önleyici bir RTOS, ancak anladığım kadarıyla, önleyici olmayan bir RTOS olarak da yapılandırılabilir)
(4) uC / OS (FreeRTOS'a benzer)
(5 ) bazı şirketlerde eski işletim sistemi / zamanlayıcı kodu (genellikle şirket tarafından dahili olarak yapılır ve korunur)
(Yeni StackOverflow hesabındaki sınırlama nedeniyle daha fazla bağlantı eklenemez)

Anladığım kadarıyla, önleyici olmayan bir işletim sistemi bu kodların bir koleksiyonudur:
(1) önleyici olmayan strateji kullanan bir zamanlayıcı.
(2) görevler arası iletişim, muteks, senkronizasyon ve zaman kontrolü için olanaklar.
(3) bellek yönetimi.
(4) diğer yararlı tesisler / Dosya Sistemi, ağ yığını, GUI vb gibi kütüphaneler (freertos ve uC / OS bu sağlar, ama emin zamanlayıcı olmayan önleyici olarak yapılandırılmış hala iş eğer değilim)
Bazı onlar her zaman orada değildir. Ancak zamanlayıcı bir zorunluluktur.


Kısaca bu kadar. Çok iş parçacıklı olması gereken bir iş yükünüz varsa ve ek yükü karşılayabiliyorsanız, iş parçacığı işletim sistemi kullanın. Aksi takdirde çoğu zaman için basit bir zaman veya görev tabanlı "zamanlayıcı" yeterlidir. Önleyici veya işbirlikçi çoklu görevlerin en iyisi olup olmadığını anlamak için ... Yükün aşağıya geldiğini ve çoklu görev üzerinde ne kadar kontrole sahip olmak istediğinizi tahmin ediyorum.
akohlsmith

Yanıtlar:


13

Bu biraz konu dışı kokuyor ama ben tekrar yoluna devam etmeye çalışacağım.

Önleyici çoklu görev, işletim sisteminin veya çekirdeğin o anda çalışan iş parçacığını askıya alabileceği ve mevcut zamanlama sezgisel yöntemine bağlı olarak başka bir iş parçasına geçebileceği anlamına gelir. Çoğu zaman çalışan iş parçacığı, sistemde devam eden başka şeyler olduğu konusunda hiçbir fikre sahip değildir ve bunun kodunuz için anlamı, çekirdeğin bir iş parçacığının ortasında bir iş parçacığını askıya almaya karar vermesi için onu tasarlamak için dikkatli olmanız gerektiğidir. çok adımlı çalışma (bir PWM çıkışını değiştirme, yeni bir ADC kanalı seçme, bir I2C çevre biriminden okuma durumu, vb.) ve bu iki iş parçacığının birbirini etkilememesi için bir süre daha başka bir iş parçacığının çalışmasına izin verin.

Rasgele bir örnek: diyelim ki çok iş parçacıklı gömülü sistemlerde yenisiniz ve I2C ADC, SPI LCD ve I2C EEPROM ile küçük bir sisteminiz var. İki konuya sahip olmanın iyi bir fikir olacağına karar verdiniz: biri ADC'den okuyan ve örnekleri EEPROM'a yazan ve diğeri son 10 örneği okuyan, bunların ortalamasını alır ve SPI LCD'de görüntüler. Deneyimsiz tasarım böyle bir şeye benzeyecektir (son derece basitleştirilmiş):

char i2c_read(int i2c_address, char databyte)
{
    turn_on_i2c_peripheral();
    wait_for_clock_to_stabilize();

    i2c_generate_start();
    i2c_set_data(i2c_address | I2C_READ);
    i2c_go();
    wait_for_ack();
    i2c_set_data(databyte);
    i2c_go();
    wait_for_ack();
    i2c_generate_start();
    i2c_get_byte();
    i2c_generate_nak();
    i2c_stop();
    turn_off_i2c_peripheral();
}

char i2c_write(int i2c_address, char databyte)
{
    turn_on_i2c_peripheral();
    wait_for_clock_to_stabilize();

    i2c_generate_start();
    i2c_set_data(i2c_address | I2C_WRITE);
    i2c_go();
    wait_for_ack();
    i2c_set_data(databyte);
    i2c_go();
    wait_for_ack();
    i2c_generate_start();
    i2c_get_byte();
    i2c_generate_nak();
    i2c_stop();
    turn_off_i2c_peripheral();
}

adc_thread()
{
    int value, sample_number;

    sample_number = 0;

    while (1) {
        value = i2c_read(ADC_ADDR);
        i2c_write(EE_ADDR, EE_ADDR_REG, sample_number);
        i2c_write(EE_ADDR, EE_DATA_REG, value);

        if (sample_number < 10) {
            ++sample_number;
        } else {
            sample_number = 0;
        }
    };
}

lcd_thread()
{
    int i, avg, sample, hundreds, tens, ones;

    while (1) {
        avg = 0;
        for (i=0; i<10; i++) {
            i2c_write(EE_ADDR, EE_ADDR_REG, i);
            sample = i2c_read(EE_ADDR, EE_DATA_REG);
            avg += sample;
        }

        /* calculate average */
        avg /= 10;

        /* convert to numeric digits for display */
        hundreds = avg / 100;
        tens = (avg % 100) / 10;
        ones = (avg % 10);

        spi_write(CS_LCD, LCD_CLEAR);
        spi_write(CS_LCD, '0' + hundreds);
        spi_write(CS_LCD, '0' + tens);
        spi_write(CS_LCD, '0' + ones);
    }
}

Bu çok kaba ve hızlı bir örnek. Böyle kodlama!

Şimdi hatırlayın, önleyici çoklu görev OS koddaki herhangi bir satırda (aslında herhangi bir montaj talimatında) bu iş parçacıklarından birini askıya alabilir ve diğer iş parçacığının çalışmasına zaman verebilir.

Bunu bir düşün. İşletim sistemi adc_thread()gerçek verileri yazmak ve yazmak için EE adresinin ayarı arasında askıya almaya karar verirse ne olacağını hayal edin . lcd_thread()ihtiyaç duyduğu verileri okumak için I2C çevre birimi ile uğraşacaktı ve adc_thread()tekrar çalışma sırası geldiğinde, EEPROM bırakıldığı aynı durumda olmayacaktı. İşler hiç iyi olmazdı. Daha da kötüsü, çoğu zaman bile işe yarayabilir, ama her zaman değil, ve kodunuzun olması gerektiği gibi görünmediğinde neden çalışmadığını anlamaya çalışarak çıldırırsınız!

Bu en iyi örnek; OS oluşmasının önüne geçme kararı verebileceğini i2c_write()gelen adc_thread()bireyin bağlam ve tekrar çalıştırmaya başlamak lcd_thread()'ın bağlamda! İşler gerçekten çok hızlı dağılabilir.

Etkili bir çoklu görev ortamında çalışmak için kod yazarken, kodunuzun uygun olmayan bir zamanda askıya alınması durumunda tüm cehennemin gevşemediğinden emin olmak için kilitleme mekanizmaları kullanmanız gerekir .

Diğer yandan, kooperatif çoklu görev, her bir iş parçacığının, yürütme zamanından vazgeçtiği zamanı kontrol ettiği anlamına gelir. Kodlama daha basittir, ancak kod tüm iş parçacıklarının çalışması için yeterli zaman aldığından emin olmak için dikkatle tasarlanmalıdır. Başka bir örnek:

char getch()
{
    while (! (*uart_status & DATA_AVAILABLE)) {
        /* do nothing */
    }

    return *uart_data_reg;
}

void putch(char data)
{
    while (! (*uart_status & SHIFT_REG_EMPTY)) {
        /* do nothing */
    }

    *uart_data_reg = data;
}

void echo_thread()
{
    char data;

    while (1) {
        data = getch();
        putch(data);
        yield_cpu();
    }
}

void seconds_counter()
{
    int count = 0;

    while (1) {
        ++count;
        sleep_ms(1000);
        yield_cpu();
    }
}

Bu kod düşündüğünüz gibi çalışmaz ya da çalışıyor gibi görünse bile, echo iş parçacığının veri hızı arttıkça çalışmaz. Tekrar bakmak için bir dakikanızı ayırın.

echo_thread()Baytın bir UART'ta görünmesini bekler ve sonra alır ve yazmak için yer olana kadar bekler, sonra yazar. Bu yapıldıktan sonra, diğer iş parçacıklarını çalıştırmak için bir dönüş verir. seconds_counter()bir sayıyı arttırır, 1000 ms bekler ve sonra diğer iş parçacıklarına çalışma şansı verir. Uzun süre UART'a iki bayt girerse sleep(), bunları görmeyi kaçırabilirsiniz, çünkü varsayımsal UART'ımızın CPU başka şeyler yapmakla meşgulken karakterleri depolayacak FIFO'su yoktur.

Bu çok zayıf örneği uygulamanın doğru yolu, yield_cpu()meşgul bir döngünüz olan her yere koymak olacaktır . Bu, işlerin ilerlemesine yardımcı olur, ancak başka sorunlara neden olabilir. Örneğin, zamanlama kritikse ve CPU'yu beklediğinizden daha uzun süren başka bir iş parçacığına iletirseniz, zamanlamanızın atmasını sağlayabilirsiniz. Tüm iş parçacıklarının doğru bir şekilde zamanlandığından emin olmak için iş parçacıklarını zorla askıya aldığından, önleyici çok görevli bir işletim sistemi bu soruna sahip olmaz.

Şimdi bunun bir zamanlayıcı ve arka plan döngüsü ile ne ilgisi var? Zamanlayıcı ve arka plan döngüsü yukarıdaki kooperatif çoklu görev örneğine çok benzer:

void timer_isr(void)
{
    ++ticks;
    if ((ticks % 10)) == 0) {
        ten_ms_flag = TRUE;
    }

    if ((ticks % 100) == 0) {
        onehundred_ms_flag = TRUE;
    }

    if ((ticks % 1000) == 0) {
        one_second_flag = TRUE;
    }
}

void main(void)
{
    /* initialization of timer ISR, etc. */

    while (1) {
        if (ten_ms_flag) {
            if (kbhit()) {
                putch(getch());
            }
            ten_ms_flag = FALSE;
        }

        if (onehundred_ms_flag) {
                    get_adc_data();
            onehundred_ms_flag = FALSE;
        }

        if (one_second_flag) {
            ++count;
                    update_lcd();
            one_second_flag = FALSE;
        }
    };
}

Bu, kooperatif diş açma örneğine oldukça yakın görünüyor; olayları ayarlayan bir zamanlayıcı ve onları arayan ve onlara atomik bir şekilde etki eden bir ana döngü var. ADC ve LCD “dişleri” birbirine karıştığından endişelenmenize gerek yok çünkü biri asla diğerini kesmeyecek. Hala çok uzun süren bir “iş parçacığı” hakkında endişelenmeniz gerekiyor; get_adc_data()30ms alırsa ne olur ? bir karakteri kontrol etmek ve tekrarlamak için üç fırsatı kaçırırsınız.

Döngü + zamanlayıcı uygulaması, kodunuzun eldeki göreve daha spesifik olarak tasarlanabilmesi nedeniyle, genellikle birlikte çalışan çok görevli bir mikro çekirdeğe göre uygulanması çok daha kolaydır. Her bir alt sisteme görevlerini çok özel ve öngörülebilir bir şekilde yapmaları için zaman tanıdığınız sabit bir sistem tasarlamak kadar çok görevli değilsiniz. İşbirlikçi olarak çok görevli bir sistemin bile her iş parçacığı için genel bir görev yapısı olması gerekir ve çalıştırılacak bir sonraki iş parçacığı oldukça karmaşık olabilen bir zamanlama işlevi tarafından belirlenir.

Her üç sistem için kilitleme mekanizmaları aynıdır, ancak her biri için gereken ek yük oldukça farklıdır.

Şahsen, neredeyse her zaman bu son standart olan döngü + zamanlayıcı uygulamasını kodlar. İplik geçirmenin çok az kullanılması gereken bir şey olduğunu düşünüyorum. Sadece yazmak ve hata ayıklamak daha karmaşık olmakla kalmaz, aynı zamanda daha fazla ek yük gerektirir (önleyici bir çoklu görev mikro çekirdeği her zaman aptalca basit bir zamanlayıcı ve ana döngü olay takipçisinden daha büyük olacaktır).

Konu üzerinde çalışan herkesin takdir edeceği bir söz de var:

if you have a problem and use threads to solve it, yoeu ndup man with y pemro.bls

:-)


Ayrıntılı örneklerle cevabınız için çok teşekkür ederim, akohlsmith. Ancak, yanıtınızdan kooperatif çoklu görev yerine neden basit zamanlayıcı ve arka plan döngü mimarisini seçtiğinizi sonuca varamıyorum . Beni yanlış anlamayın. Farklı zamanlama hakkında birçok yararlı bilgi sağlayan cevabınızı gerçekten takdir ediyorum. Sadece anlamýyorum.
hailang

Bu konuda biraz daha çalışabilir misiniz?
hailang

Teşekkürler, akohlsmith. Sonunda verdiğin cümleyi seviyorum. Beni tanımak bir süre aldı :) Cevabınızın noktasına geri döndüğünüzde, neredeyse her zaman döngü + zamanlayıcı uygulamasına kod yazıyorsunuz. Daha sonra, bu uygulamayı bırakıp önleyici olmayan işletim sistemine geçtiğiniz durumlarda, bunu size ne yaptı?
hailang

Başkasının işletim sistemini çalıştırırken hem kooperatif hem de önleyici çoklu görev sistemlerine gittim. Linux, ThreadX, ucOS-ii veya QNX. Bu durumlarda bile, basit ve etkili zamanlayıcı + olay döngüsünü kullandım ( poll()hemen akla geliyor).
akohlsmith

Katıştırılmış bir iş parçacığı veya çoklu görev hayranı değilim, ancak karmaşık sistemler için tek aklı başında seçenek olduğunu biliyorum. Konserve mikro işletim sistemleri, işleri kurmanız için hızlı bir yol sağlar ve çoğu zaman cihaz sürücüleri de sağlar.
akohlsmith

6

Çoklu görev, birçok mikrodenetleyici projesinde yararlı bir soyutlama olabilir, ancak gerçek bir önleyici zamanlayıcı çoğu durumda çok ağır ve gereksiz olacaktır. 100'den fazla mikrodenetleyici projesi yaptım. Kooperatif görevini birkaç kez kullandım, ancak ilişkili bagajı ile önleyici görev değiştirme şu ana kadar uygun değildi.

İşbirlikçi görev için öngörülen önleyici görev ile ilgili sorunlar şunlardır:

  1. Çok daha ağır. Önleyici görev zamanlayıcıları daha karmaşıktır, daha fazla kod alanı ve daha fazla döngü gerektirir. Ayrıca en az bir kesinti gerektirirler. Bu genellikle uygulama üzerinde kabul edilemez bir yüktür.

  2. Eşzamanlı olarak erişilebilen yapılar etrafında muteksler gereklidir. Kooperatif bir sistemde, atomik bir işlem olması gereken şeyin ortasında TASK_YIELD'yi aramazsınız. Bu, kuyrukları, paylaşılan küresel durumu ve birçok yeri yaratır.

Genel olarak, bir görevi belirli bir işe ayırmak, CPU'nun bunu destekleyebileceği zaman anlamlıdır ve iş, birkaç ayrı ayrı olaya bölünmek için yeterli geçmişe bağlı işlemle yeterince karmaşıktır. Bu genellikle bir iletişim giriş akışı işlenirken geçerlidir. Bu tür şeyler, daha önceki girdilere bağlı olarak genellikle büyük ölçüde duruma bağlıdır. Örneğin, opcode baytları ve ardından her opcode için benzersiz veri baytları olabilir. Sonra başka bir şey onları göndermek gibi hissettiğinde bu bayt sorunu size geliyor. Giriş akışını işleyen ayrı bir görevle, bunu dışarı gidiyor ve bir sonraki bayt alıyorsanız görev kodunda görünmesini sağlayabilirsiniz.

Genel olarak, çok fazla devlet bağlamı olduğunda görevler yararlıdır. Görevler temelde PC'nin durum değişkeni olduğu durum makineleridir.

Bir mikroun yapması gereken birçok şey bir dizi olaya yanıt olarak ifade edilebilir. Sonuç olarak, genellikle bir ana olay döngü var. Bu, olası her olayı sırayla kontrol eder, ardından tekrar en üste atlar ve hepsini tekrar yapar. Bir olayı işlemek yalnızca birkaç döngüden fazlasını alırken, olayı işledikten sonra genellikle olay döngüsünün başına geri atlarım. Bu aslında olayların listede nerede kontrol edildiklerine bağlı olarak zımni bir önceliğe sahip oldukları anlamına gelir. Birçok basit sistemde bu yeterince iyidir.

Bazen biraz daha karmaşık görevler alırsınız. Bunlar genellikle yapılacak az sayıda ayrı şeyden oluşan bir diziye ayrılabilir. Bu durumlarda dahili bayrakları etkinlik olarak kullanabilirsiniz. Bu tür şeyleri düşük uç PIC'lerde birçok kez yaptım.

Yukarıdaki gibi temel olay yapısına sahipseniz, ancak UART üzerinden bir komut akışına da yanıt vermek zorundaysanız, alınan UART akışını işlemek için ayrı bir görevin olması yararlı olur. Bazı mikro denetleyiciler, kendi çağrı yığınını okuyamayan veya yazamayan PIC 16 gibi çoklu görevler için sınırlı donanım kaynaklarına sahiptir. Bu gibi durumlarda, UART komut işlemcisi için sözde görev dediğim şeyi kullanıyorum. Ana olay döngüsü hala her şeyi işler, ancak işlenecek olaylarından biri UART tarafından yeni bir bayt alınmasıdır. Bu durumda, bu sahte görevi çalıştıran bir rutine atlar. UART komut modülü görev kodunu içerir ve yürütme adresi ve görevin birkaç kayıt değeri bu modüldeki RAM'e kaydedilir. Olay döngüsü tarafından atlanan kod, geçerli kayıtları kaydeder, kaydedilen görev kayıtlarını yükler, ve görevin yeniden başlama adresine atlar. Görev kodu, tersini yapan bir YIELD makrosu çağırır ve daha sonra ana olay döngüsünün başına geri döner. Bazı durumlarda ana olay döngüsü, düşük öncelikli bir olay olmasını sağlamak için genellikle altta olmak üzere geçiş başına bir kez çalışır.

Bir PIC 18 ve daha üstü sürümlerde, çağrı yığını bellenim tarafından okunabilir ve yazılabilir olduğundan gerçek bir kooperatif görevlendirme sistemi kullanıyorum. Bu sistemlerde, yeniden başlatma adresi, birkaç durum durumu ve veri yığını işaretçisi her görev için bir bellek arabelleğinde tutulur. Diğer tüm görevlerin bir kez çalışmasına izin vermek için bir görev TASK_YIELD öğesini çağırır. Bu, geçerli görev durumunu kaydeder, bir sonraki kullanılabilir görev için listeye bakar, durumunu yükler, sonra çalıştırır.

Bu mimaride, ana olay döngüsü, döngünün üstünde TASK_YIELD çağrısı ile başka bir görevdir.

PIC'ler için tüm çoklu görev kodum ücretsiz olarak kullanılabilir. Görmek için http://www.embedinc.com/pic/dload.htm adresinden PIC Geliştirme Araçları sürümünü yükleyin . 8 bit PIC'ler için SOURCE> PIC dizininde adlarında "görev" olan dosyaları ve 16 bit PIC'ler için SOURCE> DSPIC dizinini arayın.


muteksiler nadir olmasına rağmen, işbirlikçi olarak çok görevli sistemlerde hala gerekli olabilir. Tipik örnek, kritik bir bölüme erişmesi gereken bir ISR'dir. Kritik veriler için daha iyi bir tasarım veya uygun bir veri kabı seçerek bu neredeyse her zaman önlenebilir.
akohlsmith

@akoh: Evet, SPI veriyoluna erişim gibi paylaşılan bir kaynağı işlemek için birkaç kez muteks kullandım. Demek istediğim, muteksilerin önleyici bir sistemde oldukları ölçüde doğal olarak gerekli olmamalarıydı. Hiç bir zaman kooperatif sisteminde ihtiyaç duyulmadıklarını veya asla kullanılmadıklarını söylemek istemedim. Ayrıca, bir kooperatif sistemindeki bir muteks, tek bir biti kontrol eden bir TASK_YIELD döngüsünde eğirme kadar basit olabilir. Önleyici bir sistemde, genellikle çekirdeğin içine yerleştirilmeleri gerekir.
Olin Lathrop

@OlinLathrop: Bence önleyici olmayan sistemlerin muteksler söz konusu olduğunda en önemli avantajı, ya sadece doğrudan kesmelerle (doğası gereği önleyici olan) etkileşime girerken ya da korunan bir kaynağı tutması gerektiğinde gerekli olmasıdır. "verim" çağrıları arasında harcamak istediği süreyi aşar veya "" "bir dosyaya veri yazabilir" gibi bir çağrının etrafında korunan bir kaynak tutmak ister. Bazı durumlarda "veri yazma" çağrısı içinde bir verim elde etmek bir sorun olurdu, ben dahil ettik ...
supercat

... hemen ne kadar verinin yazılabileceğini kontrol etmek için bir yöntem ve bir miktarın kullanılabilir olmasını sağlamak için bir yöntem (muhtemelen verim olabilir) (kirli flaş bloklarının geri kazanılmasını hızlandırmak ve uygun bir sayı geri kazanılana kadar beklemek) .
supercat

Merhaba Olin, Cevabınızı çok beğendim. Bilgileri sorularımın çok ötesinde. Birçok pratik deneyim içerir.
hailang

1

Düzenleme: (Daha önceki yazımı aşağıda bırakacağım; belki bir gün birine yardımcı olacaktır.)

Her türlü Çoklu Görev İşletim Sistemi ve Kesme Servis Rutinler, rakip sistem mimarileri değildir veya olmamalıdır. Bunlar sistemin farklı seviyelerindeki farklı işler içindir. Kesmeler, bir cihazın yeniden başlatılması, muhtemelen kesintisiz aygıtların yoklanması, yazılımda zaman işleyişi vb. Gibi acil işleri işlemek için kısa kod dizileri için tasarlanmıştır. acil ihtiyaçlar karşılanmıştır. Tek yapmanız gereken bir zamanlayıcıyı yeniden başlatmak ve bir LED'i değiştirmek veya başka bir cihazı atmaksa, ISR genellikle hepsini ön planda güvenli bir şekilde yapabilir. Aksi takdirde, arka planın (bir bayrak ayarlayarak veya bir mesajı sıraya koyarak) bir şeyin yapılması gerektiğini bildirmesi ve işlemciyi serbest bırakması gerekir.

Kimin arka plan döngü sadece boş döngü çok basit bir program yapılarını gördük: for(;;){ ; }. Tüm çalışmalar zamanlayıcı ISR'de yapıldı. Bu, programın bir zamanlayıcı süresinden daha kısa sürede tamamlanması garanti edilen bazı sabit işlemleri tekrarlaması gerektiğinde işe yarayabilir; bazı sınırlı sinyal işleme türleri akla gelmektedir.

Şahsen, bir çıkışı temizleyen ISR'ler yazıyorum ve arka planın, bir çarpma kadar basit olsa ve bir zamanlayıcı süresinin bir bölümünde yapılabilecek eklenti olsa bile, yapılması gereken her şeyi devralmasına izin veriyorum. Neden? Bir gün, programıma başka bir "basit" işlev eklemek için parlak bir fikir edinebilirim ve "heck, bunu yapmak için sadece kısa bir ISR alır" ve aniden daha önce basit mimarim planladığım bazı etkileşimleri büyütür ve tutarsız bir şekilde gerçekleşir. Bunlar hata ayıklamak için çok eğlenceli değil.


(Daha önce iki çeşit çoklu görev karşılaştırması yayınlanmıştır)

Görev geçişi: Önleyici MT, hiçbir iş parçacığının CPU tarafından aç kalmamasını ve yüksek öncelikli iş parçacıklarının hazır olur olmaz çalışmasını sağlamak da dahil olmak üzere sizin için görev geçişi ile ilgilenir. Kooperatif MT, programlayıcının hiçbir işlemciyi bir seferde çok uzun süre tutmadığından emin olmasını gerektirir. Ayrıca ne kadar uzun olacağına da karar vermeniz gerekecek. Bu aynı zamanda, kodu her değiştirdiğinizde, herhangi bir kod bölümünün bu zaman kuantumunu aşıp aşmadığının farkında olmanız gerektiği anlamına gelir.

Atom dışı işlemleri koruma: Bir PMT ile, bölünmemesi gereken işlemlerin ortasında iş parçacığı takaslarının oluşmadığından emin olmanız gerekir. Örneğin, belirli bir sırayla veya maksimum süre içinde ele alınması gereken belirli cihaz kayıt çiftlerini okuma / yazma. CMT ile oldukça kolaydır - işlemciyi böyle bir işlemin ortasında vermeyin.

Hata ayıklama: İş parçacığı anahtarlarının ne zaman / nerede olacağını planladığınızdan CMT ile genellikle daha kolaydır. Bir PMT ile diş güvenli olmayan işlemlerle ilgili dişler ve böcekler arasındaki yarış koşullarının hatalarının giderilmesi özellikle zordur çünkü iplik değişiklikleri olasılıklıdır, bu nedenle tekrarlanamaz.

Kodu anlama: Bir PMT için yazılan konular hemen hemen tek başına durabilecek gibi yazılır. CMT için yazılan konular parça olarak yazılır ve seçtiğiniz program yapısına bağlı olarak bir okuyucunun izlemesi daha zor olabilir.

İş parçacığı için güvenli olmayan kitaplık kodunu kullanma: PMT iş parçacığı için güvenli bir durumda çağırdığınız her kitaplık işlevinin doğrulandığını doğrulamanız gerekir. printf () ve scanf () ve bunların çeşitleri neredeyse her zaman iş parçacığı için güvenli değildir. Bir CMT ile, özellikle işlemciyi vermeniz dışında hiçbir iplik değişikliğinin olmayacağını bileceksiniz.

Mekanik bir cihazı kontrol etmek ve / veya harici olayları izlemek için sonlu durum makine ile çalışan bir sistem genellikle CMT için iyi adaylardır, çünkü her olayda yapacak çok şey yoktur - bir motoru çalıştırmak veya durdurmak, bir bayrak ayarlamak, bir sonraki durumu seçmek Böylece, durum değiştirme işlevleri doğal olarak kısadır.

Hibrit bir yaklaşım bu tür sistemlerde gerçekten işe yarayabilir: bir makine olarak çalışan durum makinesini (ve dolayısıyla donanımın çoğunu) yönetmek için CMT ve bir durum tarafından başlatılan artık çalışan hesaplamaları yapmak için bir veya iki iplik daha değişiklik.


Cevabınız için teşekkürler, JRobert. Ama benim soruma uygun değil. Önleyici işletim sistemi ile önleyici olmayan işletim sistemi karşılaştırılır, ancak önleyici olmayan işletim sistemi ile işletim sistemi dışı işletim sistemi karşılaştırılmaz.
hailang

Doğru - özür dilerim. Düzenlemem sorunuzu daha iyi ele almalıdır.
JRobert
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.