Опять вернёмся в традиционную область разработки операционных систем (и приложений для микроконтроллеров) — написание драйверов.
Я попробую выделить некоторые общие правила и каноны в этой области. Как всегда — на примере Фантома.
Драйвер — функциональная компонента ОС, ответственная за отношения с определённым подмножеством аппаратуры компьютера.
С лёгкой руки того же Юникса драйвера делятся на блочные и байт-ориентированные. В былые времена классическими примерами были драйвер диска (операции — записать и прочитать сектор диска) и драйвер дисплея (прочитать и записать символ).
В современной реальности, конечно, всё сложнее. Драйвер — типичный инстанс-объект класса, и классов этих до фига и больше. В принципе, интерфейс драйверов пытаются как-то ужать в прокрустово ложе модели read/write, но это самообман. У драйвера сетевой карты есть метод «прочитать MAC-адрес карты» (который, конечно, можно реализовать через properties), а у драйвера USB — целая пачка USB-специфичных операций. Ещё веселее у графических драйверов — какой-нибудь bitblt( startx, starty, destx, desty, xsize, ysize, operation ) — обычное дело.
Цикл жизни драйвера, в целом, может быть описан так:
- Инициализация: драйвер получает ресурсы (но не доступ к своей аппаратуре)
- Поиск аппаратуры: драйвер получает от ядра или находит сам свои аппаратные ресурсы
- Активация — драйвер начинает работу
- Появление/пропадание устройств, если это уместно. См. тот же USB.
- Засыпание/просыпание аппаратуры, если это уместно. В контроллерах часто неиспользуемая аппаратура выключается для экономии.
- Деактивация драйвера — обслуживание запросов прекращается
- Выгрузка драйвера — освобождаются все ресурсы ядра, драйвер не существует.
(Вообще я написал в прошлом году черновик открытой спецификации интерфейса драйвера — см. репозиторий и документ.)
Мне известны три модели построения драйвера:
- Поллинг
- Прерывания
- Нити (threads)