Читаем Операционная система UNIX полностью

/dev/kmemОбеспечивает доступ к виртуальной памяти ядра. Зная виртуальные адреса внутренних структур ядра, процесс может считывать хранящуюся в них информацию. С помощью этого драйвера может, например, быть реализована версия утилиты ps(1), выводящей информацию о состоянии процессов в системе.
/dev/ksymsОбеспечивает доступ к разделу исполняемого файла ядра, содержащего таблицу символов. Совместно с драйвером /dev/kmem обеспечивает удобный интерфейс для анализа внутренних структур ядра.
/dev/memОбеспечивает доступ к физической памяти компьютера.
/dev/nullЯвляется "нулевым" устройством. При записи в это устройство данные просто удаляются, а при чтении процессу возвращается 0 байтов. Примеры использования этого устройства рассматривались в главе 1, когда с помощью /dev/null мы подавляли вывод сообщений об ошибках.
/dev/zeroОбеспечивает заполнение нулями указанного буфера. Этот драйвер часто используется для инициализации области памяти.
<p>Базовая архитектура драйверов</p>

Драйвер устройства адресуется старшим номером (major number) устройства. Напомним, что среди атрибутов специальных файлов устройств, которые обеспечивают пользовательский интерфейс доступа к периферии компьютера, это число присутствует наряду с другим, также имеющим отношение к драйверу, — младшим номером (minor number). Младший номер интерпретируется самим драйвером (например, для клонов, оно задает старшее число устройства, которое требуется "размножить"). Другим примером использования младших номеров может служить драйвер диска. В то время как доступ к любому из разделов диска осуществляется одним и тем же драйвером и, соответственно, через один и тот же старший номер, младший номер указывает, к какому именно разделу требуется обеспечить доступ.

Доступ к драйверу осуществляется ядром через специальную структуру данных (коммутатор устройств), каждый элемент которой содержит указатели на соответствующие функции драйвера — точки входа. Старшее число, по существу, является указателем на элемент коммутатора устройств, обеспечивая, тем самым, ядру возможность вызова необходимой функции указанного драйвера. Таким образом, коммутатор устройств определяет базовый интерфейс драйвера устройств.

Этот интерфейс различен для блочных и символьных устройств. Ядро содержит коммутаторы устройств двух типов: bdevsw для блочных и cdevsw для символьных устройств. Ядро размещает отдельный массив для каждого типа коммутатора, и любой драйвер устройства имеет запись в соответствующем массиве. Если драйвер обеспечивает как блочный, так и символьный интерфейсы, его точки входа будут представлены в обоих массивах.

Типичное описание этих двух массивов имеет следующий вид (назначение различных точек входа мы рассмотрим далее в этом разделе):

struct bdevsw[] {

 int (*d_open)();

 int (*d_close)();

 int (*d_strategy)();

 int (*d_size)();

 int (*d_xhalt)();

 ...

} bdevsw[];

struct cdevsw[] {

 int (*d_open)();

 int (*d_close)();

 int (*d_read)();

 int (*d_write)();

 int (*d_ioctl)();

 int (*d_xpoll)();

 int (*d_xhalt)();

 struct streamtab *d_str;

 ...

} cdevsw[];

Ядро вызывает функцию open() требуемого драйвера следующим образом:

(*bdevsw[getmajor(dev)].d_open)(dev, ...);

передавая ей в качестве одного из параметров переменную dev (типа dev_t), содержащую старший и младший номера. Макрос getmajor() служит для извлечения старшего номера из переменной dev. Благодаря этому драйвер имеет возможность определить, с каким младшим номером была вызвана функция open(), и выполнить соответствующие действия.

Перейти на страницу:

Похожие книги