13_1_块设备驱动概念

1. 块设备与字符设备的的不同:
a. 块设备以块为单位进行输入和返回输出,字符设备以字节为单位。
b. 块设备对于I/O请求 有对应的缓冲区,字符设备无须缓冲,可以直接读写。
c. 字符设备只能被顺序读写,块设备可以随机访问。

2. block_device_operation, 类似于char设备的file_operations 结构体,它是对块设备操作的集合。
include/linux/blkdev.h

struct block_device_operations {

    int (*open) (struct block_device *, fmode_t);

    int (*release) (struct gendisk *, fmode_t);  //打开和关闭块设备

    int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); 

    int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); 

    int (*direct_access) (struct block_device *, sector_t,

                        void **, unsigned long *);

    int (*media_changed) (struct gendisk *); //检查设备中内容是否已经改变。

    void (*unlock_native_capacity) (struct gendisk *);

    int (*revalidate_disk) (struct gendisk *); //如果块设备内容改变了,发生响应,以使新内容准备好。

    int (*getgeo)(struct block_device *, struct hd_geometry *); //根据块设备的几何信息,填充一个hd_geomoetry结构体,geometry结构体包含磁头,扇区,柱面等信息。

    /* this callback is with swap_lock and sometimes page table lock held */

    void (*swap_slot_free_notify) (struct block_device *, unsigned long);

    struct module *owner; //指向拥有该结构体的模块,通常被初始化为“THIS_MODULE” 
};


  1. 3. gendisk 结构体。用来表示一个独立的磁盘设备或分区。
    struct gendisk {
    
        /* major, first_minor and minors are input parameters only,
    
         * don't use directly. Use disk_devt() and disk_max_parts().
    
         */
    
        int major;            /* major number of driver */
    
        int first_minor;      //第一个次设备号
    
        int minors; /* maximum number of minors, =1 for
    
                                             * disks that can't be partitioned. */
    
    
    
        char disk_name[DISK_NAME_LEN];    /* name of major driver */ //设备名称
    
        char *(*devnode)(struct gendisk *gd, mode_t *mode);
    
        /* Array of pointers to partitions indexed by partno.
    
         * Protected with matching bdev lock but stat and other
    
         * non-critical accesses use RCU. Always access through
    
         * helpers.
    
         */
    
        struct disk_part_tbl __rcu *part_tbl;
    
        struct hd_struct part0;
    
    
    
        const struct block_device_operations *fops;  //块设备操作集合
    
        struct request_queue *queue;    //内核管理这个设备的I/O请求队列的指针
    
        void *private_data;         //可用于指向磁盘的任何私有数据,用法与字符设备驱动file结构体的private_data类似
    
    
    
        int flags;
    
        struct device *driverfs_dev; // FIXME: remove
    
        struct kobject *slave_dir;   //可被引用计数的结构体
    
    
    
        struct timer_rand_state *random;
    
    
    
        atomic_t sync_io;        /* RAID */
    
        struct work_struct async_notify;
    
    #ifdef CONFIG_BLK_DEV_INTEGRITY
    
        struct blk_integrity *integrity;
    
    #endif
    
        int node_id;
    
    };
    


  1. linux内核提供了一组函数来操作gendisk。

1. 分配gendisk.

struct gendisk *alloc_disk(int minors);

gendisk结构体是一个动态分配的结构体,需要特别的内核操作来初始化,驱动不能自己分配这个结构体。

struct gendisk *alloc_disk(int minors)    //minor就是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能修改。

{

    return alloc_disk_node(minors, -1);  //实际调用了这个

}

EXPORT_SYMBOL(alloc_disk);



struct gendisk *alloc_disk_node(int minors, int node_id)

{

    struct gendisk *disk;



    disk = kmalloc_node(sizeof(struct gendisk),

                GFP_KERNEL | __GFP_ZERO, node_id);

    if (disk) {

        if (!init_part_stats(&disk->part0)) {

            kfree(disk);

            return NULL;

        }

        disk->node_id = node_id;

        if (disk_expand_part_tbl(disk, 0)) {

            free_part_stats(&disk->part0);

            kfree(disk);

            return NULL;

        }

        disk->part_tbl->part[0] = &disk->part0;



        disk->minors = minors;

        rand_initialize_disk(disk);

        disk_to_dev(disk)->class = &block_class;

        disk_to_dev(disk)->type = &disk_type;

        device_initialize(disk_to_dev(disk));

        INIT_WORK(&disk->async_notify,

            media_change_notify_thread);

    }

    return disk;

}


  1. 2.增加gendisk

    void add_disk(struct gendisk *gd);

gendisk结构体被分配以后,系统还不能使用这个磁盘,需要调用add_disk()来注册这个磁盘设备。add_disk()的调用必须发生在驱动程序初始化工作完成,并能响应磁盘的请求之后。

/**

 * add_disk - add partitioning information to kernel list

 * @disk: per-device partitioning information

 *

 * This function registers the partitioning information in @disk

 * with the kernel.

 *

 * FIXME: error handling

 */

void add_disk(struct gendisk *disk)

{

    struct backing_dev_info *bdi;

    dev_t devt;

    int retval;



    /* minors == 0 indicates to use ext devt from part0 and should

     * be accompanied with EXT_DEVT flag. Make sure all

     * parameters make sense.

     */

    WARN_ON(disk->minors && !(disk->major || disk->first_minor));

    WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT));



    disk->flags |= GENHD_FL_UP;



    retval = blk_alloc_devt(&disk->part0, &devt);

    if (retval) {

        WARN_ON(1);

        return;

    }

    disk_to_dev(disk)->devt = devt;



    /* ->major and ->first_minor aren't supposed to be

     * dereferenced from here on, but set them just in case.

     */

    disk->major = MAJOR(devt);

    disk->first_minor = MINOR(devt);



    /* Register BDI before referencing it from bdev */ 

    bdi = &disk->queue->backing_dev_info;

    bdi_register_dev(bdi, disk_devt(disk));



    blk_register_region(disk_devt(disk), disk->minors, NULL,

             exact_match, exact_lock, disk);

    register_disk(disk);

    blk_register_queue(disk);



    retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,

                 "bdi");

    WARN_ON(retval);

}



EXPORT_SYMBOL(add_disk);

EXPORT_SYMBOL(del_gendisk);    /* in partitions/check.c */


  1. 3.释放gendisk
    void del_gendisk(struct gendisk *gd);
    
    void del_gendisk(struct gendisk *disk)
    
    {
    
        struct disk_part_iter piter;
    
        struct hd_struct *part;
    
    
    
        /* invalidate stuff */
    
        disk_part_iter_init(&piter, disk,
    
                 DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE);
    
        while ((part = disk_part_iter_next(&piter))) {
    
            invalidate_partition(disk, part->partno);
    
            delete_partition(disk, part->partno);
    
        }
    
        disk_part_iter_exit(&piter);
    
    
    
        invalidate_partition(disk, 0);
    
        blk_free_devt(disk_to_dev(disk)->devt);
    
        set_capacity(disk, 0);
    
        disk->flags &= ~GENHD_FL_UP;
    
        unlink_gendisk(disk);
    
        part_stat_set_all(&disk->part0, 0);
    
        disk->part0.stamp = 0;
    
    
    
        kobject_put(disk->part0.holder_dir);
    
        kobject_put(disk->slave_dir);
    
        disk->driverfs_dev = NULL;
    
        if (!sysfs_deprecated)
    
            sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk)));
    
        device_del(disk_to_dev(disk));
    
    }
    

4. gendisk的引用计数。

gendisk中的kobject成员,它是一个可被引用计数的结构体,通过get_disk()和put_disk()函数可用来操作引用计数。

  1. struct kobject *get_disk(struct gendisk *disk)
    {
        struct module *owner;
    
        struct kobject *kobj;
    
        if (!disk->fops)
    
            return NULL;
    
        owner = disk->fops->owner;
    
        if (owner && !try_module_get(owner))
    
            return NULL;
    
        kobj = kobject_get(&disk_to_dev(disk)->kobj);
    
        if (kobj == NULL) {
    
            module_put(owner);
    
            return NULL;
    
        }
    
        return kobj;
    }
    
    EXPORT_SYMBOL(get_disk);
    void put_disk(struct gendisk *disk)
    {
        if (disk)
    
            kobject_put(&disk_to_dev(disk)->kobj);
    }
    EXPORT_SYMBOL(put_disk);
    


  2. 5. 设置gendisk容量。

void set_capacity(struct gendisk *disk, sector_t size);

块设备中最小的可寻址单元是扇区,扇区大小一般是2的整数倍,最常见的大小是512字节.

扇区的大小是设备的物理属性,扇区是所有块设备的基本单元,块设备无法对比扇区还小的单元进行寻址和操作。

内核与块设备驱动交互的扇区都以512字节为单位,setc_capacity也是。

  1. static inline sector_t get_capacity(struct gendisk *disk)
    
    {
    
        return disk->part0.nr_sects;
    
    }
    
    static inline void set_capacity(struct gendisk *disk, sector_t size)
    
    {
    
        disk->part0.nr_sects = size;
    
    }
    

     
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值