zoukankan      html  css  js  c++  java
  • Linux操作系统编程 实验五 块设备实验

    实验目的

    1、了解Linux块设备管理机制
    2、学习块设备的基本管理
    3、编写一个简单的块设备驱动程序sbull,实现一套内存中的虚拟磁盘驱动器
    4、通过操作验证块设备驱动器
    5、实验内容:

    编写一个简单的块设备驱动程序:

    • 该块设备包括sbull_open()、sbull_ioctl()和sbull_release()等基本操作。
    • 对每个驱动器,sbull分配一个内存数组,然后使这个数组可通过块操作来访问。
    • sbull驱动可通过在该驱动器上进行分区、建立文件系统、以及加载到系统层级中来测试。
    • sbull设备被定义为一个可移出的设备。

    通过实际操作验证块设备驱动

    实验记录

    我的虚拟机版本CenOS 7.8 x64,内核版本3.10.0-1127.el7.x86_64。

    切换到root权限,随后编写sbull.c和Makefile

    sbull.c

    点击查看详细内容
    #include <linux/init.h>//module_init/exit
    #include <linux/module.h>//MODULE_AUTHOR,MODULE_LICENSE等
    #include <linux/genhd.h>//alloc_disk
    #include <linux/blkdev.h>//blk_init_queue
    #include <linux/fs.h>//register_blkdev,unregister_blkdev
    #include <linux/types.h>//u_char,u_short
    #include <linux/vmalloc.h>
    #include <linux/hdreg.h>
    #include <linux/bio.h>
     
    #include <linux/moduleparam.h>
    #include <linux/major.h>
     
     
    #include <linux/highmem.h>   //kmap  kunmap
    #include <linux/mutex.h>
     
    #include <linux/slab.h>
     
    #include <asm/uaccess.h>
    #define RAMBLK_SIZE (1024*1024*2)//分配的内存2MB大小空间
     
     
    /*
    bio代表一个io请求,里面有io请求的所有信息
    request是bio提交给io调度器产生的数据,一个request放着顺序排列的bio
    request_queue代表着一个物理设备,顺序的放着request
    */
    static struct gendisk * ramblk_disk = NULL;/*gendisk表示一个独立的磁盘设备,内核还可以用它来表示分区*/
    static struct request_queue * ramblk_request_queue = NULL;
    static int major = 0;//块设备的主设备号
    static DEFINE_SPINLOCK(ramblk_spinlock);//定义并初始化一个自旋锁
    static char * ramblk_buf = NULL;//申请的内存起始地址
    /*
    上面定义地址是用到char *,是十分有用的。类似于list中container_of一样
    */
     
    int ramblk_getgeo(struct block_device * blk_Dev, struct hd_geometry * hg)
    {
    	printk("ramblk_getgeo
    ");
    	hg->cylinders = 64;
    	hg->heads = 8;
    	hg->sectors = (RAMBLK_SIZE/8/64/512);
    	return 0;
    }
     
     
    /*
    如果说file_operation结构是连接虚拟的VFS文件的操作与具体文件系统的文件操作之间的枢纽,那么block_device_operations就是连接抽象的块设备操作与具体块设备操作之间的枢纽。
    */
    static const struct block_device_operations ramblk_fops = {
    	.owner	= THIS_MODULE,
    	.getgeo = ramblk_getgeo,
    };
     
    static void ramblk_make_request(struct request_queue *q, struct bio *bio)
    {
    	printk("do_ramblk_request
    ");
    //	struct block_device *bdev = bio->bi_bdev;
    	int rw;
    	struct bio_vec *bvec;
    	bvec = bio->bi_io_vec;
    //	sector_t sector;
    	int i;
    	//int err = -EIO;
    	//struct request *req;
    	void *disk_mem;
    	void *bvec_mem;
    	
    	if((bio->bi_sector << 9) + bio->bi_size > RAMBLK_SIZE)
    		return -EIO;
    	disk_mem = ramblk_buf + (bio->bi_sector << 9);
    //	sector = bio->bi_sector;
    	
    //	if(bio_end_sector(bio) > get_capacity(bdev->db_disk))
    //		goto out;
    	
    	rw = bio_rw(bio);
    	if(rw == READA)
    		rw = READ;
    	/*bio中的每个段是由一个bio_vec数据结构描述的*/
    	/*
    	bio_vec结构体中的字段
    	struct page* bv_page 指向段的页框中页描述符的指针
    	unsigned int bv_len 段的字节长度
    	unsigned int bv_offset 页框中段数据的偏移量
    	*/
    	
    	
    //	 bvec_mem = kmap_atomic(bvec->bv_page) + bvec->bv_offset;
    	/*高端内存映射
    	允许睡眠:kmap(永久映射)
    	不允许睡眠:kmap_atomic(临时映射)会覆盖以前到映射
    	*/
    	/*因bio_vec中的内存地址是使用page*描述的,故在高端内存中需要使用kmap进行映射才能访问,再加上
    	在bio_vec中的偏移量,才是高端地址内存中的实际位置*/
    	bvec_mem = kmap(bvec->bv_page) + bvec->bv_offset;  
    	/*bio_for_each_segment宏定义bio.h
    	#define bio_for_each_segment(bvl, bio, i) for(i=0; bvl = bio_iovec_idx((bio),(i)), i< (bio)->bi_vcnt; i++)
    	bio_iovec_idx宏定义bio.h
    	#define bio_iovec_idx(bio, idx) (&((bio)->bi_io_vec[(idx)]))
    	*/
    	bio_for_each_segment(bvec, bio, i)
    	{
            /*判断bio请求处理的方向*/
            switch(rw)
            {
                case READ:
                    memcpy(bvec_mem, disk_mem, bvec-> bv_len);
                    break;
     
                case WRITE : 
                    memcpy(disk_mem, bvec_mem, bvec-> bv_len);
                    break;
                default : 
              //      kunmap_atomic(bvec->bv_page);
    				kunmap(bvec->bv_page);
            }
    		kunmap(bvec->bv_page);
    		disk_mem += bvec->bv_len;
    	}
    	bio_endio(bio, 0);//bio中所有的bio_vec处理完后报告处理结束
    }
     
     
    static int ramblk_init(void)
    {
    	printk("ramblk_init
    ");
    	struct gendisk *disk;
    //	1.分配gendisk结构体,使用alloc_disk函数
    /*
    	gendisk结构是一个动态分配的结构, 它需要一些内核的特殊处理来进行初始化,驱动程序不能自己动态分配该架构
    	而使用struct gendisk *alloc_disk(int mimors) 参数minors是该磁盘使用的次设备号的数目
    */
    	
    //	2.设置
    //	2.1 分配/设置队列,提供读写能力.使用函数blk_init_queue(request_fn_proc *rfn,spin_lock_t *lock)
    //	ramblk_request_queue = blk_init_queue(ramblk_make_request,&ramblk_spinlock);
     
    	major = register_blkdev(0,"sbull");//注册主设备
    	if(major < 0){//检查是否成功分配一个有效的主设备号
    		printk(KERN_ALERT "register_blkdev error.
    ");
    		return -1;
    	}
    	/*使用制造请求的方式,先分配queue*/
    	ramblk_request_queue = blk_alloc_queue(GFP_KERNEL);
    	/*在绑定请求制造函数*/
    	blk_queue_make_request(ramblk_request_queue, ramblk_make_request);
    	disk = ramblk_disk = alloc_disk(16);//minors=分区+1 
    	
    //	2.2 设置disk的其他信息,比如容量、主设备号等
    	
    	
    	//设置主设备号
    	ramblk_disk->major = major;
    	ramblk_disk->first_minor = 0;//设置第一个次设备号
    	ramblk_disk->minors=1;//设置最大的次设备号,=1表示磁盘不能被分区
    	sprintf(ramblk_disk->disk_name, "sbull%c", 'a');//设置设备名
    	ramblk_disk->fops = &ramblk_fops;//设置fops  设置前面表述的各种设备操作
    	ramblk_disk->queue = ramblk_request_queue;//设置请求队列
    	set_capacity(ramblk_disk, RAMBLK_SIZE/512);//设置容量
    	
    //	3.硬件相关的操作
    	ramblk_buf = (char*)vmalloc(RAMBLK_SIZE);//申请RAMBLK_SIZE内存
    	
    //	4.注册
    	add_disk(ramblk_disk);//add partitioning information to kernel list
    	printk("ramblk_init.
    ");
    	return 0;
    }
     
    static void ramblk_exit(void)
    {
    	del_gendisk(ramblk_disk);
    	put_disk(ramblk_disk);
    	unregister_blkdev(major,"sbull");//注销设备驱动
    	blk_cleanup_queue(ramblk_request_queue);//清除队列
    	
    	vfree(ramblk_buf);//释放申请的内存
    	printk("ramblk_exit.
    ");
    }
     
     
    module_init(ramblk_init);//入口
    module_exit(ramblk_exit);//出口
     
    MODULE_AUTHOR("hustcs");
    MODULE_LICENSE("Dual BSD/GPL");
    

    Makefile

    点击查看详细内容
    ifneq ($(KERNELRELEASE),)
    obj-m += sbull.o
    else
    PWD := $(shell pwd)
    KVER ?= $(shell uname -r)
    KDIR := /usr/src/kernels/$(KVER)
    all:
    	@$(MAKE) -C $(KDIR) M=$(PWD)
    clean:
    	@rm -rf .*.cmd *.o *.mod.c *.ko *.symvers *.ko.unsigned *.order
    endif
    
    

    实验过程

    执行make命令

    安装内核模块sbull.ko,然后在已安装的模块中查找sbull

    使用指令dmesg,查看内核输出信息

    查看模块信息

    获取设备列表

    查看sbulla文件夹

    查看sbull设备信息

    格式化sbull设备

    创建挂载点并挂载该设备

    进入目录/mnt/sbull,并尝试创建一个文件

  • 相关阅读:
    2021,6,10 xjzx 模拟考试
    平衡树(二)——Treap
    AtCoder Beginner Contest 204 A-E简要题解
    POJ 2311 Cutting Game 题解
    Codeforces 990G GCD Counting 题解
    NOI2021 SDPTT D2T1 我已经完全理解了 DFS 序线段树 题解
    第三届山东省青少年创意编程与智能设计大赛总结
    Luogu P6042 「ACOI2020」学园祭 题解
    联合省选2021 游记
    Codeforces 1498E Two Houses 题解 —— 如何用结论吊打标算
  • 原文地址:https://www.cnblogs.com/ast935478677/p/14167584.html
Copyright © 2011-2022 走看看