Phantom-84Вообще-то спецификации пишут на естественных языках, а не на алгоритмических (на коих их записать попросту невозможно). И "понимабельность" алгоритмов от замены "если" на "if" навряд ли возрастёт. Кроме того, как прикажете описывать такие вещи, как возникновение прерываний, исходное состояние и т.д. и т.п.?
Я набросал макет драйвера FDC, осуществляющего чтение с диска без запрета прерываний; его текст привожу ниже. Естественно, при этом пришлось сделать кучу предположений и умолчаний об особенностях работы ОС.
Код:
// Точка входа по инициализации драйвера
// Вызывается в процессе загрузки ОС
procedure Init;
begin
InitController; // Инициализация "железа"
Buf1Free:= True; // Оба буфера свободны
Buf2Free:= True;
end;
// Точка входа по запуску операции ввода-вывода
// ОС вызывает драйвер по этой точке только в том случае, если очередь
// запросов ввода-вывода была пуста или же драйвер вызвал ОС с сообщением
// о завершении ранее начатой операции (через IOEnded)
// Прерывания разрешены (уровень отложенных обработчиков)
procedure NewIORequest(...);
begin
CheckIORequest; // Проверка запроса ввода-вывода на корректность
if ErrorFound then IOEnded(Failure) // Есть ошибки - операция завершается,
// не начавшись; ОС может повторно
// вызвать NewIORequest для запуска
// следующей операции
else begin
SCnt:= ... // Установка исходного счётчика секторов
CHS:= ... // Установка исходного дискового адреса
StartIO; // Запуск операции на выполнение (см. ниже)
end;
end;
// Запуск операции на контроллере и пересылка предыдущего буфера
// Для упрощения рассматривается только операция чтения с дискеты
// Прерывания разрешены (уровень отложенных обработчиков)
procedure StartIO;
begin
if Buf1Free then
begin
Buf1Free:= False;
StartRead(CHS, Buf1);
FlushBuffer(Buf2Free, Buf2);
end else
begin
Buf2Free:= False;
StartRead(CHS, Buf2);
FlushBuffer(Buf1Free, Buf1);
end;
end;
// Точка входа в драйвер по прерыванию от FDC
// Прерывания от FDC запрещены (уровень обработчика аппаратного прерывания)
// Предполагается, что регистры сохранены и другие подобные действия,
// общие для любых прерываний, уже выполнены
procedure InterruptHandler;
begin
GetFDCStatus; // Считывание состояния FDC, чтобы снять запрос прерывания
DeferHandling; // Разрешение прерываний (FDC вызвать их уже не может)
// и откладывание обработки до тех пор, пока не закончится
// выполнение других отложенных обработчиков
// Отсюда выполнение продолжается на уровне отложенных обработчиков
if IOError then // Если прерывание вызвано ошибкой ввода-вывода
begin
ResetController; // Сброс контроллера, очистка буферов и т.п.
Buf1Free:= True;
Buf2Free:= True;
IOEnded(Failure); // Вызов ОС с сообщением о неудачном завершении
// операции; она может инициировать новую операцию,
// вызвав драйвер через StartIO
end else
begin
Dec(SCnt); // Уменьшение счётчика секторов
if SCnt = 0 then // Чтение завершено
begin
if Buf1Free then FlushBuffer(Buf2Free, Buf2) // Копирование последнего
else FlushBuffer(Buf1Free, Buf1); // прочитанного буфера
IOEnded(Successful); // Операция завершена успешно
end else
begin
Inc(CHS); // Дисковый адрес следующего читаемого сектора
StartIO; // Запуск очередного чтения
end;
end;
end;
// Копирование буфера в область памяти задачи
// Прерывания разрешены (уровень отложенных обработчиков)
procedure FlushBuffer(var BufFree : Boolean; Buf : Pointer);
begin
if BufFree then Exit;
MoveToUserBuffer(Buf); // Собственно копирование в буфер пользователя
// (его адрес извлекается из запроса ввода-вывода)
IncUserBufferPtr; // Передвижение указателя к участку буфера
// пользователя для следующего сектора
BufFree:= True; // Освобождение сброшенного буфера драйвера
end;
"За кадром" остаётся синхронизация выполнения различных участков кода внутри ОС -- этот вопрос не имеет прямого отношения к вводу-выводу, поскольку относится ко всему ядру в целом. Я использую отложенную обработку прерываний, когда при запрещённых прерываниях выполняется лишь необходимый минимум действий, связанных с аппаратурой, а основная обработка выполняется в так называемых "отложенных обработчиках", которые выполняются строго последовательно (новый отложенный обработчик получает управление только тогда, когда закончится выполнение всех предыдущих отложенных обработчиков). Эта схема использовалась, в частности, в ОСРВ RSX-11 (для PDP-11), откуда перекочевала в VAX/VMS, а из неё -- в Windows NT (разница фактически только в "навороченности" этой схемы в разных ОС, но суть остаётся одной и той же). Если есть необходимость/желание, можно обсудить вопросы синхронизации внутри ядра в отдельной теме.