Сложный конечный автомат
Драйвер, таким образом, состоит из основной нити, обработчика прерывания, и, возможно, одной или нескольких высокоприоритетных нитей, создаваемых обработчиком. Все эти нити совместно (и, как правило, гарантируя взаимоисключение) исполняют более или менее сложный конечный автомат, состояния которого соответствуют этапам выполнения очередного запроса к устройству.
Как правило, первое состояние автомата обрабатывается основной нитью драйвера, а последующие – обработчиком прерываний. В финальном состоянии автомата мы сообщаем процессу, породившему запрос, что запрос отработан. В зависимости от протокола взаимодействия этого процесса основной нитью драйвера, такое сообщение может осуществляться как fork-процессом, так и пробуждением основной нити.
Драйвер IDE/ATA для Linux
В примере 10.5 приведена основная функция обработки запроса и функция об работки прерывания, используемая при записи нескольких секторов. Обе эти функции вызываются драйвером контроллера IDE/ATA, который представляет собой диспетчер запросов к подключенным к контроллеру устройствам.
Структура *hwgroup представляет собой блок переменных состояний контроллера устройства. Эта структура содержит также указатель на текущий запрос к устройству. Информации, содержащейся в этих структурах, достаточно, чтобы очередная функция конечного автомата драйвера узнала все, необходимое ей для выполнения очередного этапа запроса. В данном случае конечный автомат весьма прост и состоит из многократного вызова функции ide_multiwrite, копирующей в контроллер очередной блок данных. Условием завершения автомата служат ошибка контроллера либо завершение запроса. Функции ide__dma_read, ide_dma_write, ide_read и ide_write, исполняемые машиной состояний при обработке других запросов не приводятся.
Пример 10.5. Фрагменты драйвера диска IDE/ATA ОС Linux 2.2, перевод комментариев автора.
/* * ide_multwrite() передает приводу блок из не более, чем mcount * секторов как часть многосекторной операции записи. * * Возвращает 0 при успехе. * * Обратите внимание, что мы можем быть вызваны из двух контекстов – * контекста do_rw и контекста IRQ. IRQ (Interrupt Request, * запрос прерывания)может произойти в любой * момент после того, как мы выведем полное количество секторов, * поэтому мы должны обновлять состояние _до_ того, как мы выведем * последнюю часть данных! */ int ide_multwrite (ide_drive__t *drive, unsigned int mcount) { ide_hwgroup_t *hwgroup= HWGROUP(drive); 'struct request *rq = &hwgroup › wrq; do { char *buffer; int nsect = rq › current_nr_sectors; if (nsect > mcount) nsect = mcount; mcount – = nsect; buffer = rq › buffer; rq › sector += nsect; rq › buffer += nsect << 9; rq › nr_sectors – = nsect; rq › current nr sectors – = nsect; /* Переходим ли мы к следующему bh после этого? */ if (!rq › current_nr_sectors) { struct buffer_head *bh = rq › bh › b_reqnext; /* Завершиться, если у нас кончились запросы V if (!bh) { mcount = 0; } else ( rq › bh = bh; rq › current_nr_sectors = bh › b_size " 9; rq › buffer = bh › b_data; /* * Теперь мы все настроили, чтобы прерывание * снова вызвало нас после последней передачи. */ idedisk_output_data(drive, buffer, nsect"7); } while (mcount); return 0; /* * multwrite_intr() – обработчик прерывания многосекторной записи */ static ide_startstop_t multwrite_intr (ide_drive_t *drive) { byte stat; ir.t i; ide_hwgroup_t *hwgroup = HWGROUP(drive); struct request *rq = &hwgroup › wrq; if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive › bad_wstat)) { if (stat & DRQ_STAT) { /* * Привод требует данных. Помним что rq – * копия запроса. */ if (rq › nr_sectors) { if (ide_multwrite(drive, drive › mult_count)) return ide_stopped; " ide_set__handler (drive, &multwrite_intr, WAIT_CMD, NULL); return ide_started; } } else { /* * Если копирование всех блоков завершилось, * мы можем завершить исходный запрос. */