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\n");
	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\n");
//	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\n");
	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.\n");
		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.\n");
	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.\n");
}
 
 
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,並嘗試創建一個文件


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM