(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

"Драйверы устройств в Linux. Часть 15: Диск в оперативной памяти - экспериментируем с драйверами блочных устройств".

Источник: rus-linux

В этой статье, которая является частью серии статей о драйверах устройств в Linux, будут проведены эксперименты с фиктивным жестким диском, расположенным в оперативной памяти, с помощью которого будет продемонстрирована работа драйверов блочных устройств.

После вкусного обеда изучение теории тянет слушателей в сон. Поэтому давайте сразу начнем с кода.

Исходный код диска в оперативной памяти

Давайте создадим директорий DiskOnRAM, в котором будут находиться следующие шесть файлов: три с исходными кодами на языке С, два с заголовочными файлами для С и один файл Makefile.

partition.h

#ifndef PARTITION_H
#define PARTITION_H
 
#include <linux/types.h>
 
extern void copy_mbr_n_br(u8 *disk);
#endif

partition.c

#include <linux/string.h>
 
#include "partition.h"
 
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))
 
#define SECTOR_SIZE 512
#define MBR_SIZE SECTOR_SIZE
#define MBR_DISK_SIGNATURE_OFFSET 440
#define MBR_DISK_SIGNATURE_SIZE 4
#define PARTITION_TABLE_OFFSET 446
#define PARTITION_ENTRY_SIZE 16 // sizeof(PartEntry)
#define PARTITION_TABLE_SIZE 64 // sizeof(PartTable)
#define MBR_SIGNATURE_OFFSET 510
#define MBR_SIGNATURE_SIZE 2
#define MBR_SIGNATURE 0xAA55
#define BR_SIZE SECTOR_SIZE
#define BR_SIGNATURE_OFFSET 510
#define BR_SIGNATURE_SIZE 2
#define BR_SIGNATURE 0xAA55
 
typedef struct
{
    unsigned char boot_type; // 0x00 - Inactive; 0x80 - Active (Bootable)
    unsigned char start_head;
    unsigned char start_sec:6;
    unsigned char start_cyl_hi:2;
    unsigned char start_cyl;
    unsigned char part_type;
    unsigned char end_head;
    unsigned char end_sec:6;
    unsigned char end_cyl_hi:2;
    unsigned char end_cyl;
    unsigned long abs_start_sec;
    unsigned long sec_in_part;
} PartEntry;
 
typedef PartEntry PartTable[4];
 
static PartTable def_part_table =
{
    {
        boot_type: 0x00,
        start_head: 0x00,
        start_sec: 0x2,
        start_cyl: 0x00,
        part_type: 0x83,
        end_head: 0x00,
        end_sec: 0x20,
        end_cyl: 0x09,
        abs_start_sec: 0x00000001,
        sec_in_part: 0x0000013F
    },
    {
        boot_type: 0x00,
        start_head: 0x00,
        start_sec: 0x1,
        start_cyl: 0x0A, // extended partition start cylinder (BR location)
        part_type: 0x05,
        end_head: 0x00,
        end_sec: 0x20,
        end_cyl: 0x13,
        abs_start_sec: 0x00000140,
        sec_in_part: 0x00000140
    },
    {
        boot_type: 0x00,
        start_head: 0x00,
        start_sec: 0x1,
        start_cyl: 0x14,
        part_type: 0x83,
        end_head: 0x00,
        end_sec: 0x20,
        end_cyl: 0x1F,
        abs_start_sec: 0x00000280,
        sec_in_part: 0x00000180
    },
    {
    }
};
static unsigned int def_log_part_br_cyl[] = {0x0A, 0x0E, 0x12};
static const PartTable def_log_part_table[] =
{
    {
        {
            boot_type: 0x00,
            start_head: 0x00,
            start_sec: 0x2,
            start_cyl: 0x0A,
            part_type: 0x83,
            end_head: 0x00,
            end_sec: 0x20,
            end_cyl: 0x0D,
            abs_start_sec: 0x00000001,
            sec_in_part: 0x0000007F
        },
        {
            boot_type: 0x00,
            start_head: 0x00,
            start_sec: 0x1,
            start_cyl: 0x0E,
            part_type: 0x05,
            end_head: 0x00,
            end_sec: 0x20,
            end_cyl: 0x11,
            abs_start_sec: 0x00000080,
            sec_in_part: 0x00000080
        },
    },
    {
        {
            boot_type: 0x00,
            start_head: 0x00,
            start_sec: 0x2,
            start_cyl: 0x0E,
            part_type: 0x83,
            end_head: 0x00,
            end_sec: 0x20,
            end_cyl: 0x11,
            abs_start_sec: 0x00000001,
            sec_in_part: 0x0000007F
        },
        {
            boot_type: 0x00,
            start_head: 0x00,
            start_sec: 0x1,
            start_cyl: 0x12,
            part_type: 0x05,
            end_head: 0x00,
            end_sec: 0x20,
            end_cyl: 0x13,
            abs_start_sec: 0x00000100,
            sec_in_part: 0x00000040
        },
    },
    {
        {
            boot_type: 0x00,
            start_head: 0x00,
            start_sec: 0x2,
            start_cyl: 0x12,
            part_type: 0x83,
            end_head: 0x00,
            end_sec: 0x20,
            end_cyl: 0x13,
            abs_start_sec: 0x00000001,
            sec_in_part: 0x0000003F
        },
    }
};
 
static void copy_mbr(u8 *disk)
{
    memset(disk, 0x0, MBR_SIZE);
    *(unsigned long *)(disk + MBR_DISK_SIGNATURE_OFFSET) = 0x36E5756D;
    memcpy(disk + PARTITION_TABLE_OFFSET, &def_part_table, PARTITION_TABLE_SIZE);
    *(unsigned short *)(disk + MBR_SIGNATURE_OFFSET) = MBR_SIGNATURE;
}
static void copy_br(u8 *disk, int start_cylinder, const PartTable *part_table)
{
    disk += (start_cylinder * 32 /* sectors / cyl */ * SECTOR_SIZE);
    memset(disk, 0x0, BR_SIZE);
    memcpy(disk + PARTITION_TABLE_OFFSET, part_table,
        PARTITION_TABLE_SIZE);
    *(unsigned short *)(disk + BR_SIGNATURE_OFFSET) = BR_SIGNATURE;
}
void copy_mbr_n_br(u8 *disk)
{
    int i;
 
    copy_mbr(disk);
    for (i = 0; i < ARRAY_SIZE(def_log_part_table); i++)
    {
        copy_br(disk, def_log_part_br_cyl[i], &def_log_part_table[i]);
    }
}

ram_device.h

#ifndef RAMDEVICE_H
#define RAMDEVICE_H
 
#define RB_SECTOR_SIZE 512
 
extern int ramdevice_init(void);
extern void ramdevice_cleanup(void);
extern void ramdevice_write(sector_t sector_off, u8 *buffer, unsigned int sectors);
extern void ramdevice_read(sector_t sector_off, u8 *buffer, unsigned int sectors);
#endif

ram_device.c

#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/string.h>
 
#include "ram_device.h"
#include "partition.h"
 
#define RB_DEVICE_SIZE 1024 /* sectors */
/* So, total device size = 1024 * 512 bytes = 512 KiB */
 
/* Array where the disk stores its data */
static u8 *dev_data;
 
int ramdevice_init(void)
{
    dev_data = vmalloc(RB_DEVICE_SIZE * RB_SECTOR_SIZE);
    if (dev_data == NULL)
        return -ENOMEM;
    /* Setup its partition table */
    copy_mbr_n_br(dev_data);
    return RB_DEVICE_SIZE;
}
 
void ramdevice_cleanup(void)
{
    vfree(dev_data);
}
 
void ramdevice_write(sector_t sector_off, u8 *buffer, unsigned int sectors)
{
    memcpy(dev_data + sector_off * RB_SECTOR_SIZE, buffer,
        sectors * RB_SECTOR_SIZE);
}
void ramdevice_read(sector_t sector_off, u8 *buffer, unsigned int sectors)
{
    memcpy(buffer, dev_data + sector_off * RB_SECTOR_SIZE,
        sectors * RB_SECTOR_SIZE);
}

ram_block.c

/* Disk on RAM Driver */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/errno.h>
 
#include "ram_device.h"
 
#define RB_FIRST_MINOR 0
#define RB_MINOR_CNT 16
 
static u_int rb_major = 0;
 
/*
 * The internal structure representation of our Device
 */
static struct rb_device
{
    /* Size is the size of the device (in sectors) */
    unsigned int size;
    /* For exclusive access to our request queue */
    spinlock_t lock;
    /* Our request queue */
    struct request_queue *rb_queue;
    /* This is kernel's representation of an individual disk device */
    struct gendisk *rb_disk;
} rb_dev;
 
static int rb_open(struct block_device *bdev, fmode_t mode)
{
    unsigned unit = iminor(bdev->bd_inode);
 
    printk(KERN_INFO "rb: Device is opened\n");
    printk(KERN_INFO "rb: Inode number is %d\n", unit);
 
    if (unit > RB_MINOR_CNT)
        return -ENODEV;
    return 0;
}
 
static int rb_close(struct gendisk *disk, fmode_t mode)
{
    printk(KERN_INFO "rb: Device is closed\n");
    return 0;
}
 
/*
 * Actual Data transfer
 */
static int rb_transfer(struct request *req)
{
    //struct rb_device *dev = (struct rb_device *)(req->rq_disk->private_data);
 
    int dir = rq_data_dir(req);
    sector_t start_sector = blk_rq_pos(req);
    unsigned int sector_cnt = blk_rq_sectors(req);
 
    struct bio_vec *bv;
    struct req_iterator iter;
 
    sector_t sector_offset;
    unsigned int sectors;
    u8 *buffer;
 
    int ret = 0;
 
    //printk(KERN_DEBUG "rb: Dir:%d; Sec:%lld; Cnt:%d\n", dir, start_sector, sector_cnt);
 
    sector_offset = 0;
    rq_for_each_segment(bv, req, iter)
    {
        buffer = page_address(bv->bv_page) + bv->bv_offset;
        if (bv->bv_len % RB_SECTOR_SIZE != 0)
        {
            printk(KERN_ERR "rb: Should never happen: "
                "bio size (%d) is not a multiple of RB_SECTOR_SIZE (%d).\n"
                "This may lead to data truncation.\n",
                bv->bv_len, RB_SECTOR_SIZE);
            ret = -EIO;
        }
        sectors = bv->bv_len / RB_SECTOR_SIZE;
        printk(KERN_DEBUG "rb: Sector Offset: %lld; Buffer: %p; Length: %d sectors\n",
            sector_offset, buffer, sectors);
        if (dir == WRITE) /* Write to the device */
        {
            ramdevice_write(start_sector + sector_offset, buffer, sectors);
        }
        else /* Read from the device */
        {
            ramdevice_read(start_sector + sector_offset, buffer, sectors);
        }
        sector_offset += sectors;
    }
    if (sector_offset != sector_cnt)
    {
        printk(KERN_ERR "rb: bio info doesn't match with the request info");
        ret = -EIO;
    }
 
    return ret;
}
 
/*
 * Represents a block I/O request for us to execute
 */
static void rb_request(struct request_queue *q)
{
    struct request *req;
    int ret;
 
    /* Gets the current request from the dispatch queue */
    while ((req = blk_fetch_request(q)) != NULL)
    {
#if 0
        /*
         * This function tells us whether we are looking at a filesystem request
         * - one that moves block of data
         */
        if (!blk_fs_request(req))
        {
            printk(KERN_NOTICE "rb: Skip non-fs request\n");
            /* We pass 0 to indicate that we successfully completed the request */
            __blk_end_request_all(req, 0);
            //__blk_end_request(req, 0, blk_rq_bytes(req));
            continue;
        }
#endif
        ret = rb_transfer(req);
        __blk_end_request_all(req, ret);
        //__blk_end_request(req, ret, blk_rq_bytes(req));
    }
}
 
/*
 * These are the file operations that performed on the ram block device
 */
static struct block_device_operations rb_fops =
{
    .owner = THIS_MODULE,
    .open = rb_open,
    .release = rb_close,
};
 
/*
 * This is the registration and initialization section of the ram block device
 * driver
 */
static int __init rb_init(void)
{
    int ret;
 
    /* Set up our RAM Device */
    if ((ret = ramdevice_init()) < 0)
    {
        return ret;
    }
    rb_dev.size = ret;
 
    /* Get Registered */
    rb_major = register_blkdev(rb_major, "rb");
    if (rb_major <= 0)
    {
        printk(KERN_ERR "rb: Unable to get Major Number\n");
        ramdevice_cleanup();
        return -EBUSY;
    }
 
    /* Get a request queue (here queue is created) */
    spin_lock_init(&rb_dev.lock);
    rb_dev.rb_queue = blk_init_queue(rb_request, &rb_dev.lock);
    if (rb_dev.rb_queue == NULL)
    {
        printk(KERN_ERR "rb: blk_init_queue failure\n");
        unregister_blkdev(rb_major, "rb");
        ramdevice_cleanup();
        return -ENOMEM;
    }
 
    /*
     * Add the gendisk structure
     * By using this memory allocation is involved,
     * the minor number we need to pass bcz the device
     * will support this much partitions
     */
    rb_dev.rb_disk = alloc_disk(RB_MINOR_CNT);
    if (!rb_dev.rb_disk)
    {
        printk(KERN_ERR "rb: alloc_disk failure\n");
        blk_cleanup_queue(rb_dev.rb_queue);
        unregister_blkdev(rb_major, "rb");
        ramdevice_cleanup();
        return -ENOMEM;
    }
 
    /* Setting the major number */
    rb_dev.rb_disk->major = rb_major;
    /* Setting the first mior number */
    rb_dev.rb_disk->first_minor = RB_FIRST_MINOR;
    /* Initializing the device operations */
    rb_dev.rb_disk->fops = &rb_fops;
    /* Driver-specific own internal data */
    rb_dev.rb_disk->private_data = &rb_dev;
    rb_dev.rb_disk->queue = rb_dev.rb_queue;
    /*
     * You do not want partition information to show up in
     * cat /proc/partitions set this flags
     */
    //rb_dev.rb_disk->flags = GENHD_FL_SUPPRESS_PARTITION_INFO;
    sprintf(rb_dev.rb_disk->disk_name, "rb");
    /* Setting the capacity of the device in its gendisk structure */
    set_capacity(rb_dev.rb_disk, rb_dev.size);
 
    /* Adding the disk to the system */
    add_disk(rb_dev.rb_disk);
    /* Now the disk is "live" */
    printk(KERN_INFO "rb: Ram Block driver initialised (%d sectors; %d bytes)\n",
        rb_dev.size, rb_dev.size * RB_SECTOR_SIZE);
 
    return 0;
}
/*
 * This is the unregistration and uninitialization section of the ram block
 * device driver
 */
static void __exit rb_cleanup(void)
{
    del_gendisk(rb_dev.rb_disk);
    put_disk(rb_dev.rb_disk);
    blk_cleanup_queue(rb_dev.rb_queue);
    unregister_blkdev(rb_major, "rb");
    ramdevice_cleanup();
}
 
module_init(rb_init);
module_exit(rb_cleanup);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email@sarika-pugs.com>");
MODULE_DESCRIPTION("Ram Block Driver");
MODULE_ALIAS_BLOCKDEV_MAJOR(rb_major);

Вы также можете загрузить отсюда код, используемый для демонстрации.

Как и обычно, с помощью команды make соберем драйвер "диска в оперативной памяти"(dor.ko), объединив вместе три файла на С. Чтобы увидеть, как это делается, смотрите файл Makefile.

Makefile

# If called directly from the command line, invoke the kernel build system.
ifeq ($(KERNELRELEASE),)
 
    KERNEL_SOURCE := /usr/src/linux
    PWD := $(shell pwd)
default: module
 
module:
    $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=$(PWD) modules
 
clean:
    $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=$(PWD) clean
 
# Otherwise KERNELRELEASE is defined; we've been invoked from the
# kernel build system and can use its language.
else
 
    obj-m := dor.o
    dor-y := ram_block.o ram_device.o partition.o
 
endif

Чтобы привести в исходное состояние файлы, используемые при сборке, выполните, как и обычно, команду make clean.

Как только сборка будет завершена, выполните следующие три эксперимента (смотрите рис.1 - 3).

Рис.1: Экспериментируем с драйвером "диска в оперативной памяти"

Рис.2: xxd показывает первоначальные данные, находящиеся в первом разделе (/dev/rb1)

Рис.3: Форматирование третьего раздела (/dev/rb3)

Пожалуйста, обратите внимание, что все эти действия нужно выполнять с привилегиями пользователя root:

  • Загрузите драйвер dor.ko с помощью команды insmod. В результате будут созданы файлы блочного устройства, соответствующие диску размером в 512 Кбайтов, расположенному в оперативной памяти.
  • Проверьте файлы автоматически созданного блочного устройства (/dev/rb*). /dev/rb - это весь диск, размер которого равен 512 Кбайтов. rb1, rb2 и rb3 являются первичными разделами, причем rb2 является расширенным разделом, в котором расположены логические разделы rb5, rb6 и rb7.
  • Прочитайте весь диск с помощью утилиты дампа диска dd.
  • Опять с помощью утилиты dd заполните нулями первый сектор первого раздела (/dev/rb1)
  • С помощью команды cat запишите некоторый текст в первый раздел диска (/dev/rb1).
  • С помощью утилиты xxd отобразите начальное содержимое первого раздела (/dev/rb1).
  • С помощью команды fdisk отобразите информацию о разделах диска. Результат работы команды fdisk показан на рис.3.
  • С помощью mkfs.vfat (рис.3) выполните быстрое форматирование третьего первичного раздела (/dev/rb3) - создайте файловую систему vfat (точно такую, как у нашего флеш устройства).
  • Смонтируйте вновь отформатированный раздел с помощью команды mount, например, в точке монтирования /mnt (рис.3).
  • Утилита df, отображающая использования дисков, теперь должна показать, что этот раздел смонтирован в точке /mnt (рис.3). Вы можете пойти дальше и сохранить на диске данные, но не забудьте, что этот диск находится в оперативной памяти, т. е. после выключения компьютера файлы на нем не сохраняются.
  • После того, как вы размонтируете раздел с помощью команды umount /mnt, выгрузите драйвер с помощью команды rmmod dor.

Теперь давайте изучим правила

Мы всего лишь попробовали воспользоваться диском, созданным в оперативной памяти (disk on RAM - DOR), но при этом фактически не знаем правил, как это все происходит. Так что давайте попытаемся разобраться во всех подробностях этого процесса. В каждом из трех файлов .c представлена определенная часть драйвера; в ram_device.c и ram_device.h абстрагированы основные операции с памятью, такие vmalloc/vfree, memcpy и т. п., с помощью которых реализованы API таких операции, как инициализация/очистка, чтение/запись и т.д.

В partition.c и partition.h реализованы функции, эмулирующие в DOR работу с таблицами различных разделов. С помощью этого кода предоставляется информация о разделах, например, номер раздела, его тип, размер и т. п., которая отображается с помощью функцииfdisk. Файл ram_block.c является основой реализации блочного драйвера, позволяющей отображать DOR в пользовательском пространстве в виде файлов блочного устройства (/dev/rb*). Другими словами, с помощью четырех из пяти файлов ram_device.* и partition.* формируется горизонтальный слой драйвер устройства, а с помощью файла ram_block.c формируется вертикальный (блочный) слой драйвера устройства. Итак, давайте разберемся в деталях.

Основы драйверов блочных устройств

Концептуально, блочные драйверы очень похожи на драйверы символьных устройств, в частности в отношении следующего:

  • Используются файлы устройств
  • Есть старшие и младшие номера
  • Используются операции с файлами устройств
  • Применяется концепция регистрации устройств

Итак, если вы уже знаете, как реализован символьный драйвер, вам будет проще понять реализацию блочных драйверов.

Тем не менее, они, безусловно, не идентичны. Основные различия заключаются в следующем:

  • Абстракция блочно-ориентированных устройств отличается от абстракции байт-ориентированных устройств
  • Чтобы обеспечить оптимальные производительность, блочные драйверы должны вызываться планировщиками ввода/вывода. Сравните это с символьными драйверами, которые должны использоваться внутри виртуальной файловой системы VFS.
  • Чтобы обеспечить эффективный доступ к данным, блочные драйверы проектируются так, что их можно интегрировать с механизмом кэш-буфера Linux. Символьные драйверы являются драйверами непосредственного доступа, которые напрямую получают доступ к аппаратному обеспечению.

И все это является причиной различий в реализации. Давайте проанализируем ключевые фрагменты кода из файла ram_block.c, и начнем с конструктора драйвера rb_init().

Первым шагом будет регистрация 8-битного (блочного) старшего номера (что неявно означает регистрацию всех 256 8-битных младших номеров, связанных с ним). Функция для этого выглядит следующим образом:

int register_blkdev(unsigned int major, const char *name);

Здесь major является старшим номером, который должен регистроваться, а name является регистрационной меткой, отображаемой в директории/proc/devices. Интересно, что если в качестве первого параметра major передается 0, то функция register_blkdev() пытается выделить и зарегистрировать произвольный свободный старший номер; в случае успеха происходит возврат выделенного старшего номера. Соответствующая функция отмены регистрации выглядит следующим образом:

void unregister_blkdev(unsigned int major, const char *name);

Прототипы обеих этих функций находятся в <linux/fs.h>.

На втором шаге в структуру block_device_operations (прототип в <linux/blkdev.h>) заносятся операции для работы с файлами устройств с зарегистрированными старшими номерами.

Но эти операции мало похожи на операции с файлами символьных устройств; совпадения, как правило, незначительны. Если вдаваться в детали, то нет, что удивительно, даже таких операций, как чтение и запись. Но, поскольку, как мы уже знаем, блочный драйвер должен быть интегрирован с планировщиками ввода/вывода, реализация чтения и записи осуществляется с помощью так называемых очередей запросов. Таким образом, кроме операций для работы файлов устройств, также потребуется предоставить следующее:

  • Очередь запросов для запросов на чтение/запись
  • Механизм блокировки запросов для защиты от одновременного доступа
  • Функция запросов для обработки запросов из очереди запросов

Кроме того, нет отдельного интерфейса для создания файлов блочных устройств, так что также следует предоставить:

  • Префикс имени файла устройства, который, как правило, называется именем диска disk_name (rb для драйвера dor)
  • Начальный младший номер для файлов устройств, который, как правило, называется первым младшим номером first_minor.

Наконец, нужно также предоставить два специальных значения, необходимые для характеризации блочных устройств, а именно:

    Максимальное количество разделов для данного блочного устройства, указав для этого количество младших номеров.
  • Размер устройства, указываемый в единицах 512-байтовых секторов, что нужно абстрактного доступа к логическим блокам.

Все эти операции регистрируются в структуре struct gendisk с помощью следующей функции:

void add_disk(struct gendisk *disk);

Соответствующая функция удаления delete выглядит следующим образом:

void del_gendisk(struct gendisk *disk);

Прежде, чем использовать функцию добавления диска add_disk(), нужно либо непосредственно, либо с помощью различных макросов/функций, таких какset_capacity(), инициальзировать различные поля структуры struct gendisk. Как минимум, нужно непосредственно инициализировать следующие поля - major,first_minor, fops, queue, disk_name. И даже перед тем, как эти поля будут инициализированы, нужно будет с помощью следующей функции выделить память под структуру struct gendisk:

struct gendisk *alloc_disk(int minors);

Здесь minors указывает общее количество разделов, поддерживаемых для этого диска. И соответствующая обратная функция будет выглядеть так:

void put_disk(struct gendisk *disk);

Прототипы всех этих функций имеются в <linux/genhd.h>.

Очередь запросов и функция запросов

Перед тем, как использовать функцию add_disk(), нужно также инициализировать очередь запросов и и занести ее в структуру struct gendisk. Инициализация очереди запросов осуществляется с помощью следующей функции:

struct request_queue *blk_init_queue(request_fn_proc *, spinlock_t *);

В качестве параметров мы указываем функцию обработки запросов и инициализируем механизм зашиты от одновременного доступа. Ниже приведена соответствующая функция работы с очередью:

void blk_cleanup_queue(struct request_queue *);

Функция запроса (обработки) должна быть определена с помощью следующего прототипа:

void request_fn(struct request_queue *q);

Она должна кодироваться так, чтобы для запроса использовался параметр q, например, следующим образом:

struct request *blk_fetch_request(struct request_queue *q);

Затем функция должна либо обработать запрос, либо инициализировать обработку. В любом случае блокировок возникать не должно, поскольку функция запроса вызывается не из контекста процесса обработки. Более того, внутри функции запроса должны использоваться только те функции, из-за которых не возникает блокировок в очереди запросов.

Ниже приведен типичный пример обработки запроса, демонстрируемый на примере функции rb_request() из файла ram_block.c:

while ((req = blk_fetch_request(q)) != NULL) /* Fetching a request */
{
        /* Processing the request: the actual data transfer */
        ret = rb_transfer(req); /* Our custom function */
         /* Informing that the request has been processed with return of ret */
        __blk_end_request_all(req, ret);
}

Запросы и их обработка

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

rq_data_dir(req); /* Operation type: 0 - read from device; otherwise - write to device */
blk_req_pos(req); /* Starting sector to process */
blk_req_sectors(req); /* Total sectors to process */
rq_for_each_segment(bv, req, iter) /* Iterator to extract individual buffers */

Макрос rq_for_each_segment() является специализированным, в котором с помощью команды iter происходит обращение к струтуре struct request (req) и при каждой итерации выполняется извлечение конкретных данных из буфера в структуру struct bio_vec (bv: basic input/output vector). А затем, когда на каждой итерации передача данных будет завершена, для выполнения соответствующей передачи данных будет использован, в зависимости от типа операции, один из следующих интерфейсов API из файла ram_device.c:

void ramdevice_write(sector_t sector_off, u8 *buffer, unsigned int sectors);
void ramdevice_read(sector_t sector_off, u8 *buffer, unsigned int sectors);

Код функции rb_transfer() смотрите полностью в файле ram_block.c.

Подведем итог

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

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 28.06.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft Windows Professional 10, Электронный ключ
Microsoft 365 Apps for business (corporate)
Microsoft 365 Business Basic (corporate)
Microsoft 365 Business Standard (corporate)
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год.
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
Вопросы и ответы по MS SQL Server
Мир OLAP и Business Intelligence: новости, статьи, обзоры
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100