Лично мне это всё как головоломка интересно. Но если понадеяться на участие местных опытных форумчан, то может что и получится из этого.. В конце концов - в споре рождается истина. Должна по крайней мере.. В общем, в одном из первых постов Химик предлагал такую штуку, как двухуровневая обработка прерываний (не смотреть пост выше). Недавно я её вспомнил, и решил "примотать" к теме кооперативки. Вот что получилось: Операционная система из одного потока (вымышленная). Состоит из двух логических уровней. 1. (Нижний) Асинхронный. 2. (Верхний) Синхронный.
Нижний уровень - уровень обработчиков прерываний и исключений. Это те объекты системы, которые получают управление внезапно, т.е. асинхронно, по отношению к работе системы. Отдельно об обработчиках: 1. Обработчики прерываний. Их главная задача - избавить верхний уровень от активного опроса при работе с устройствами в/в, при этом представляя асинхронные события в синхронной форме. Два способа сделать это - "буферизировать" события, добавляя их в очередь событий, либо сразу выполнять диспетчеризацию "ожидающих", не откладывая на потом. Лично мне больше нравится второй вариант. Все обработчики прерываний при этом - маленькие однообразные функции, но с разным "кодом" прерывания. 2. Обработчики исключений. Те ситуации, которые требуют неотложной реакции системы обрабатываются ими. Это может быть и ошибка деления на ноль, и страничное исключение и т.д. Да хоть нажатие правой кнопки мыши, если это так критично. Обработка исключений может быть и вложенной, по всем канонам и правилам. Хотя я не вижу в этом особого смысла. Трудно представить ситуацию, когда, например, обработчик страничного исключения вызвал бы ошибку деления на ноль, или наоборот. Естественно, обработчики исключений более толстые, чем "братья по уровню", да и функции у них по-разнообразней.
Верхний уровень - уровень синхронного коммунизма:), кооперативной многозадачности или многозадачности на основе активных объектов (АО) - кому-как больше нравится. На дне уровня лежит тонкий слой системы обмена сообщениями. Поверх него располагается планировщик. Всё остальное - толстый слой активных объектов (потоков, по-старинке). АО могут находиться в трёх состояниях: "исполнение", "готовность", "ожидание". Диспетчеризацией занимаются: обработчики прерываний, система обмена сообщениями, планировщик. Планировщик занимается только очередью "готовых" АО. И только он может переводить их в состояние "исполнение", передавая управление на нужную точку входа. Остальные диспетчеры могут только переводить АО из "ожидания" в "готовность". Именно поэтому планировщик располагается выше по уровню. В состояние "ожидание" АО переходят сами, вызывая функции ожидания сообщения или прерывания, тем самым передавая управление планировщику или системе обмена сообщениями. Механизм взаимодействия АО, как уже стало ясно - это обмен сообщениями. При этом сообщения являются и объектами синхронизации, т.к. операции с ними влияют на состояния объектов. Работа объекта начинается с получения сообщения или события. Далее он выполняет возложенную на него миссию, и переходит к ожиданию нового сообщения/события. События - это специальные "легкие" сообщения. Если послать объекту событие, то система обмена сообщениями просто переведёт ожидающего адресата в состояние готовности. При этом адресат не получит ни одного байта данных. Т.е. события - это однобитовые сообщения. Именно события и посылают обработчики прерываний. Ещё одно отличие сообщений от событий: сообщения отправляются одному, а получаются от всех, а события отправляются всем, а ожидаются от одного.
Возможные функции здесь: Message(Destination, &Msg) - отправить сообщение. WaitMessage(Source*, &Msg) - ждать сообщение. Event() - "создать" событие. WaitEvent(Source) - ждать событие. Sleep() - пропустить "ход".
Звёздочкой пометил источник, в случае не указания которого является возвращаемым аргументом (что вероятнее всего).
Или более краткий вариант в духе L4: Msg(PID, &Msg, operation_type) - в зависимости от выбранного типа операции, отправляет/принимает сообщения/события. Передаваемые и принимаемые аргументы определяются типом операции, как и вызов команды Sleep().
|