Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
16.2 Операции блочного устройства
hardorsoft
25.10.10 в 22:29
|
|
В предыдущем подразделе мы сделали короткое введение в структуру block_device_operations. Теперь мы подробнее рассмотрим эти операции, а потом поговорим об обработке запросов. В конце мы упомянем еще одну возможность драйвера sbull: сделаем вид, что устройство – сменный носитель. Когда бы пользователь не закрыл устройство, устанавливается 30-секундный таймер, и если устройство не открывается в течении этого времени, контекст очищается, и ядру сообщается о смене носителя. 30 секунд дают пользователю время, например на то, чтобы монтировать устройство после создания файловой системы.
hardorsoft
25.10.10 в 22:29
|
|
16.2.1 Методы open и release
hardorsoft
25.10.10 в 22:29
|
|
Для реализации фиктивного съемного носителя sbull должен знать, когда последний пользователь закрыл устройство. Драйвером поддерживается счетчик пользователей. Эта работа выполняется в методах open и release.
hardorsoft
25.10.10 в 22:30
|
|
Метод open выглядит очень похоже на соответствующий метод для символьных устройств. Он получает, в качестве аргументов, указатели на и структуру file. Когда inode ссылается на блочное устройство, поле i_bdev->bd_disk содержит указатель на ассоциированную структуру gendisk. Этот указатель может быть использован для получения драйвером внутренней структуры данных устройства. Фактически это первое что делает метод open.
hardorsoft
25.10.10 в 22:30
|
|
static int sbull_open(struct inode *inode, struct file *filp) { struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data; del_timer_sync(&dev->timer); filp->private_data = dev; spin_lock(&dev->lock); if (! dev->users) check_disk_change(inode->i_bdev); dev->users++; spin_unlock(&dev->lock); return 0; }
hardorsoft
25.10.10 в 22:30
|
|
Получив указатель на структуру устройства, sbull_open, вызывает del_timer_sync для удаления таймера съемного носителя, если тот активен. Обратите внимание, что мы не блокируем устройство посредством spinlock, пока таймер не удален. В противном случае возможно возникновение взаимной блокировки, в том случае если таймерная функция запустится перед ее удалением. Когда устройство заблокировано, мы выполняем функцию ядра check_disk_change, для проверки: удачно ли прошла смена носителя. Можно было бы утверждать, что это должно выполнить ядро, но, следуя стандарту, обработку выполняет драйвер при открытии.
hardorsoft
25.10.10 в 22:31
|
|
На последнем шаге инкрементируем счетчик пользователей, и возвращаемся из функции.
hardorsoft
25.10.10 в 22:31
|
|
Метод release, напротив, выполняет декремент счетчика пользователей, и, при необходимости, запускает таймер смены носителя:
hardorsoft
25.10.10 в 22:31
|
|
static int sbull_release(struct inode *inode, struct file *filp) { struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data; spin_lock(&dev->lock); dev->users--; if (!dev->users) { dev->timer.expires = jiffies + INVALIDATE_DELAY; add_timer(&dev->timer); } spin_unlock(&dev->lock); return 0; }
hardorsoft
25.10.10 в 22:31
|
|
В драйвере, обслуживающем реальное устройство, методы open и release выполняют соответствующую настройку устройства и драйвера. Это может заключаться в раскручивании диска или его остановки, блокировки лотка (для устройств со сменными носителями), выделения буфера ПДП, и т.д.
hardorsoft
25.10.10 в 22:32
|
|
Вы можете спросить, кто же реально открывает блочное устройство? Имеется несколько операций, которые вызывают открытие устройства, непосредственно из пользовательского пространства. Это разбиение диска на разделы, построение файловой системы на разделе, выполнение проверки файловой системы, а так же монтирование раздела. В последнем случае, имеется не пользовательский процесс, который хранит дескриптор открытого устройства для использования его ядром. Драйвер блочного устройства не может отличить операцию mount (которая открывает устройство из пространства ядра) от работы утилиты mkfs (которая работает из пространства пользователя).
hardorsoft
25.10.10 в 22:32
|
|
16.2.2 Поддержка устройств со сменными носителями.
hardorsoft
25.10.10 в 22:32
|
|
Структура block_device_operations содержит два метода для поддержки устройств со сменными носителями. Если ваше устройство не позволяет смену носителя, вы можете опустить реализацию этих методов. Методы относительно просты.
hardorsoft
25.10.10 в 22:32
|
|
Метод media_changed вызывается из функции check_disk_change для указания того, что произошла смена носителя. В этом случае, возвращается не нулевое значение. Реализация для sbull – проста. Происходит проверка значения флага, который устанавливается, если сработал таймер смены носителя:
hardorsoft
25.10.10 в 22:33
|
|
int sbull_media_changed(struct gendisk *gd) { struct sbull_dev *dev = gd->private_data; return dev->media_change; }
hardorsoft
25.10.10 в 22:33
|
|
Метод revalidate вызывается после смены носителя. Его работа заключается в подготовке драйвера к работе с новым носителем. После выполнения revalidate ядро повторяет чтение таблицы разделов и начинает работать с устройством. В sbull этот метод просто сбрасывает флаг media_change, и обнуляет память устройства, симулируя получение пустого диска:
hardorsoft
25.10.10 в 22:33
|
|
int sbull_revalidate(struct gendisk *gd) { struct sbull_dev *dev = gd->private_data; if (dev->media_change) { dev->media_change = 0; memset (dev->data, 0, dev->size); } return 0; }
hardorsoft
25.10.10 в 22:33
|
|
|
|
|
Блочное устройство предоставляет метод ioctl для управления устройством. Код высокого уровня подсистемы блочных устройств перехватывает команды ioctl перед подачей их вашему драйверу (смотрите файл ядра drivers/block/ioctl.c для более полной информации). Фактически, современный драйвер блочного устройства может вовсе не реализовывать очень многие команды ioctl.
hardorsoft
25.10.10 в 22:34
|
|
В драйвере sbull метод ioctl выполняет только одну команду – запрос геометрии устройства:
hardorsoft
25.10.10 в 22:34
|
|
int sbull_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { long size; struct hd_geometry geo; struct sbull_dev *dev = filp->private_data; switch(cmd) { case HDIO_GETGEO: /* получение геометрии: т.к. У нас виртуальное устройство,мы создаем что-то правдоподобное. Мы устанавливаем 16 секторов, 4 головки и вычисляем соответствующее количество цилиндров. Начало данных устанавливаем на 4 сектор */ size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE); geo.cylinders = (size & ~0x3f) >> 6; geo.heads = 4; geo.sectors = 16; geo.start = 4; if (copy_to_user((void __user *) arg, &geo, sizeof(geo))) return -EFAULT; return 0; } return -ENOTTY; /* Не известная команда */ }
hardorsoft
25.10.10 в 22:35
|
|
Задача предоставления геометрии может показаться странной, поскольку наше устройство является виртуальным и ничего не делает с дорожками и цилиндрами. Многие реальные блочные устройства окружаются множеством сложных структур. Ядру не интересна геометрия блочного устройства, оно рассматривается просто как линейный массив секторов. Однако есть пользовательские утилиты, которые по-прежнему запрашивают геометрию диска. Следует обратить внимание на программу fdisk, которая редактирует таблицу разделов. Ей необходима информация о цилиндрах. Без этой информации fdisk не будет работать.
hardorsoft
25.10.10 в 22:35
|
|
Устройство sbull может быть разбито на разделы. Мы предоставляем в ioctl фиктивную геометрию, которая соответствует емкости нашего устройства. Подобное делают многие дисковые драйверы. Обратите внимание, что количество секторов в устройстве преобразуется, учитывая тот факт, что ядро работает только с 512-байтными секторами .
hardorsoft
25.10.10 в 22:35
|
|