Каковы преимущества не преимущественной ОС? а цена на эти льготы?


14

Каковы преимущества неперекачивающей ОС по сравнению с микроконтроллером с обнаженным металлом, по сравнению с самодельным кодом с фоновой петлей и архитектурой прерываний по таймеру? Какие из этих преимуществ являются достаточно привлекательными для того, чтобы проект принял ОС без упреждения, вместо того, чтобы использовать самодельный код с архитектурой фонового цикла?
,

Пояснение к вопросу:

Я действительно ценю все те, кто ответил на мой вопрос. Я чувствую, что ответ был почти там. Я добавляю это объяснение к моему вопросу здесь, которое показывает мое собственное рассмотрение и может помочь сузить вопрос или сделать его более точным.

Я пытаюсь понять, как выбрать наиболее подходящую ОСРВ для проекта в целом.
Для достижения этой цели поможет лучшее понимание основных концепций и наиболее привлекательных преимуществ различных типов ОСРВ и соответствующей цены, поскольку не существует наилучшей ОСРВ для всех приложений.
Я читал книги об ОС несколько лет назад, но у меня их больше нет. Я искал в Интернете, прежде чем опубликовать свой вопрос здесь, и обнаружил, что эта информация была наиболее полезной: http://www.ustudy.in/node/5456 .
Есть много другой полезной информации, такой как введения на веб-сайте различных ОСРВ, статьи, сравнивающие упреждающее планирование и не упреждающее планирование, и т. Д.
Но я не нашел ни одной темы, в которой упоминалось, когда выбирать ОС с непрогнозирующей ОСРВ, а когда лучше, просто написать собственный код, используя прерывание по таймеру и фоновый цикл.
У меня есть свои собственные ответы, но я их недостаточно удовлетворяю.
Я действительно хотел бы знать ответ или мнение от более опытных людей, особенно в отраслевой практике.

Насколько я понимаю, до сих пор:
независимо от того, используете ли вы или не используете ОС, всегда необходимы определенные виды кодов планирования, даже в форме кода, подобного следующему:

    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 Kernel (небольшая непревзойденная ОСРВ - заявлена ​​на ее веб-сайте)
http://www.frontiernet.net/~rhode/kisskern.html
(2) uSmartX (облегченная ОСРВ - заявлено на его веб-сайте)
(3) FreeRTOS (Это ОСВ с вытеснением, но, как я понимаю, она также может быть настроена как
ОС без ОС ) (4) ОС / ОС (аналогично FreeRTOS)
(5 ) устаревший код ОС / планировщика в некоторых компаниях (обычно создаваемый и поддерживаемый компанией внутри компании)
(невозможно добавить дополнительные ссылки из-за ограничений новой учетной записи StackOverflow)

Как я понимаю, ОС без упреждения представляет собой набор этих кодов:
(1) планировщик, использующий стратегию без упреждения.
(2) средства для межзадачного общения, мьютекса, синхронизации и контроля времени.
(3) управление памятью.
(4) другие полезные средства / библиотеки, такие как Файловая система, сетевой стек, GUI и т. Д. (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()контекста и снова запустить его из 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. Однако из вашего ответа я не могу сделать вывод, почему вы выбираете простую архитектуру таймера и фонового цикла, а не совместную многозадачность . Не пойми меня неправильно. Я очень ценю ваш ответ, который предоставляет много полезной информации о различных расписаниях. У меня просто нет смысла.
Хайлянг,

Не могли бы вы поработать над этим немного больше?
Хайлянг

Спасибо, Аколсмит. Мне нравится предложение, которое вы ставите в конце. Мне потребовалось некоторое время, чтобы распознать это :) Возвращаясь к сути вашего ответа, вы почти всегда пишете код реализации loop + timer. Затем, в случаях, когда вы отказались от этой реализации и обратились к ОС без упреждения, что заставило вас сделать это?
Хайланг,

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

Я не фанат многопоточности или многозадачности во встроенных системах, но я знаю, что для сложных систем это единственный разумный вариант. Консервированные микро-операционные системы дают вам быстрый способ начать работу и часто предоставляют драйверы устройств.
akohlsmith

6

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

Проблемы с упреждающими задачами в сочетании с совместными задачами:

  1. Гораздо тяжелее. Упреждающие планировщики задач более сложны, занимают больше места в коде и занимают больше циклов. Они также требуют как минимум одного прерывания. Это часто является недопустимым бременем для приложения.

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

В целом, назначение задачи определенной работе имеет смысл, когда процессор может это поддерживать, и эта задача достаточно сложна с достаточным количеством операций, зависящих от истории, поэтому разбить ее на несколько отдельных отдельных событий будет громоздко. Это обычно имеет место при обработке входного потока связи. Такие вещи обычно сильно зависят от состояния в зависимости от некоторых предыдущих входных данных. Например, могут быть байты кода операции, за которыми следуют байты данных, уникальные для каждого кода операции. Тогда есть проблема этих байтов, приходящих к вам, когда что-то еще похоже на их отправку. С помощью отдельной задачи, обрабатывающей поток ввода, вы можете сделать так, чтобы он отображался в коде задачи, как если бы вы выходили и получали следующий байт.

В целом, задачи полезны, когда много контекста состояния. Задачи - это, в основном, конечные автоматы, причем ПК является переменной состояния.

Многие вещи, которые должен делать микро, могут быть выражены как реакция на ряд событий. В результате у меня обычно есть основной цикл событий. Это проверяет каждое возможное событие в последовательности, затем возвращается к началу и делает все это снова. Когда обработка события занимает больше, чем несколько циклов, я обычно возвращаюсь к началу цикла обработки событий после обработки события. Фактически это означает, что события имеют подразумеваемый приоритет в зависимости от того, где они отмечены в списке. На многих простых системах это достаточно хорошо.

Иногда вы получаете немного более сложные задачи. Они часто могут быть разбиты на последовательность из небольшого количества отдельных вещей, которые нужно сделать. В этих случаях вы можете использовать внутренние флаги как события. Я делал подобные вещи много раз на PICах низкого уровня.

Если у вас есть базовая структура событий, как указано выше, но вы также должны реагировать на поток команд, например, по 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 . Найдите файлы с именами «task» в их именах в каталоге SOURCE> PIC для 8-битных PIC и в каталоге SOURCE> DSPIC для 16-битных PIC.


мьютексы все еще могут быть необходимы в кооперативных многозадачных системах, хотя это редко. Типичным примером является ISR, которому нужен доступ к критическому разделу. Этого почти всегда можно избежать путем улучшения дизайна или выбора подходящего контейнера данных для критических данных.
akohlsmith

@akoh: Да, я несколько раз использовал мьютексы для обработки общего ресурса, например, доступа к шине SPI. Моя точка зрения заключалась в том, что мьютексы не являются обязательными в той степени, в которой они находятся в упреждающей системе. Я не хотел сказать, что они никогда не нужны или никогда не используются в кооперативной системе. Кроме того, мьютекс в кооперативной системе может быть таким же простым, как вращение в цикле TASK_YIELD, проверяющем один бит. В упреждающей системе они обычно должны быть встроены в ядро.
Олин Латроп

@OlinLathrop: Я думаю, что наиболее значительным преимуществом систем без вытеснения, когда речь идет о мьютексах, является то, что они требуются либо при непосредственном взаимодействии с прерываниями (которые по своей природе являются вытесняющими), либо когда нужно удерживать защищенный ресурс. превышает время, которое вы хотите потратить между вызовами «yield», или хотите удерживать защищенный ресурс вокруг вызова, который «может» дать (например, «записать данные в файл»). В некоторых случаях, когда возникла проблема с выходом в вызове «запись данных», я включил ...
суперкат

... метод для проверки того, сколько данных может быть записано немедленно, и метод (который, вероятно, даст) для обеспечения доступности некоторого количества (ускорение восстановления грязных флэш-блоков и ожидание восстановления подходящего числа) ,
суперкат

Привет, Олин, мне очень нравится твой ответ. Его информация далеко за пределами моих вопросов. Это включает в себя много практического опыта.
Хайланг

1

Изменить: (Я оставлю свой предыдущий пост ниже; возможно, это поможет кому-то когда-нибудь.)

Многозадачные ОС любого рода и подпрограммы обработки прерываний не являются или не должны быть конкурирующими системными архитектурами. Они предназначены для разных работ на разных уровнях системы. Прерывания действительно предназначены для коротких кодовых последовательностей, чтобы обрабатывать немедленные хлопоты, такие как перезапуск устройства, возможно, опрос устройств без прерывания, хронометраж в программном обеспечении и т. Д. Обычно предполагается, что фон будет выполнять любую дальнейшую обработку, которая больше не критична по времени после насущные потребности были удовлетворены. Если все, что вам нужно сделать, это перезапустить таймер и переключить светодиод или включить другое устройство, ISR обычно может делать все это на переднем плане безопасно. В противном случае он должен сообщить фону (установив флаг или поставив в очередь сообщение), что что-то нужно сделать, и освободить процессор.

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

Лично я пишу ISR, которые очищают выход, и позволяют фону перенять все, что нужно сделать, даже если это так просто, как умножение и добавление, что можно сделать за долю таймера. Почему? Когда-нибудь у меня появится блестящая идея добавить еще одну «простую» функцию в мою программу, и «черт, для этого потребуется короткий ISR», и вдруг моя ранее простая архитектура расширяет некоторые взаимодействия, которые я не планировал и случается непоследовательно. Это не очень весело для отладки.


(Ранее размещено сравнение двух видов многозадачности)

Переключение задач: Превентивный MT позаботится о переключении задач за вас, включая обеспечение того, чтобы ни один из потоков не нуждался в ЦП, и что потоки с высоким приоритетом запускаются, как только они готовы. Совместная MT требует, чтобы программист удостоверился, что ни один поток не удерживает процессор слишком долго одновременно. Вам также придется решить, как долго это будет слишком долго. Это также означает, что всякий раз, когда вы изменяете код, вам нужно знать, превышает ли какой-либо сегмент кода этот временной интервал.

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

Отладка: Обычно проще с CMT, так как вы планируете, когда и где произойдет переключение потоков. Условия гонки между потоками и ошибки, связанные с не поточно-ориентированными операциями с PMT, особенно трудно отлаживать, поскольку изменения потоков вероятностны, поэтому не повторяются.

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

Использование не поточно-ориентированного библиотечного кода: вам необходимо убедиться, что каждая библиотечная функция, которую вы вызываете, работает в поточно-ориентированном режиме PMT. printf () и scanf () и их варианты почти всегда не являются поточно-ориентированными. С CMT вы будете знать, что никакое изменение потока не произойдет, за исключением случаев, когда вы специально используете процессор.

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

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


Спасибо за ваш ответ, JRobert. Но это не с учетом моего вопроса. Он сравнивает вытесняющую ОС с не вытесняющей ОС, но не сравнивает не вытесняющую ОС с не-ОС.
Хайланг

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