美文网首页
力卉编程 | 块设备节点的创建步骤

力卉编程 | 块设备节点的创建步骤

作者: 力卉编程 | 来源:发表于2020-04-25 20:18 被阅读0次

创建【块设备】的代码跟创建字节设备类似:

module_init(blk_init);
module_exit(blk_exit);
见:力卉编程 | 简单驱动自动创建设备节点

创建块设备的步骤:

如上,blk_init函数,里面做了这几件事:

1、注册一个块设备register_blkdev

2、申请一个结构,来保存这个块设备信息的(不是每个块设备都必须)

3、再安装这个设备setup_device

3.1 完成块设备大小的计算
3.2 把快设备放入请求队列中(IO调度层把请求排序后放入请求队列中,里面的参数blk_request是一个函数,用于指明使用哪个函数对这个请求进行处理)

  • 3.2.1、使用blk_fetch_request从队列中取出要处理的一个请求
  • 3.2.2、使用blk_transfer实现对对应扇区的硬件操作,比如读和写,应该这里的块设备是内存模拟的,所以使用的是memcpy函数
  • 3.2.3、使用__blk_end_request_cur判断请求队列是否还有请求要处理,如果有则继续处理,没有退出。

3.3 指明设备的扇区大小
3.4 然后用alloc_disk函数分配一个gendisk结构(一个驱动可能对于几个块设备,用gendisk来区分)
3.5紧接着需要对这个结构进行初始化,如下:
3.6 注册这个块设备

参考代码如下:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h>   /* kmalloc() */
#include <linux/fs.h>   /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/timer.h>
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h> /* invalidate_bdev */
#include <linux/bio.h>
 
MODULE_LICENSE("Dual BSD/GPL");
 
static int major = 0;
static int sect_size = 512;
static int nsectors = 1024;
 
/*
* The internal representation of our device.
*/
struct blk_dev 
{
    int size;                        /* Device size in sectors */
    u8 *data;                        /* The data array */
    struct request_queue *queue;     /* The device request queue */
    struct gendisk *gd;              /* The gendisk structure */
};
 
struct blk_dev *dev;
 
/*
* Handle an I/O request, in sectors.
*/
static void blk_transfer(struct blk_dev *dev, unsigned long sector,
    unsigned long nsect, char *buffer, int write)
{
    unsigned long offset = sector * sect_size;
    unsigned long nbytes = nsect * sect_size;
 
 
    if ((offset + nbytes) > dev->size) 
    {
        printk(KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
        return;
    }
    if (write)
        memcpy(dev->data + offset, buffer, nbytes);
    else
        memcpy(buffer, dev->data + offset, nbytes);
}
 
/*
* 读写请求处理函数
*/
static void blk_request(struct request_queue *q)
{
    struct request *req;
 
    //从队列中取出要处理的一个请求
    req = blk_fetch_request(q);
    while (req != NULL) 
    {
        struct blk_dev *dev = req->rq_disk->private_data;
 
        blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));
 
        if (!__blk_end_request_cur(req, 0))
        {
            req = blk_fetch_request(q);
        }
    }
}
 
/*
* Transfer a single BIO.
*/
static int blk_xfer_bio(struct blk_dev *dev, struct bio *bio)
{
    int i;
    struct bio_vec *bvec;
    sector_t sector = bio->bi_sector;
 
    /* Do each segment independently. */
    bio_for_each_segment(bvec, bio, i) 
    {
        char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
        blk_transfer(dev, sector, bio_cur_bytes(bio) >> 9 /* in sectors */,
            buffer, bio_data_dir(bio) == WRITE);
        sector += bio_cur_bytes(bio) >> 9; /* in sectors */
        __bio_kunmap_atomic(bio, KM_USER0);
    }
    
    return 0; /* Always "succeed" */
}
 
/*
* Transfer a full request.
*/
static int blk_xfer_request(struct blk_dev *dev, struct request *req)
{
    struct bio *bio;
    int nsect = 0;
 
    __rq_for_each_bio(bio, req) 
    {
        blk_xfer_bio(dev, bio);
        nsect += bio->bi_size / sect_size;
    }
    
    return nsect;
}
 
/*
* The device operations structure.
*/
static struct block_device_operations blk_ops = 
{
    .owner = THIS_MODULE,
};
 
/*
* Set up our internal device.
*/
static void setup_device()
{
    //计算设备大小
    dev->size = nsectors * sect_size;
    dev->data = vmalloc(dev->size);
    if (dev->data == NULL) 
    {
        printk(KERN_NOTICE "vmalloc failure.\n");
        
        return;
    }
 
    //把块设备放入请求队列中,blk_request用于指明处理这个请求的函数
    dev->queue = blk_init_queue(blk_request, NULL);
    if (dev->queue == NULL)
        goto out_vfree;
 
    //指明设备的扇区大小
    blk_queue_logical_block_size(dev->queue, sect_size);
    dev->queue->queuedata = dev;
 
    //分配gendisk结构
    dev->gd = alloc_disk(1);
    if (!dev->gd) 
    {
        printk(KERN_NOTICE "alloc_disk failure\n");
        goto out_vfree;
    }
 
    /*初始化alloc_disk*/
    dev->gd->major = major;//主设备号
    dev->gd->first_minor = 0;//次设备号
    dev->gd->fops = &blk_ops;//操作函数集
    dev->gd->queue = dev->queue;//请求队列
    dev->gd->private_data = dev;//私有数据
    sprintf(dev->gd->disk_name, "simp_blk%d", 0);//磁盘名字
    set_capacity(dev->gd, nsectors*(sect_size / sect_size));//扇区数
 
    //注册块设备
    add_disk(dev->gd);
    return;
 
out_vfree:
    if (dev->data)
        vfree(dev->data);
}
 
static int __init blk_init(void)
{
    /*
    * 注册块设备,申请主设备号
    */
    major = register_blkdev(major, "blk");
    if (major <= 0) 
    {
        printk(KERN_WARNING "blk: unable to get major number\n");
        return -EBUSY;
    }
    //申请一个描述结构(不是每个块设备都有)
    dev = kmalloc(sizeof(struct blk_dev), GFP_KERNEL);
    if (dev == NULL)
        goto out_unregister;
    //安装这个设备
    setup_device();
 
    return 0;
 
out_unregister:
    unregister_blkdev(major, "sbd");
        return -ENOMEM;
}
 
 
static void blk_exit(void)
{
    if (dev->gd)
    {
        del_gendisk(dev->gd);
        put_disk(dev->gd);
    }
    if (dev->queue)
        blk_cleanup_queue(dev->queue);
    if (dev->data)
        vfree(dev->data);
 
    nregister_blkdev(major, "blk");
    
    kfree(dev);
}
 
module_init(blk_init);
module_exit(blk_exit);

设备节点的做法最好对比看,可以更深入了解各种设备。

整理有更改,便于阅读,代码无变化。

文 | 力卉编程

相关文章

网友评论

      本文标题:力卉编程 | 块设备节点的创建步骤

      本文链接:https://www.haomeiwen.com/subject/zxflwhtx.html