一、開(kāi)發(fā)環(huán)境
主 ?機(jī):VMWare--Fedora 9
開(kāi)發(fā)板:Mini2440--64MB Nand, Kernel:2.6.30.4
二、塊設(shè)備基本概念
扇區(qū)(Sectors):任何塊設(shè)備硬件對(duì)數(shù)據(jù)處理的基本單位。通常,1個(gè)扇區(qū)的大小為512byte。
塊(Blocks):由Linux制定對(duì)內(nèi)核或文件系統(tǒng)等數(shù)據(jù)處理的基本單位。通常,1個(gè)塊由1個(gè)或多個(gè)扇區(qū)組成。
段(Segments):由若干個(gè)相鄰的塊組成。是Linux內(nèi)存管理機(jī)制中一個(gè)內(nèi)存頁(yè)或者內(nèi)存頁(yè)的一部分。
頁(yè)、段、塊、扇區(qū)之間的關(guān)系圖如下:
綜合上描述:塊設(shè)備驅(qū)動(dòng)是基于扇區(qū)(sector)來(lái)訪(fǎng)問(wèn)底層物理磁盤(pán),基于塊(block)來(lái)訪(fǎng)問(wèn)上層文件系統(tǒng)。扇區(qū)一般是2的n次方大小,典型為512B,內(nèi)核也要求塊是2的n次方大小,且塊大小通常為扇區(qū)大小的整數(shù)倍,并且塊大小要小于頁(yè)面大小,典型大小為512B、1K或4K。?
三、塊設(shè)備在Linux中的結(jié)構(gòu)
首先我們來(lái)看一下,塊設(shè)備在整個(gè)Linux中應(yīng)用的總體結(jié)構(gòu),如圖:
塊設(shè)備驅(qū)動(dòng)層(Block Device Driver)在總體結(jié)構(gòu)中扮演的角色。
? 從上圖可以看出,塊設(shè)備的應(yīng)用在Linux中是一個(gè)完整的子系統(tǒng)。
? 首先,我們先看一下,塊設(shè)備驅(qū)動(dòng)是以何種方式對(duì)塊設(shè)備進(jìn)行訪(fǎng)問(wèn)的。在Linux中,驅(qū)動(dòng)對(duì)塊設(shè)備的輸入或輸出(I/O)操作,都會(huì)向塊設(shè)備發(fā)出一個(gè)請(qǐng)求,在驅(qū)動(dòng)中用request結(jié)構(gòu)體描述。但對(duì)于一些磁盤(pán)設(shè)備而言請(qǐng)求的速度很慢,這時(shí)候內(nèi)核就提供一種隊(duì)列的機(jī)制把這些I/O請(qǐng)求添加到隊(duì)列中(即:請(qǐng)求隊(duì)列),在驅(qū)動(dòng)中用request_queue結(jié)構(gòu)體描述。在向塊設(shè)備提交這些請(qǐng)求前內(nèi)核會(huì)先執(zhí)行請(qǐng)求的合并和排序預(yù)操作,以提高訪(fǎng)問(wèn)的效率,然后再由內(nèi)核中的I/O調(diào)度程序子系統(tǒng)(即:上圖中的I/O調(diào)度層)來(lái)負(fù)責(zé)提交I/O請(qǐng)求,I/O調(diào)度程序?qū)⒋疟P(pán)資源分配給系統(tǒng)中所有掛起的塊I/O請(qǐng)求,其工作是管理塊設(shè)備的請(qǐng)求隊(duì)列,決定隊(duì)列中的請(qǐng)求的排列順序以及什么時(shí)候派發(fā)請(qǐng)求到設(shè)備,關(guān)于更多詳細(xì)的I/O調(diào)度知識(shí)這里就不深加研究了。
? 其次,塊設(shè)備驅(qū)動(dòng)又是怎樣維持一個(gè)I/O請(qǐng)求在上層文件系統(tǒng)與底層物理磁盤(pán)之間的關(guān)系呢?這就是上圖中通用塊層(Generic?Block?Layer)要做的事情了。在通用塊層中,通常用一個(gè)bio結(jié)構(gòu)體來(lái)對(duì)應(yīng)一個(gè)I/O請(qǐng)求,它代表了正在活動(dòng)的以段(Segment)鏈表形式組織的塊IO操作,對(duì)于它所需要的所有段又用bio_vec結(jié)構(gòu)體表示。
? 再次,塊設(shè)備驅(qū)動(dòng)又是怎樣對(duì)底層物理磁盤(pán)進(jìn)行反問(wèn)的呢?上面講的都是對(duì)上層的訪(fǎng)問(wèn)對(duì)上層的關(guān)系。Linux提供了一個(gè)gendisk數(shù)據(jù)結(jié)構(gòu)體,用他來(lái)表示一個(gè)獨(dú)立的磁盤(pán)設(shè)備或分區(qū)。在gendisk中有一個(gè)類(lèi)似字符設(shè)備中file_operations的硬件操作結(jié)構(gòu)指針,他就是block_device_operations結(jié)構(gòu)體,他的作用相信大家已經(jīng)很清楚了。
?
具體描述上面中講到的維持各層關(guān)系的數(shù)據(jù)結(jié)構(gòu)體(這里只列出了較常用的一些成員)
request與request_queue結(jié)構(gòu)體,定義在/include/linux/blkdev.h中:
struct request
{
????struct list_head queuelist;?????/*鏈表結(jié)構(gòu)*/
????struct request_queue *q;????????/*請(qǐng)求隊(duì)列*/
????sector_t sector;????????????????/*要傳送的下一個(gè)扇區(qū)*/
????sector_t hard_sector;???????????/*要完成的下一個(gè)扇區(qū)*/
????unsigned long nr_sectors;???????/*要傳送的扇區(qū)數(shù)目*/
????unsigned long hard_nr_sectors;??/*要完成的扇區(qū)數(shù)目*/
????unsigned int current_nr_sectors;/*當(dāng)前要傳送的扇區(qū)數(shù)目*/
????unsigned int hard_cur_sectors;??/*當(dāng)前要完成的扇區(qū)數(shù)目*/
????struct bio *bio;????????????????/*請(qǐng)求的bio結(jié)構(gòu)體的鏈表*/
????struct bio *biotail;????????????/*請(qǐng)求的bio結(jié)構(gòu)體的鏈表尾*/
????void *elevator_private;
????void *elevator_private2;
????struct gendisk *rq_disk;
????unsigned long start_time;
????unsigned short nr_phys_segments;/*請(qǐng)求在物理內(nèi)存中占據(jù)不連續(xù)段的數(shù)目*/
????unsigned short ioprio;
????char *buffer;???????????????????/*傳送的緩沖區(qū)*/
????int tag;
????int errors;
????int ref_count;??????????????????/*引用計(jì)數(shù)*/
????.
????.
????.
};
struct request_queue
{
????.
????.
????.
????struct list_head queue_head;
????unsigned long nr_requests;?????? /*最大的請(qǐng)求數(shù)目*/
????unsigned int nr_congestion_on;
????unsigned int nr_congestion_off;
????unsigned int nr_batching;
????unsigned int max_sectors;??????? /*最大的扇區(qū)數(shù)目*/
????unsigned int max_hw_sectors;
????unsigned short max_phys_segments;/*最大的段數(shù)目*/
????unsigned short max_hw_segments;
????unsigned short hardsect_size;????/*扇區(qū)尺寸大小*/
????unsigned int max_segment_size;?? /*最大的段尺寸大小*/
????unsigned long seg_boundary_mask; /*段邊界掩碼*/
????void *dma_drain_buffer;
????unsigned int dma_drain_size;
????unsigned int dma_pad_mask;
????unsigned int dma_alignment;????? /*DMA傳輸內(nèi)存對(duì)齊*/
????struct blk_queue_tag *queue_tags;
????struct list_head tag_busy_list;
????unsigned int nr_sorted;
????unsigned int in_flight;
????unsigned int rq_timeout;
????struct timer_list timeout;
????struct list_head timeout_list;
????.
????.
????.
};
bio與bio_vec結(jié)構(gòu)體,定義在/include/linux/bio.h中:
struct bio
{
????sector_t bi_sector;??????????? /*要傳送的第一個(gè)扇區(qū)*/
????struct bio *bi_next;?????????? /*下一個(gè)bio*/
????struct block_device?*bi_bdev;
????unsigned long bi_flags;??????? /*狀態(tài)、命令等*/
????unsigned long bi_rw;?????????? /*低位表示READ/WRITE,高位表示優(yōu)先級(jí)*/
????unsigned short bi_vcnt;??????? /*bio_vec的數(shù)量*/
????unsigned short bi_idx;???????? /*當(dāng)前bvl_vec的索引*/
????unsigned int bi_phys_segments; /*不相鄰物理段的數(shù)目*/
????unsigned int bi_size;????????? /*以字節(jié)為單位所需傳送的數(shù)據(jù)大小*/
????unsigned int bi_seg_front_size;/*為了明確硬件尺寸,需要考慮bio中第一個(gè)和最后一個(gè)虛擬的可合并段的尺寸大小*/
????unsigned int bi_seg_back_size;
????unsigned int bi_max_vecs;????? /*支持最大bvl_vecs的數(shù)量*/
????struct bio_vec *bi_io_vec;???? /*vec列表*/
????.
????.
????.
};
struct bio_vec
{
????struct page?*bv_page;???/*頁(yè)指針*/
????unsigned int bv_len;????/*傳輸?shù)淖止?jié)數(shù)*/
????unsigned int bv_offset;?/*偏移位置*/
};
上面的這些結(jié)構(gòu)體都是對(duì)上層的支持,那么對(duì)硬件底層的支持比較重要的結(jié)構(gòu)體是gendisk,定義在/include/linux/genhd.h中:
struct gendisk
{
????int major;????????????/*主設(shè)備號(hào)*/
????int first_minor;????? /*第一個(gè)次設(shè)備號(hào)*/
????int minors;?????????? /*最大的次設(shè)備號(hào),如果不能分區(qū)則為1*/
????char disk_name[DISK_NAME_LEN];? /*設(shè)備名稱(chēng)*/
????struct disk_part_tbl *part_tbl; /*磁盤(pán)上的分區(qū)表信息*/
????struct hd_struct part0;
????struct block_device_operations *fops;/*塊設(shè)備對(duì)底層硬件的操作結(jié)構(gòu)體指針*/
????struct request_queue *queue; /*請(qǐng)求隊(duì)列*/
????void *private_data;????????? /*私有數(shù)據(jù)*/
????.
????.
????.
};
那么這些結(jié)構(gòu)體之間的關(guān)系圖如下:
塊設(shè)備驅(qū)動(dòng)的I/O請(qǐng)求處理的兩種方式:
塊設(shè)備驅(qū)動(dòng)的I/O請(qǐng)求處理有兩種方式,分別是使用請(qǐng)求隊(duì)列和不使用請(qǐng)求隊(duì)列。那么這兩種方式有什么不同呢?在第2點(diǎn)中已講到使用請(qǐng)求隊(duì)列有助于提高系統(tǒng)的性能,但對(duì)于一些完全可隨機(jī)訪(fǎng)問(wèn)的塊設(shè)備(如:Ram盤(pán)等)使用請(qǐng)求隊(duì)列并不能獲得多大的益處,這時(shí)候,通用塊層提供了一種無(wú)隊(duì)列的操作模式,使用這種模式,驅(qū)動(dòng)必須提供一個(gè)制造請(qǐng)求函數(shù)。我們還是用代碼來(lái)區(qū)別它們吧。
使用請(qǐng)求隊(duì)列:
static int __int ramdisk_init(void)
{
????/*塊設(shè)備驅(qū)動(dòng)注冊(cè)*/
????register_blkdev(RAMDISK_MAJOR, RAMDISK_NAME);
????/*使用請(qǐng)求隊(duì)列的方式*/
????ramdisk_queue = blk_init_queue(ramdisk_do_request, NULL);
????/*分配gendisk*/
.........
????/*初始化gendisk*/
.........
????/*添加gendisk到系統(tǒng)中*/
.........
}
/*請(qǐng)求處理函數(shù),請(qǐng)求隊(duì)列的處理流程如下:
?*首先:從請(qǐng)求隊(duì)列中拿出一條請(qǐng)求
?*其次:判斷這一條請(qǐng)求的方向,是向設(shè)備寫(xiě)還是讀,然后將數(shù)據(jù)裝入緩沖區(qū)
?*最后:通知請(qǐng)求完成*/
static void ramdisk_do_request(struct request_queue_t *queue)
{
????struct request *req;
????/*使用循環(huán)一條請(qǐng)求一條請(qǐng)求的來(lái)處理,elv_next_request函數(shù)是遍歷隊(duì)列中的每一條請(qǐng)求*/
????while(req = elv_next_request(queue) != NULL)
????{
????????/*判斷要傳輸數(shù)據(jù)的總長(zhǎng)度大小是否超過(guò)范圍*/
????????if ((req->sector + req->current_nr_sectors) << 9 > RAMDISK_SIZE)
????????{
????????????/*如果超過(guò)范圍就直接報(bào)告請(qǐng)求失敗*/
????????????end_request(req, 0);
????????????continue;
????????}
????????/*判斷請(qǐng)求處理的方向*/
????????switch (rq_data_dir(req))
????????{
????????????case READ:
????????????????memcpy(req->buffer, disk_data + (req->sector << 9), req->current_nr_sectors << 9);
????????????????end_request(req, 1);/*報(bào)告請(qǐng)求處理成功*/
????????????????break;
????????????case WRITE:
????????????????memcpy(disk_data + (req->sector << 9), req->buffer, req->current_nr_sectors << 9);
????????????????end_request(req, 1);/*報(bào)告請(qǐng)求處理成功*/
????????????????break;
????????????default:
????????????????break;
????????}
????}
}
不使用請(qǐng)求隊(duì)列,制造請(qǐng)求函數(shù):
static int __int ramdisk_init(void)
{
????/*塊設(shè)備驅(qū)動(dòng)注冊(cè)*/
????ramdisk_major = register_blkdev(RAMDISK_MAJOR, RAMDISK_NAME);
????/*使用制造請(qǐng)求的方式,先分配ramdisk_queue*/
????ramdisk_queue = blk_alloc_queue(GFP_KERNEL);
??? /*再綁定請(qǐng)求制造函數(shù)*/
????blk_queue_make_request(ramdisk_queue, &ramdisk_make_request);
????/*分配gendisk*/
????.........
????/*初始化gendisk*/
????.........
????/*添加gendisk到系統(tǒng)中*/
????.........
}
/*綁定請(qǐng)求制造函數(shù)。注意:第一個(gè)參數(shù)仍然是請(qǐng)求隊(duì)列,但在這里實(shí)際不包含任何請(qǐng)求。
所以這里要處理的重點(diǎn)對(duì)象的bio中的每個(gè)bio_vec,他表示一個(gè)或多個(gè)要傳送的緩沖區(qū)。*/
static int ramdisk_make_request(struct request_queue_t *queue, struct bio *bio)
{
????int i;
????struct bio_vec *bvec;
????void *disk_mem;
????void *bvec_mem;
????/*在遍歷段之前先判斷要傳輸數(shù)據(jù)的總長(zhǎng)度大小是否超過(guò)范圍*/
????if((bio->bi_sector << 9) + bio->bi_size > RAMDISK_SIZE)
????{
????????/*如果超出范圍就通知這個(gè)bio處理失敗*/
????????bio_endio(bio, 0, -EIO);
????????return 0;
????}
????/*獲得這個(gè)bio請(qǐng)求在塊設(shè)備內(nèi)存中的起始位置*/
????disk_mem = disk_data + (bio->bi_sector << 9);
????/*開(kāi)始遍歷這個(gè)bio中的每個(gè)bio_vec*/
????bio_for_each_segment(bvec, bio, i)
????{
????????/*因bio_vec中的內(nèi)存地址是使用page *描述的,故在高端內(nèi)存中需要用kmap進(jìn)行映射后才能訪(fǎng)問(wèn),
????????再加上在bio_vec中的偏移位置,才是在高端物理內(nèi)存中的實(shí)際位置*/
????????bvec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
????????/*判斷bio請(qǐng)求處理的方向*/
????????switch(bio_data_dir(bio))
????????{
????????????case READ:
????????????case READA:
????????????????memcpy(bvec_mem, disk_mem, bvec-> bv_len);
????????????????break;
????????????case WRITE :
????????????????memcpy(disk_mem, bvec_mem, bvec-> bv_len);
????????????????break;
????????????default :
????????????????kunmap(bvec->bv_page);
????????}
????????/*處理完每一個(gè)bio_vec都應(yīng)把kmap映射的地址取消掉*/
????????kunmap(bvec->bv_page);
????????/*累加當(dāng)前bio_vec中的內(nèi)存長(zhǎng)度,以確定下一個(gè)bio_vec在塊設(shè)備內(nèi)存中的位置*/
????????disk_mem += bvec->bv_len;
????}
????/*bio中所有的bio_vec處理完后報(bào)告處理結(jié)束*/
????bio_endio(bio, bio->bi_size, 0);
????return 0;
}
四、塊設(shè)備驅(qū)動(dòng)(RamDisk)實(shí)現(xiàn)步驟詳解
其實(shí)從上面的結(jié)構(gòu)體關(guān)系圖就可以看出這就是塊設(shè)備驅(qū)動(dòng)程序的整體的結(jié)構(gòu)了,當(dāng)然這只是較簡(jiǎn)單的塊設(shè)備驅(qū)動(dòng)了,現(xiàn)在我們就即將要做的就是實(shí)現(xiàn)一個(gè)簡(jiǎn)單的RamDisk塊設(shè)備驅(qū)動(dòng)了。其實(shí)告訴大家我這里為什么只實(shí)現(xiàn)一個(gè)簡(jiǎn)單的塊設(shè)備驅(qū)動(dòng),因?yàn)槲沂且獮橐院蟮腗MC/SD卡驅(qū)動(dòng)、Nand flash驅(qū)動(dòng)等做一些前提準(zhǔn)備的。好了,還是先了解一下什么是RamDisk吧。
RamDisk是將Ram中的一部分內(nèi)存空間模擬成一個(gè)磁盤(pán)設(shè)備,以塊設(shè)備的訪(fǎng)問(wèn)方式來(lái)訪(fǎng)問(wèn)這一片內(nèi)存,達(dá)到數(shù)據(jù)存取的目的。RamDisk設(shè)備在Linux設(shè)備文件系統(tǒng)中對(duì)應(yīng)的設(shè)備文件節(jié)點(diǎn)一般為:/dev/ram%d。
建立驅(qū)動(dòng)代碼文件my2440_ramdisk.c,實(shí)現(xiàn)驅(qū)動(dòng)模塊的加載和卸載,步驟如下:
加載部分:分配請(qǐng)求隊(duì)列及綁定請(qǐng)求制造函數(shù) -> 分配及初始化gendisk -> 添加gendisk -> 注冊(cè)塊設(shè)備驅(qū)動(dòng)。
卸載部分:清除請(qǐng)求隊(duì)列 -> 刪除gendisk -> 注銷(xiāo)塊設(shè)備驅(qū)動(dòng)。
#include
#include
#include
#include
#include
#include
#define RAMDISK_MAJOR????0????????/*主設(shè)備號(hào)設(shè)置0讓內(nèi)核動(dòng)態(tài)產(chǎn)生一個(gè)主設(shè)備號(hào)*/
#define RAMDISK_NAME????"my2440_ramdisk"????/*設(shè)備名稱(chēng)*/
#define RAMDISK_SIZE????(4 * 1024 * 1024)???/*虛擬磁盤(pán)的大小,共4M*/
static int ramdisk_major = RAMDISK_MAJOR;???/*用來(lái)保存動(dòng)態(tài)分配的主設(shè)備號(hào)*/
static struct class *ramdisk_class;???????? /*定義一個(gè)設(shè)備類(lèi),好在/dev下動(dòng)態(tài)生成設(shè)備節(jié)點(diǎn)*/
static struct gendisk *my2440_ramdiak;??????/*定義一個(gè)gendisk結(jié)構(gòu)體用來(lái)表示一個(gè)磁盤(pán)設(shè)備*/
static struct request_queue *ramdisk_queue;?/*定義磁盤(pán)設(shè)備的請(qǐng)求隊(duì)列*/
unsigned char *disk_data;/*定義一個(gè)指針來(lái)表示ramdisk塊設(shè)備在內(nèi)存中的域*/
/*塊設(shè)備驅(qū)動(dòng)操作結(jié)構(gòu)體,其實(shí)不需要做什么操作,這里就設(shè)置為空*/
static struct block_device_operations ramdisk_fops =
{
????.owner????= THIS_MODULE,
};
static int __init ramdisk_init(void)
{
????int ret;
????/*塊設(shè)備驅(qū)動(dòng)注冊(cè), 注意這個(gè)塊設(shè)備驅(qū)動(dòng)的注冊(cè)在2.6內(nèi)核中是可選的,
????該函數(shù)由內(nèi)核提供。這里使用是為了獲得一個(gè)動(dòng)態(tài)生成的主設(shè)備號(hào)*/
????ramdisk_major = register_blkdev(RAMDISK_MAJOR, RAMDISK_NAME);
????if(ramdisk_major <= 0)
????{
????????return ramdisk_major;
????}
????/*動(dòng)態(tài)創(chuàng)建一個(gè)設(shè)備節(jié)點(diǎn),跟字符型設(shè)備一樣*/
????ramdisk_class = class_create(THIS_MODULE, RAMDISK_NAME);
????if(IS_ERR(ramdisk_class))
????{
????????ret = -1;
????????goto err_class;
????}
????device_create(ramdisk_class, NULL, MKDEV(ramdisk_major, 0), NULL, RAMDISK_NAME);
????/*RamDisk屬真正隨機(jī)訪(fǎng)問(wèn)的設(shè)備,因此不使用請(qǐng)求隊(duì)列的處理方式,而使用制造請(qǐng)求的方式*/
????ramdisk_queue = blk_alloc_queue(GFP_KERNEL);/*分配ramdisk_queue*/
????if(!ramdisk_queue)
????{
????????ret = -ENOMEM;
????????goto err_queue;
????}
????blk_queue_make_request(ramdisk_queue, &ramdisk_make_request);/*綁定請(qǐng)求制造函數(shù)*/
????/*分配gendisk,該函數(shù)由內(nèi)核提供,參數(shù)為磁盤(pán)設(shè)備的次設(shè)備號(hào)數(shù)量(或者磁盤(pán)的分區(qū)數(shù)量)
????注意一個(gè)分區(qū)就代表一個(gè)次設(shè)備,這里指定數(shù)量后以后就不能被修改了*/
????my2440_ramdiak = alloc_disk(1);
????if(!my2440_ramdiak)
????{
????????ret = -ENOMEM;
????????goto err_alloc;
????}
????/*初始化gendisk*/
????my2440_ramdiak->major = ramdisk_major;????/*這里指定的主設(shè)備號(hào)就是在上面動(dòng)態(tài)獲取的主設(shè)備號(hào)*/
????my2440_ramdiak->first_minor????= 0;???????/*指定第一個(gè)次設(shè)備號(hào)為0*/
????my2440_ramdiak->fops = &ramdisk_fops;???? /*指定塊設(shè)備驅(qū)動(dòng)對(duì)底層硬件操作的結(jié)構(gòu)體指針,定義在后面來(lái)講*/
????my2440_ramdiak->queue = ramdisk_queue;????/*指定初始化好的請(qǐng)求隊(duì)列*/
????sprintf(my2440_ramdiak->disk_name, RAMDISK_NAME);/*指定磁盤(pán)設(shè)備的名稱(chēng)*/
????/*設(shè)置磁盤(pán)設(shè)備的容量大小,該函數(shù)由內(nèi)核提供。
????注意該函數(shù)是以512字節(jié)為1個(gè)扇區(qū)單位進(jìn)行處理的,因?yàn)閮?nèi)核要求如此*/
????set_capacity(my2440_ramdiak, RAMDISK_SIZE >> 9);/*右移9位就是除以512*/
????/*添加gendisk到系統(tǒng)中, 該函數(shù)由內(nèi)核提供*/
????add_disk(my2440_ramdiak);
????return 0;
/*錯(cuò)誤處理*/
err_class:
????unregister_blkdev(ramdisk_major, RAMDISK_NAME);
err_queue:
????device_destroy(ramdisk_class, MKDEV(ramdisk_major, 0));
????class_destroy(ramdisk_class);
err_alloc:
????blk_cleanup_queue(ramdisk_queue);
????return ret;
}
static void __exit ramdisk_exit(void)
{????
????/*刪除磁盤(pán)設(shè)備*/
????del_gendisk(my2440_ramdiak);
????put_disk(my2440_ramdiak);
????/*清除請(qǐng)求隊(duì)列*/
????blk_cleanup_queue(ramdisk_queue);
????/*清除設(shè)備類(lèi)*/
????device_destroy(ramdisk_class, MKDEV(ramdisk_major, 0));
????class_destroy(ramdisk_class);
????/*注銷(xiāo)塊設(shè)備*/
????unregister_blkdev(ramdisk_major, RAMDISK_NAME);
}
module_init(ramdisk_init);
module_exit(ramdisk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huang Gang");
MODULE_DESCRIPTION("My2440 RamDisk Driver");
RamDisk屬真正隨機(jī)訪(fǎng)問(wèn)的設(shè)備,因此沒(méi)有使用請(qǐng)求隊(duì)列的處理方式,而是使用制造請(qǐng)求的方式。制造請(qǐng)求處理函數(shù)實(shí)現(xiàn)如下:
/*綁定請(qǐng)求制造函數(shù)。注意:第一個(gè)參數(shù)仍然是請(qǐng)求隊(duì)列,但在這里實(shí)際不包含任何請(qǐng)求。
所以這里要處理的重點(diǎn)對(duì)象的bio中的每個(gè)bio_vec,他表示一個(gè)或多個(gè)要傳送的緩沖區(qū)。*/
static int ramdisk_make_request(struct request_queue_t *queue, struct bio *bio)
{
????int i;
????struct bio_vec *bvec;
????void *disk_mem;
????void *bvec_mem;
????/*在遍歷段之前先判斷要傳輸數(shù)據(jù)的總長(zhǎng)度大小是否超過(guò)范圍*/
????if((bio->bi_sector << 9) + bio->bi_size > RAMDISK_SIZE)
????{
????????/*如果超出范圍就通知這個(gè)bio處理失敗*/
????????bio_io_error(bio);
????????return 0;
????}
????/*獲得這個(gè)bio請(qǐng)求在塊設(shè)備內(nèi)存中的起始位置*/
????disk_mem = disk_data + (bio->bi_sector << 9);
????/*開(kāi)始遍歷這個(gè)bio中的每個(gè)bio_vec*/
????bio_for_each_segment(bvec, bio, i)
????{
????????/*因bio_vec中的內(nèi)存地址是使用page *描述的,故在高端內(nèi)存中需要用kmap進(jìn)行映射后才能訪(fǎng)問(wèn),
????????再加上在bio_vec中的偏移位置,才是在高端物理內(nèi)存中的實(shí)際位置*/
????????bvec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
????????/*判斷bio請(qǐng)求處理的方向*/
????????switch(bio_data_dir(bio))
????????{
????????????case READ:
????????????case READA:
????????????????memcpy(bvec_mem, disk_mem, bvec->bv_len);
????????????????break;
????????????case WRITE :
????????????????memcpy(disk_mem, bvec_mem, bvec->bv_len);
????????????????break;
????????????default :
????????????????kunmap(bvec->bv_page);
????????}
????????/*處理完每一個(gè)bio_vec都應(yīng)把kmap映射的地址取消掉*/
????????kunmap(bvec->bv_page);
????????/*累加當(dāng)前bio_vec中的內(nèi)存長(zhǎng)度,以確定下一個(gè)bio_vec在塊設(shè)備內(nèi)存中的位置*/
????????disk_mem += bvec->bv_len;
????}
????/*bio中所有的bio_vec處理完后報(bào)告處理結(jié)束*/
????bio_endio(bio, 0);
????return 0;
}
評(píng)論
查看更多