Термины и определения многозадачности

Определения

Для начала дадим несколько определений.

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

Задачи — самостоятельные подпрограммы, имеющие возможность работать параллельно друг с другом.

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

Планировщик — подпрограмма в составе ядра, занимающаяся поиском готовых к выполнению задач, выделением самой приоритетной из них и передачей ей управления.

Сервисы ОС — набор подпрограмм и макросов, предназначенных для доступа к функциям ядра. В основном применяются для управления состояниями задач и обмена информацией между задачами.

Ресурсы — аппаратные возможности микроконтроллера, а также внешние модули, подключенные к нему, которыми может пользоваться задача во время своего выполнения.

Ресурсами являются:

  • в первую очередь — модуль выборки и выполнения команд, программный счетчик, АЛУ, аккумулятор (WREG) и регистр состояния (STATUS) (в один момент времени выполняться может только одна задача);
  • оперативная память (каждая задача может по-своему использовать память);
  • внутренняя EEPROM (пока одна задача выполняет запись, другие не могут производить ни запись ни чтение);
  • встроенные периферийные модули: последовательный порт, параллельный порт, АЦП, таймеры, порты ввода/вывода и т.д. (несколько задач могут выводить данные через USART; один и тот же вывод контроллера используется для разных целей);
  • какие-то внешние устройства, подключенные к контроллеру: LCD, EEPROM, DAC и т.д.

Многозадачность

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

Не вдаваясь в подробности, рассмотрим, как выглядит многозадачность на практике. Рассмотрим для простоты параллельное выполнение двух процессов (задач): Task1 (голубой график) и Task2 (зеленый график).

Рис.1

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

Т.к. контроллер в один момент времени может выполнять только одну команду (или одну последовательность команд), то для выполнения двух задач он должен переключаться с выполнения одной последовательности команд на другую и обратно. Поэтому на уровне программы многозадачность будет выглядеть несколько иначе, чем на уровне пользователя (рис.1б): задачи по очереди сменяют друг друга, захватывая на какое-то время ресурсы контроллера. Одна задача, отработав какое-то время, приостанавливается и начинает выполняться другая. Пользователю же это будет незаметно, т.к. смена задач происходит достаточно быстро.

Контроллер должен знать, когда и какую последовательность команд выполнять, когда и как переключаться между ними. Для этого и служит операционная система. И на уровне операционной системы многозадачность будет выглядеть так, как показано на рис.1в. Обратим внимание, что здесь добавлен еще один график — желтый — ядро операционной системы. Прерываясь, задача Task1 передает управление не задаче Task2, а планировщику операционной системы (в кооперативных ОСРВ именно задача передает управление планировщику, в отличие от вытесняющих, где сам планировщик либо более приоритетная задача отнимают управление у менее приоритетной), который в свою очередь принимает решение о том, какой задаче отдать управление. В нашем случае он отдает управление задаче Task2. Когда Task2 отдает управление планировщику, у Task1 появляется шанс получить управление вновь.

Наконец, для полноты картины добавим еще один график (рис.1г) — прерывания. Как видно из рисунка прерывание может прервать как задачу, так и планировщик.

Здесь, наверное, может возникнуть вопрос: для чего нужны сложности с планировщиком, если можно в вечном цикле (часто называют суперциклом или суперлупом) по очереди вызывать функции, содержащие код для Task1 и Task2? Штука в том, что при вызове функции мы каждый раз попадаем в ее начало, в то время как ОС позволяет нам прервать функцию в любом месте, позволив выполниться другим функциям, а затем продолжить ее выполнение с того места, где она была приостановлена. (Это основное отличие от суперцикла. На остальных отличиях пока не будем заострять внимание.) Благодаря этой особенности, каждая задача может быть написана как отдельная независимая программа (она должна быть написана с учетом некоторых требований, но об этом чуть ниже).

Источник: pic24.ru