Які переваги непередбаченої ОС? і ціна на ці пільги?


14

Для оголеного металевого MCU, в порівнянні з домашнім кодом з фоновим циклом та архітектурою переривання таймера, які переваги неперешкодженої ОС? Що з цих переваг є достатньо привабливим для проекту, який повинен прийняти ОС, що не передбачає перевагу, а не використовувати домашній код з фоновою архітектурою циклу?
.

Пояснення до питання:

Я дуже ціную всіх, хто відповів на моє запитання. Я відчуваю, що відповідь майже там. Я додаю це пояснення до свого запитання тут, яке показує мій власний розгляд і може допомогти звузити питання або зробити його більш точним.

Що я намагаюся зробити, це зрозуміти, як вибрати найбільш відповідний RTOS для проекту в цілому.
Щоб досягти цього, допоможе краще розуміння основних понять та найбільш привабливих переваг від різних видів RTOS та відповідної ціни, оскільки немає кращого RTOS для всіх застосувань.
Я читав книги про ОС кілька років тому, але більше не маю їх при собі. Я шукав в Інтернеті, перш ніж розмістив тут своє запитання, і виявив, що ця інформація є найбільш корисною: http://www.ustudy.in/node/5456 .
Є багато іншої корисної інформації, як, наприклад, вступ на веб-сайті різних RTOS, статті, що порівнюють попереднє планування та непередбачене планування тощо.
Але я не знайшов жодної теми, коли слід вибрати непідтримувану RTOS, а коли краще просто написати власний код, використовуючи переривання таймера та фоновий цикл.
У мене є певні власні відповіді, але я ними недостатньо задоволений.
Мені дуже хотілося б дізнатися відповідь чи погляд від більш досвідчених людей, особливо в галузі.

Моє розуміння поки що:
незалежно від того, використовувати або не використовувати ОС, завжди потрібні певні види планування, навіть у вигляді коду типу:

    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;  
    }  

Перевага 1:
Непереважна ОС позначає спосіб / стиль програмування для коду планування, щоб інженери могли поділяти один і той же погляд, навіть якщо раніше вони не були в одному проекті. Тоді, з однаковим поглядом на поняття завдання, інженери можуть працювати над різними завданнями та перевіряти їх, максимально профілювати їх самостійно.
Але скільки ми насправді можемо отримати від цього? Якщо інженери працюють над одним і тим же проектом, вони можуть знайти спосіб спільного використання того ж виду, не використовуючи ОС, що не виключає.
Якщо один інженер є з іншого проекту чи компанії, він отримає вигоду, якщо раніше знав ОС. Але якщо він цього не зробив, то, схоже, це не має великого значення для його вивчення нової ОС або нового фрагмента коду.

Перевага 2:
Якщо код ОС добре перевірений, це економить час від налагодження. Це справді добра користь.
Але якщо у додатку є лише близько 5 завдань, я думаю, що насправді безладно писати власний код, використовуючи переривання таймера та фоновий цикл.

Тут не випереджаюча ОС називається комерційною / вільною / застарілою ОС з непередбачуваним планувальником.
Коли я розміщував це запитання, я в основному думаю про деякі ОС, як-от:
(1) Ядро KISS (Невеликий непідкупний RTOS - заявлений на його веб-сайті)
http://www.frontiernet.net/~rhode/kisskern.html
(2) uSmartX (легкий RTOS - стверджується на його веб-сайті)
(3) FreeRTOS (Це превентивний RTOS, але, як я розумію, він може бути налаштований і як неперешкоджаючий RTOS)
(4) uC / OS (подібно до FreeRTOS)
(5 ) застарілий код ОС / планувальника в деяких компаніях (зазвичай їх виробляє та підтримує внутрішня компанія)
(Неможливо додати більше посилань, оскільки обмеження з нового облікового запису StackOverflow)

Як я розумію, неперспективна ОС - це сукупність таких кодів:
(1) планувальник, що використовує непередбачувану стратегію.
(2) засоби для взаємодії між завданнями, мьютекс, синхронізацію та контроль часу.
(3) управління пам'яттю.
(4) інші корисні засоби / бібліотеки, такі як файлова система, мережевий стек, графічний інтерфейс і т. Д. (FreeRTOS та uC / OS надають це, але я не впевнений, чи все ще вони працюють, коли планувальник налаштований як непередбачений)
Деякі з їх не завжди є. Але планувальник обов'язковий.


Це майже в двох словах. Якщо у вас є робоче навантаження, яке повинно бути багатопоточним, і ви можете дозволити собі накладні витрати, використовуйте нитку ОС. Інакше у більшості випадків вистачає простого "планувальника" простого часу або на основі завдань. І щоб розібратися, чи найкраще багатозадачність чи кооперативне багатозадачність ... Я думаю, що це зводиться до накладних витрат і скільки контролю над багатозадачністю потрібно зробити.
akohlsmith

Відповіді:


13

Це пахне якось поза темою, але я спробую повернути його назад на трасу.

Попередня багатозадачність означає, що операційна система або ядро ​​може призупинити поточний поточний потік і перейти на інший, грунтуючись на будь-якій евристиці планування, яка в ньому є. У більшості випадків запущені потоки не мають поняття, що в системі відбуваються інші речі, і що це означає для вашого коду, це те, що ви повинні бути обережними, щоб спроектувати його так, що якщо ядро ​​вирішить призупинити нитку в середині багатоступінчаста операція (скажімо, зміна виходу ШІМ, вибір нового каналу АЦП, стан зчитування з периферійного пристрою I2C тощо) і нехай деякий час працює нитка, щоб ці два потоки не заважали один одному.

Довільний приклад: скажімо, ви новачок у вбудованих багатопотокових системах, і у вас є маленька система з I2C ADC, SPI LCD та I2C EEPROM. Ви вирішили, що було б хорошою ідеєю мати дві нитки: одну, яка читає з АЦП і записує зразки в EEPROM, та ту, яка зчитує останні 10 зразків, усереднює їх і відображає їх на РК-дисплеї SPI. Недосвідчений дизайн виглядатиме приблизно так (грубо спрощено):

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);
    }
}

Це дуже сирий і швидкий приклад. Не кодуйте так!

Тепер пам’ятайте, що переважна багатозадачна ОС може призупинити будь-який з цих потоків у будь-якому рядку коду (фактично в будь-якій інструкції зі зборки) і надати іншому потоку час для запуску.

Подумайте над цим. Уявіть, що буде, якби ОС вирішила призупинити adc_thread()між встановленням адреси EE для запису та запису фактичних даних. lcd_thread()біг би, гуртуючись навколо периферійного пристрою I2C, щоб прочитати потрібні йому дані, а коли adc_thread()отримав свою чергу знову запуститись, EEPROM не опинився б у такому ж стані, як він залишився. Речі взагалі не будуть працювати дуже добре. Гірше, що це може працювати навіть більшу частину часу, але не весь час, і ви з’їдете з розуму, намагаючись зрозуміти, чому ваш код не працює, коли він ЛЮБИТЬ так, як слід!

Це найкращий приклад; ОС може вирішити вийти i2c_write()з adc_thread()контексту 's і почати його заново з lcd_thread()контексту! Речі можуть дуже швидко заплутатися.

Коли ви пишете код для роботи в переважній багатозадачній обстановці, ви повинні використовувати механізми блокування, щоб переконатися, що якщо ваш код буде призупинено в невідповідний час, що все пекло не вийде з ладу.

Спільна багатозадачність, з іншого боку, означає, що кожен потік контролює, коли він відмовляється від часу його виконання. Кодування простіше, але код повинен бути розроблений ретельно, щоб переконатися, що всі потоки отримують достатньо часу для запуску. Ще один надуманий приклад:

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();
    }
}

Цей код не працюватиме, як ви думаєте, або навіть якщо він, здається, працює, він не працюватиме, оскільки швидкість передачі даних ехопотоку зростає. Знову давайте хвилину, щоб розглянути його.

echo_thread()чекає, коли байт з'явиться в UART, а потім отримує його і чекає, поки є місце, щоб його написати, а потім записує його. Після цього це дає іншим потокам поворот для запуску. seconds_counter()збільшується кількість, чекайте 1000 мс, а потім дайте шанс іншим потокам запуститись. Якщо два байти входять до UART, поки це триває sleep(), ви можете пропустити їх, оскільки наш гіпотетичний UART не має FIFO для зберігання символів, поки процесор зайнятий іншими справами.

Правильним способом реалізації цього дуже бідного прикладу було б поставити yield_cpu()там, де коли-небудь у вас є зайнятий цикл. Це допоможе речей рухатися, але може спричинити й інші проблеми. Наприклад, якщо час є критичним, і ви віддаєте процесор на інший потік, який займає більше часу, ніж ви очікували, ви можете скинути час. Переважна багатозадачна ОС не мала б цієї проблеми, оскільки вона примусово призупиняє потоки, щоб переконатися, що всі потоки заплановані правильно.

Тепер що це стосується циклу таймера та фону? Цикл таймеру та фону дуже схожий на приклад спільного багатозадачності, наведений вище:

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;
        }
    };
}

Це виглядає досить близько до прикладу кооперативної нитки; у вас є таймер, який встановлює події та основний цикл, який шукає їх та діє на них атомним способом. Вам не потрібно турбуватися про те, що «нитки» АЦП та РК заважають один одному, тому що один ніколи не перебиватиме іншого. Ви все ще повинні турбуватися про те, що "нитка" займе занадто довго; наприклад, що станеться, якщо get_adc_data()потрібно 30 мс? ви пропустите три можливості перевірити характер і повторити його.

Реалізація таймера циклу + часто реалізується набагато простіше, ніж спільний багатозадачний мікроядер, оскільки ваш код може бути розроблений більш конкретно для завдання, яке виконується. Ви насправді не так багатозадачні, як розробляти фіксовану систему, де ви даєте кожній підсистемі певний час для виконання її завдань дуже специфічним і передбачуваним способом. Навіть багатозадачна система, що працює в кооперативі, повинна мати загальну структуру завдань для кожного потоку, і наступний потік, який буде виконуватися, визначається функцією планування, яка може стати досить складною.

Механізми блокування для всіх трьох систем однакові, але накладні витрати, необхідні для кожної, зовсім інші.

Особисто я майже завжди кодую цей останній стандарт, виконання циклу + таймер. Я вважаю, що різьблення - це те, що слід використовувати дуже ощадно. Не тільки складніше писати та налагоджувати, але й вимагає більше накладних витрат (попереджувальний багатозадачний мікроядро завжди буде більшим, ніж дурно простий таймер і послідовник основного циклу).

Також є приказка, що кожен, хто працює над темами, оцінить:

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

:-)


Дуже дякую за вашу відповідь з детальними прикладами, akohlsmith. Однак я не можу зробити висновок із вашої відповіді, чому ви обираєте просту архітектуру таймера та фонового циклу, а не спільну багатозадачність . Не зрозумій мене неправильно. Я дуже вдячний за вашу відповідь, яка надає багато корисної інформації про різні графіки. Я просто не маю сенсу.
hailang

Чи можете ви трохи більше попрацювати над цим?
hailang

Дякую, akohlsmith. Мені подобається речення, яке ви поставили в кінці. Мені знадобилося деякий час, щоб визнати це :) Назад до вашої відповіді, ви майже завжди кодуєте цикл + реалізацію таймера. Потім, у випадках, коли ви відмовились від цієї реалізації та звернетесь до непередбачуваної ОС, що змусило вас це зробити?
hailang

Я працював як з кооперативною, так і з превентивною багатозадачністю, коли запускав чужу ОС. Або Linux, ThreadX, ucOS-ii або QNX. Навіть у деяких із цих ситуацій я використовував простий та ефективний цикл таймер + подія ( poll()приходить відразу на думку).
akohlsmith

Я не прихильник різьблення або багатозадачності вбудованих, але знаю, що для складних систем це єдиний розумний варіант. Консервовані мікроопераційні системи дають вам швидкий спосіб впорядкувати та працювати, а також часто надавати драйвери пристроїв.
akohlsmith

6

Багатозадачність може бути корисною абстракцією у багатьох проектах мікроконтролерів, хоча справжній попереджуючий планувальник був би надто важким і непотрібним у більшості випадків. Я зробив понад 100 проектів мікроконтролерів. Я неодноразово використовував завдання кооперативу, але до цього часу переважне переключення завдань з пов'язаним багажем досі не було доречним.

Проблеми з попереднім завданням, призначеним для виконання кооперативних завдань, є:

  1. Набагато важкіший. Попереджувальні планувальники завдань складніші, займають більше кодового простору та займають більше циклів. Вони також вимагають хоча б одного перерви. Це часто є неприйнятним навантаженням на заявку.

  2. Мутекси потрібні навколо структур, до яких можна отримати доступ одночасно. У системі співпраці ви просто не називаєте TASK_YIELD посеред того, що має бути атомною операцією. Це впливає на черги, загальний глобальний стан і перебирається на багато місць.

Взагалі, присвячувати завдання певній роботі має сенс, коли ЦП може це підтримати, і це завдання є досить складним з достатньою роботою, що залежить від історії, що розбиття його на кілька окремих подій було б громіздким. Як правило, це стосується обробки вхідного потоку зв'язку. Зазвичай такі речі залежать від стану, залежно від певного вкладу. Наприклад, можуть бути байти опкоду, а потім - унікальні для кожного коду байти даних. Тоді виникає проблема цих байтів, коли щось інше відчуває, як надіслати їх. Окремим завданням, що обробляє вхідний потік, ви можете змусити його відображатися в коді завдання так, ніби ви виходите і отримуєте наступний байт.

Загалом, завдання корисні, коли в державі багато контексту. Завдання - це в основному машини, коли ПК є змінною стану.

Багато речей, які має робити мікро, можна виразити як реагування на набір подій. Як результат, у мене зазвичай є цикл основної події. Це перевіряє кожну можливу подію послідовно, потім стрибає на вершину і робить це все заново. Коли обробка події займає більше ніж кілька циклів, я зазвичай переходжу до початку циклу події після обробки події. Це фактично означає, що події мають неявний пріоритет залежно від того, де вони перевіряються у списку. Для багатьох простих систем це досить добре.

Іноді ви отримуєте трохи складніші завдання. Їх часто можна розбити на послідовність невеликої кількості окремих справ. Ви можете використовувати внутрішні прапори як події в цих випадках. Я робив подібні речі багато разів на низьких кінцевих фотографіях.

Якщо у вас є основна структура подій, як описано вище, але ви також повинні відповідати на командний потік через UART, наприклад, корисно мати окрему задачу для обробки отриманого потоку UART. Деякі мікроконтролери мають обмежені апаратні ресурси для виконання багатозадачних завдань, наприклад, PIC 16, який не може читати чи записувати власний стек викликів. У таких випадках я використовую те, що я називаю псевдозадачею для командного процесора UART. Основний цикл подій все ще обробляє все інше, але одна з його подій, яку потрібно обробити, полягає в тому, що UART отримав новий байт. У такому випадку він переходить на рутину, яка виконує цю псевдозадачу. Командний модуль UART містить код завдання, а адреса виконання та декілька значень реєстру завдання зберігаються в оперативній пам'яті в цьому модулі. Код, на який переходить цикл події, зберігає поточні регістри, завантажує збережені регістри завдань, і переходить на адресу перезавантаження завдання. Код завдання викликає макрос YIELD, який робить реверс, який згодом переходить до початку циклу основної події. У деяких випадках цикл основної події виконує псевдозадачу один раз за прохід, як правило, внизу, щоб зробити це подіями низького пріоритету.

У PIC 18 і новіших версіях я використовую справжню спільну систему завдань, оскільки стек викликів читається і записується за допомогою мікропрограмного забезпечення. У цих системах адреса перезапуску, декілька інших фрагментів стану та покажчик стека даних зберігаються в буфері пам'яті для кожного завдання. Щоб дозволити виконувати всі інші завдання один раз, завдання викликає TASK_YIELD. Це зберігає поточний стан завдання, переглядає список наступного завдання, завантажує його стан, а потім виконує його.

У цій архітектурі цикл основної події - це лише інше завдання, з викликом TASK_YIELD у верхній частині циклу.

Весь мій багатозадачний код для PIC доступний безкоштовно. Щоб побачити це, встановіть випуск програми PIC Development Tools за адресою http://www.embedinc.com/pic/dload.htm . Шукайте файли з "завданням" у своїх іменах у каталозі ДЖЕРЕЛ> ПІК для 8-бітних ПІК та в каталозі ДЖЕРЕЛ> ДСПІК для 16-бітних ПІК.


mutexe все ще можуть знадобитися в кооперативних багатозадачних системах, хоча це рідко. Типовий приклад - ISR, який потребує доступу до критичного розділу. Цього практично можна уникнути за рахунок кращого дизайну або вибору відповідного контейнера даних для критичних даних.
akohlsmith

@akoh: Так, я декілька разів використовував мьютекси для обробки спільного ресурсу, наприклад, доступу до шини SPI. Моя думка полягала в тому, що мютекси від природи не потрібні в тій мірі, в якій вони є в превентивній системі. Я не хотів сказати, що вони ніколи не потрібні або ніколи не використовуються в кооперативній системі. Також mutex в кооперативній системі може бути таким же простим, як спінінг у циклі TASK_YIELD, перевіряючи один біт. У переважній системі їх, як правило, потрібно вбудувати в ядро.
Олін Латроп

@OlinLathrop: Я думаю, що найбільш важливою перевагою систем, що не мають переваги, коли мова йде про мютекси, є те, що вони потрібні лише при взаємодії безпосередньо з перериваннями (які за своєю природою переважають), або коли потрібен час, щоб утримати захищений ресурс перевищує час, який потрібно провести між дзвінками "дохідності", або потрібно утримувати захищений ресурс навколо дзвінка, який може "принести" (наприклад, "запис даних у файл"). У деяких випадках, коли вихід із "записом даних" був би проблемою, я включив ...
supercat

... метод перевірити, скільки даних можна записати негайно, і метод (який, можливо, призведе до отримання), щоб забезпечити наявність деякої кількості (прискорення рекультивації брудних блоків спалаху та чекання, поки відповідне число буде відновлено) .
supercat

Привіт Олін, мені так подобається твоя відповідь. Її інформація далеко не виходить за мої запитання. Він включає багато практичного досвіду.
hailang

1

Редагувати: (Я залишу свою попередню посаду нижче; можливо, колись це допоможе.)

Багатозадачні операційні системи будь-якого виду та процедури переривання обслуговування не є або не повинні бути конкуруючими архітектурами системи. Вони призначені для різних робочих місць на різних рівнях системи. Переривання дійсно призначені для коротких послідовностей коду для вирішення негайних завдань, таких як перезапуск пристрою, можливо опитування пристроїв, що не перебивають роботу, затримка часу в програмному забезпеченні і т.д. негайні потреби були задоволені. Якщо все, що вам потрібно зробити, це перезапустити таймер і ввімкнути світлодіод або імпульсувати інший пристрій, ISR зазвичай може робити це все на передньому плані безпечно. В іншому випадку йому потрібно повідомити тло (встановивши прапор або в черзі повідомлення), що щось потрібно робити, і відпустити процесор.

Я бачив дуже прості програмні структури, фоновий цикл яких просто цикл простою: for(;;){ ; } . Вся робота виконувалася в таймері ISR. Це може працювати, коли програмі потрібно повторити постійну операцію, яка гарантовано закінчиться менше, ніж за таймер; приходять на думку певні обмежені види обробки сигналів.

Особисто я пишу ISR, які очищають вихід, і нехай фон переймає все, що потрібно робити, навіть якщо це так просто, як множення, і додаю, що це можна зробити за частку періоду за таймер. Чому? Колись у мене з’явиться яскрава ідея додати ще одну «просту» функцію до моєї програми, і «чорт, просто знадобиться короткий ISR, щоб зробити це», і раптом моя раніше проста архітектура наростає деякі взаємодії, які я не планував і трапляються непослідовно. Це не так весело налагоджувати.


(Раніше розміщене порівняння двох видів багатозадачних завдань)

Переключення завдань: Переважаючий МТ піклується про переключення завдань для вас, включаючи забезпечення того, що жодна нитка не отримує голодування процесора, і що потоки з високим пріоритетом запускаються, як тільки вони будуть готові. Кооперативний MT вимагає, щоб програміст переконався, що жодна нитка не затримує процесор занадто довго. Вам також доведеться вирішити, як довго занадто довго. Це також означає, що кожного разу, коли ви змінюєте код, вам потрібно буде знати, чи перевищує який-небудь сегмент коду цей квантовий час.

Захист неатомних операцій. За допомогою PMT вам потрібно буде переконатися, що в середині операцій не відбувається заміна потоку, яку не слід розділяти. Читання / запис певних пар-регістрів пристроїв, якими слід обробляти певний порядок або, наприклад, протягом максимального часу. З CMT це досить просто - просто не поступайтеся процесору посеред такої операції.

Налагодження: як правило, простіше з CMT, оскільки ви плануєте, коли / де відбуватимуться перемикання потоків. Умови перегонів між потоками та помилками, пов’язаними з операціями, які не захищені потоками, з PMT, особливо важко налагодити, оскільки зміни потоку є ймовірними, тому не повторюються.

Розуміння коду: Нитки, написані для PMT, написані майже так, ніби вони можуть стояти окремо. Нитки, написані для CMT, записуються у вигляді сегментів, і залежно від структури програми, яку ви вибрали, читачеві може бути складніше слідувати.

Використання коду бібліотеки, який не є безпечним для потоків: Вам потрібно буде переконатися, що кожна функція бібліотеки, яку ви викликаєте, під потоком безпеки для PMT printf () та scanf () та їх варіанти майже завжди не є безпечними для потоків. За допомогою CMT ви будете знати, що жодна зміна потоку не відбудеться, крім випадків, коли ви конкретно даєте процесор.

Система обмеженого стану, керована машиною для керування механічним пристроєм та / або відстеження зовнішніх подій, часто є хорошими кандидатами на CMT, оскільки на кожному заході робити не багато чого - запустити або зупинити двигун, встановити прапор, вибрати наступний стан і т. д. Таким чином, функції зміни стану суттєво короткі.

Гібридний підхід може працювати дуже добре в таких системах: CMT для управління державною машиною (і, отже, більшість апаратних засобів), що працює як один потік, і ще один або два потоки, щоб робити довші запущені обчислення, розпочаті державою змінити.


Дякую за вашу відповідь, Джорберте. Але це не з урахуванням мого питання. Він порівнює попереджувальну ОС проти непереважну ОС, але не порівнює непереважну ОС та не-ОС.
hailang

Правильно - вибачте. Моя редакція повинна краще вирішити ваше питання.
JRobert
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.