Linux块设备驱动总结
生活随笔
收集整理的這篇文章主要介紹了
Linux块设备驱动总结
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
《Linux設(shè)備驅(qū)動(dòng)程序》第十六章 塊設(shè)備驅(qū)動(dòng)程序讀書(shū)筆記
簡(jiǎn)介
一個(gè)塊設(shè)備驅(qū)動(dòng)程序主要通過(guò)傳輸固定大小的隨機(jī)數(shù)據(jù)來(lái)訪問(wèn)設(shè)備
Linux內(nèi)核視塊設(shè)備為與字符設(shè)備相異的基本設(shè)備類型
Linux塊設(shè)備驅(qū)動(dòng)程序接口使得塊設(shè)備可以發(fā)揮其最大的功效,但是其復(fù)雜程序又是編程者必須面對(duì)的一
個(gè)問(wèn)題
一個(gè)數(shù)據(jù)塊指的是固定大小的數(shù)據(jù),而大小的值由內(nèi)核確定
數(shù)據(jù)塊的大小通常是4096個(gè)字節(jié),但是可以根據(jù)體系結(jié)構(gòu)和所使用的文件系統(tǒng)進(jìn)行改變
與數(shù)據(jù)塊對(duì)應(yīng)的是扇區(qū),它是由底層硬件決定大小的一個(gè)塊,內(nèi)核所處理的設(shè)備扇區(qū)大小是512字節(jié)
如果要使用不同的硬件扇區(qū)大小,用戶必須對(duì)內(nèi)核的扇區(qū)數(shù)做相應(yīng)的修改
注冊(cè)
注冊(cè)塊設(shè)備驅(qū)動(dòng)程序
<linux/fs.h>
int register_blkdev(unsigned int major, const char *name);
如果需要的話分配一個(gè)動(dòng)態(tài)的主設(shè)備號(hào)
在/proc/devices中創(chuàng)建一個(gè)入口項(xiàng)
int unregister_blkdev(unsigned int major, const char *name);
注冊(cè)磁盤(pán)
struct block_device_operations
int (*open) (struct inode *inode, struct file *filp);
int (*release) (struct inode *inode, struct file *filp);
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
int (*media_changed) (struct gendisk *gd);
int (*revalidate_disk) (struct gendisk *gd);
struct module *owner;
gendisk結(jié)構(gòu)
<linux/genhd.h>
struct gendisk
int major;
int first_minor;
int minors;
常取16
char disk_name[32]
顯示在/proc/partitions和sysfs中
struct block_device_operations *fops;
struct request_queue *queue;
int flags;
sector_t capacity;
void *private_data;
struct gendisk *alloc_disk(int minors);
void del_gendisk(struct gendisk *gd);
void add_disk(struct gendisk *gd);
塊設(shè)備操作
open和release函數(shù)
對(duì)于那些操作實(shí)際硬件設(shè)備的驅(qū)動(dòng)程序,open和release函數(shù)可以設(shè)置驅(qū)動(dòng)程序和硬件的狀態(tài)。這些操作
包括使磁盤(pán)開(kāi)始或者停止旋轉(zhuǎn),鎖住可移動(dòng)介質(zhì)的倉(cāng)門以及分配DMA緩存等
有一些操作能夠讓塊設(shè)備在用戶空間內(nèi)被直接打開(kāi),這些操作包括給磁盤(pán)分區(qū),或者在分區(qū)上創(chuàng)建文件
系統(tǒng),或者運(yùn)行文件系統(tǒng)檢查程序
對(duì)可移動(dòng)介質(zhì)的支持
調(diào)用media_changed函數(shù)以檢查介質(zhì)是否被改變
在介質(zhì)改變后將調(diào)用revalideate函數(shù)
ioctl函數(shù)
高層的塊設(shè)備子系統(tǒng)在驅(qū)動(dòng)程序獲得ioctl命令前,已經(jīng)截取了大量的命令
實(shí)際上在一個(gè)現(xiàn)代驅(qū)動(dòng)程序中,許多ioctl命令根本就不用實(shí)現(xiàn)
請(qǐng)求處理
每個(gè)塊設(shè)備驅(qū)動(dòng)程序的核心是它的請(qǐng)求函數(shù)
request函數(shù)介紹
void request(request_queue_t *queue);
當(dāng)內(nèi)核需要驅(qū)動(dòng)程序處理讀取、寫(xiě)入以及其他對(duì)設(shè)備的操作時(shí),就會(huì)調(diào)用該函數(shù)
每個(gè)設(shè)備都有一個(gè)請(qǐng)求隊(duì)列
dev->queue = blk_init_queue(test_request, &dev->lock);
對(duì)request函數(shù)的調(diào)用是與用戶空間進(jìn)程中的動(dòng)作完全異步的
一個(gè)簡(jiǎn)單的request函數(shù)
struct request * elv_next_request(request_queue_t queue);
void end_request(struct request *req, int succeeded);
struct request
sector_t secotr;
unsigned long nr_sectors;
char *buffer
rq_data_dir(struct request *req);
請(qǐng)求隊(duì)列
一個(gè)塊設(shè)備請(qǐng)求隊(duì)列可以這樣描述:包含塊設(shè)備I/O請(qǐng)求的序列
請(qǐng)求隊(duì)列跟蹤未完成的塊設(shè)備的I/O請(qǐng)求
請(qǐng)求隊(duì)列還實(shí)現(xiàn)了插件接口
I/O調(diào)度器還負(fù)責(zé)合并鄰近的請(qǐng)求
請(qǐng)求隊(duì)列擁有request_queue或request_queue_t結(jié)構(gòu)類型
<linux/blkdev.h>
隊(duì)列的創(chuàng)建與刪除
request_queue_t *blk_init_queue(request_fn_proc *request, spinlock_t *lock);
void blk_cleanup_queue(request_queue_t *queue);
隊(duì)列函數(shù)
struct request *elv_next_request(request_queue_t *queue);
void blkdev_dequeue_request(struct request *req);
void elv_requeue_request(request_queue_t *queue, struct request *req);
隊(duì)列控制函數(shù)
void blk_stop_queue(request_queue_t *queue);
void blk_start_queue(request_queue_t *queue);
void blk_queue_bounce_limit(request_queue_t *queue, u64 dma_addr);
void blk_queue_max_sectors(request_queue_t *queue, unsigned short max);
void blk_queue_max_phys_segments(request_queue_t *queue, unsigned short max);
void blk_queue_max_hw_segments(request_queue_t *queue, unsigned short max);
void blk_queue_max_segment_size(request_queue_t *queue, unsigned short max);
void blk_queue_segment_boundary(request_queue_t *queue, unsigned long mask);
void blk_queue_dma_alignment(request_queue_t *queue, int mask);
void blk_queue_hardsect_size(request_queue_t *queue, unsigned short max);
請(qǐng)求過(guò)程剖析
從本質(zhì)上講,一個(gè)request結(jié)構(gòu)是作為一個(gè)bio結(jié)構(gòu)的鏈表實(shí)現(xiàn)的
bio結(jié)構(gòu)
bio結(jié)構(gòu)包含了驅(qū)動(dòng)程序執(zhí)行請(qǐng)求的全部信息,而不必與初始化這個(gè)請(qǐng)求的用戶空間的進(jìn)程相關(guān)聯(lián)
<linux/bio.h>
struct bio
sector_t bi_sector;
unsigned int bi_size;
以字節(jié)為單位所需要傳輸?shù)臄?shù)據(jù)大小
unsigned long bi_flags;
unsigned short bio_phys_segments;
unsigned short bio_hw_segments;
struct bio_vec *bi_io_vec
struct bio_vec
struct page *vb_page;
unsigned int bv_len;
unsigned int bv_offset;
example
int segno;
struct bio_vec *bvec;
bio_for_each_segment(bvec, bio, segno)
{
/* 使用該段進(jìn)行一定的操作 */
}
char *__bio_kmap_atomic(struct bio *bio, int i, enum km_type type);
void __bio_kunmap_atomic(char *buffer, enum km_type type):
struct page *bio_page(struct bio *bio);
int bio_offset(struct bio *bio);
int bio_cur_sectors(struct bio *bio);
char *bio_data(struct bio *bio);
char *bio_kmap_irq(struct bio *bio, unsigned long *flags);
void bio_kunmap_irq(char *buffer, unsigned long *flags);
request結(jié)構(gòu)成員
struct request
sector_t hard_sector;
unsigned long hard_nr_sectors;
unsigned int hard_cur_sectors;
struct bio *bio;
char *buffer;
unsigned short nr_phys_segments;
struct list_head queuelist;
屏障請(qǐng)求
在驅(qū)動(dòng)程序接收到請(qǐng)求前,塊設(shè)備層重新組合了請(qǐng)求以提高I/O性能
出于同樣的目的,驅(qū)動(dòng)程序也可以重新組合請(qǐng)求
但在無(wú)限制重新組合請(qǐng)求時(shí)面臨了一個(gè)問(wèn)題:一些應(yīng)用程序的某些操作,要在另外一些操作開(kāi)始前完成
2.6版本的塊設(shè)備層使用屏障(barrier)請(qǐng)求來(lái)解決這個(gè)問(wèn)題
如果一個(gè)請(qǐng)求被設(shè)置了REQ_HARDBARRER標(biāo)志,那么在其他后續(xù)請(qǐng)求被初始化前,它必須被寫(xiě)入驅(qū)動(dòng)器
void blk_queue_ordered(request_queue_t *queue, int flag);
int blk_barrier_rq(sruct request *req);
如果返回一個(gè)非零值,該請(qǐng)求是一個(gè)屏障請(qǐng)求
不可重試請(qǐng)求
int blk_noretry_request(struct request *req);
請(qǐng)求完成函數(shù)
int end_that_request_first(struct request *req, int success, int count);
void end_that_request_last(struct request *req);
example
void end_request(struct request *req, int uptodate)
{
if (!end_that_request(req, uptodate, req->hard_cur_sectors)
{
add_disk_randomness(req->rq_disk);
blkdev_dequeue_request(req);
end_that_request_last(req);
}
}
使用bio
example
struct request *req
struct bio *bio;
rq_for_each_bio(bio, req)
{
/* 使用該bio結(jié)構(gòu)進(jìn)行一定的操作 */
}
塊設(shè)備請(qǐng)求和DMA
int blk_rq_map_sg(request_queue_t *queue, struct request *req, struct scatterlist *list);
clear_bit(QUEUE_FLAG_CLEAR, &queue->queue_flags);
不使用請(qǐng)求隊(duì)列
typedef int (make_request_fn) (request_queue_t *q, struct bio *bio);
void bio_endio(struct bio *bio, unsigned int bytes, int error);
request_queue_t *blk_alloc_queue(int flags);
并未真正地建立一個(gè)保存請(qǐng)求的隊(duì)列
void blk_queue_make_request(request_queue_t *queue, make_request_fn *func);
drivers/block/ll_rw_block.c
其他一些細(xì)節(jié)
命令預(yù)處理
typedef int (prep_rq_fn) (request_queue_t *queue, struct request *req);
該函數(shù)要能返回下面的值之一
BLKPREP_OK
BLKPREP_KILL
BLKPREP_DEFER
void blk_queue_prep_rq(request_queue_t *queue, prep_rq_fn *func);
標(biāo)記命令隊(duì)列
同時(shí)擁有多個(gè)活動(dòng)請(qǐng)求的硬件通常支持某種形式的標(biāo)記命令隊(duì)列(Tagged Command Queueing, TCQ)
TCQ只是為每個(gè)請(qǐng)求添加一個(gè)整數(shù)(標(biāo)記)的技術(shù),這樣當(dāng)驅(qū)動(dòng)器完成它們中的一個(gè)請(qǐng)求后,它就可以告
訴驅(qū)動(dòng)程序完成的是哪個(gè)
int blk_queue_int_tags(request_queue_t *queue, int depth, struct blk_queue_tag *tags);
int blk_queue_resize_tags(request_queue_t *queue, int new_depth);
int blk_queue_start_tag(request_queue_t *queue, struct request *req);
void blk_queue_end_tag(request_queue_t *queue, struct request *req);
struct request *blk_queue_find_tag(request_queue_t *queue, int tag);
void blk_queue_invalidate_tags(request_queue_t *queue);
========
Linux設(shè)備驅(qū)動(dòng)--塊設(shè)備(一)之概念和框架
基本概念?
?塊設(shè)備(blockdevice)
--- 是一種具有一定結(jié)構(gòu)的隨機(jī)存取設(shè)備,對(duì)這種設(shè)備的讀寫(xiě)是按塊進(jìn)行的,他使用緩沖區(qū)來(lái)存放暫時(shí)
的數(shù)據(jù),待條件成熟后,從緩存一次性寫(xiě)入設(shè)備或者從設(shè)備一次性讀到緩沖區(qū)。
字符設(shè)備(Character device)
---是一個(gè)順序的數(shù)據(jù)流設(shè)備,對(duì)這種設(shè)備的讀寫(xiě)是按字符進(jìn)行的,而且這些字符是連續(xù)地形成一個(gè)數(shù)據(jù)
流。他不具備緩沖區(qū),所以對(duì)這種設(shè)備的讀寫(xiě)是實(shí)時(shí)的。
?
扇區(qū)(Sectors):任何塊設(shè)備硬件對(duì)數(shù)據(jù)處理的基本單位。通常,1個(gè)扇區(qū)的大小為512byte。(對(duì)設(shè)備而
言)
塊 ?(Blocks):由Linux制定對(duì)內(nèi)核或文件系統(tǒng)等數(shù)據(jù)處理的基本單位。通常,1個(gè)塊由1個(gè)或多個(gè)扇區(qū)組
成。(對(duì)Linux操作系統(tǒng)而言)
段(Segments):由若干個(gè)相鄰的塊組成。是Linux內(nèi)存管理機(jī)制中一個(gè)內(nèi)存頁(yè)或者內(nèi)存頁(yè)的一部分。
頁(yè)、段、塊、扇區(qū)之間的關(guān)系圖如下:
?
塊設(shè)備驅(qū)動(dòng)整體框架
?塊設(shè)備的應(yīng)用在Linux中是一個(gè)完整的子系統(tǒng)。
在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ù)操作,以提高訪問(wèn)的效率,然后再由內(nèi)核中的I/O調(diào)度程序子系統(tǒng)來(lái)負(fù)責(zé)
提交 ?I/O 請(qǐng)求, ?調(diào)度程序?qū)⒋疟P(pán)資源分配給系統(tǒng)中所有掛起的塊 I/O ?請(qǐng)求,其工作是管理塊設(shè)備
的請(qǐng)求隊(duì)列,決定隊(duì)列中的請(qǐng)求的排列順序以及什么時(shí)候派發(fā)請(qǐng)求到設(shè)備。
由通用塊層(Generic Block Layer)負(fù)責(zé)維持一個(gè)I/O請(qǐng)求在上層文件系統(tǒng)與底層物理磁盤(pán)之間的關(guān)系。
在通用塊層中,通常用一個(gè)bio結(jié)構(gòu)體來(lái)對(duì)應(yīng)一個(gè)I/O請(qǐng)求。
Linux提供了一個(gè)gendisk數(shù)據(jù)結(jié)構(gòu)體,用來(lái)表示一個(gè)獨(dú)立的磁盤(pán)設(shè)備或分區(qū),用于對(duì)底層物理磁盤(pán)進(jìn)行
訪問(wèn)。在gendisk中有一個(gè)類似字符設(shè)備中file_operations的硬件操作結(jié)構(gòu)指針,是
block_device_operations結(jié)構(gòu)體。
當(dāng)多個(gè)請(qǐng)求提交給塊設(shè)備時(shí),執(zhí)行效率依賴于請(qǐng)求的順序。如果所有的請(qǐng)求是同一個(gè)方向(如:寫(xiě)數(shù)據(jù)
),執(zhí)行效率是最大的。內(nèi)核在調(diào)用塊設(shè)備驅(qū)動(dòng)程序例程處理請(qǐng)求之前,先收集I/O請(qǐng)求并將請(qǐng)求排序,
然后,將連續(xù)扇區(qū)操作的多個(gè)請(qǐng)求進(jìn)行合并以提高執(zhí)行效率(內(nèi)核算法會(huì)自己做,不用你管),對(duì)I/O請(qǐng)
求排序的算法稱為電梯算法(elevator algorithm)。電梯算法在I/O調(diào)度層完成。內(nèi)核提供了不同類型
的電梯算法,電梯算法有
1 noop(實(shí)現(xiàn)簡(jiǎn)單的FIFO,基本的直接合并與排序),
2 anticipatory(延遲I/O請(qǐng)求,進(jìn)行臨界區(qū)的優(yōu)化排序),
3 Deadline(針對(duì)anticipatory缺點(diǎn)進(jìn)行改善,降低延遲時(shí)間),
4 Cfq(均勻分配I/O帶寬,公平機(jī)制)
PS:其實(shí)IO調(diào)度層(包括請(qǐng)求合并排序算法)是不需要用戶管的,內(nèi)核已經(jīng)做好
相關(guān)數(shù)據(jù)結(jié)構(gòu)
block_device: ? ? ?描述一個(gè)分區(qū)或整個(gè)磁盤(pán)對(duì)內(nèi)核的一個(gè)塊設(shè)備實(shí)例?
gendisk: ? ? ? ? ? ? ? 描述一個(gè)通用硬盤(pán)(generic hard disk)對(duì)象。
hd_struct: ? ? ? ? ? ? 描述分區(qū)應(yīng)有的分區(qū)信息?
bio: ? ? ? ? ? ? ? ? ? ? ? ?描述塊數(shù)據(jù)傳送時(shí)怎樣完成填充或讀取塊給driver
request: ? ? ? ? ? ? ? ?描述向內(nèi)核請(qǐng)求一個(gè)列表準(zhǔn)備做隊(duì)列處理。?
request_queue: ?描述內(nèi)核申請(qǐng)request資源建立請(qǐng)求鏈表并填寫(xiě)B(tài)IO形成隊(duì)列。
========
Linux設(shè)備驅(qū)動(dòng)--塊設(shè)備(二)之相關(guān)結(jié)構(gòu)體
上回最后面介紹了相關(guān)數(shù)據(jù)結(jié)構(gòu),下面再詳細(xì)介紹
塊設(shè)備對(duì)象結(jié)構(gòu) block_device
內(nèi)核用結(jié)構(gòu)block_device實(shí)例代表一個(gè)塊設(shè)備對(duì)象,如:整個(gè)硬盤(pán)或特定分區(qū)。如果該結(jié)構(gòu)代表一個(gè)分
區(qū),則其成員bd_part指向設(shè)備的分區(qū)結(jié)構(gòu)。如果該結(jié)構(gòu)代表設(shè)備,則其成員bd_disk指向設(shè)備的通用硬
盤(pán)結(jié)構(gòu)gendisk
當(dāng)用戶打開(kāi)塊設(shè)備文件時(shí),內(nèi)核創(chuàng)建結(jié)構(gòu)block_device實(shí)例,設(shè)備驅(qū)動(dòng)程序還將創(chuàng)建結(jié)構(gòu)gendisk實(shí)例,
分配請(qǐng)求隊(duì)列并注冊(cè)結(jié)構(gòu)block_device實(shí)例。
塊設(shè)備對(duì)象結(jié)構(gòu)block_device列出如下(在include/linux/fs.h中)
[cpp] view plain copy print?
struct block_device { ?
dev_t bd_dev; ?/* not a kdev_t - it's a search key */ ?
struct inode * bd_inode; /* 分區(qū)節(jié)點(diǎn) */ ?
struct super_block * bd_super; ?
int bd_openers; ?
struct mutex bd_mutex;/* open/close mutex 打開(kāi)與關(guān)閉的互斥量*/ ?
struct semaphore bd_mount_sem; ? ?/*掛載操作信號(hào)量*/ ??
struct list_head bd_inodes; ?
void * bd_holder; ?
int bd_holders; ?
#ifdef CONFIG_SYSFS ?
struct list_head bd_holder_list; ?
#endif ?
struct block_device * bd_contains; ?
unsigned bd_block_size; ? ? /*分區(qū)塊大小*/ ?
struct hd_struct * bd_part; ?
unsigned bd_part_count; ? /*打開(kāi)次數(shù)*/ ?
int bd_invalidated; ?
struct gendisk * bd_disk; /*設(shè)備為硬盤(pán)時(shí),指向通用硬盤(pán)結(jié)構(gòu)*/ ?
struct list_head bd_list; ?
struct backing_dev_info *bd_inode_backing_dev_info; ?
unsigned long bd_private; ?
/* The counter of freeze processes */ ?
int bd_fsfreeze_count; ?
/* Mutex for freeze */ ?
struct mutex bd_fsfreeze_mutex; ?
}; ?
通用硬盤(pán)結(jié)構(gòu) gendisk
結(jié)構(gòu)體gendisk代表了一個(gè)通用硬盤(pán)(generic hard disk)對(duì)象,它存儲(chǔ)了一個(gè)硬盤(pán)的信息,包括請(qǐng)求
隊(duì)列、分區(qū)鏈表和塊設(shè)備操作函數(shù)集等。塊設(shè)備驅(qū)動(dòng)程序分配結(jié)構(gòu)gendisk實(shí)例,裝載分區(qū)表,分配請(qǐng)求
隊(duì)列并填充結(jié)構(gòu)的其他域。
支持分區(qū)的塊驅(qū)動(dòng)程序必須包含 <linux/genhd.h> 頭文件,并聲明一個(gè)結(jié)構(gòu)gendisk,內(nèi)核還維護(hù)該結(jié)
構(gòu)實(shí)例的一個(gè)全局鏈表gendisk_head,通過(guò)函數(shù)add_gendisk、del_gendisk和get_gendisk維護(hù)該鏈表。
結(jié)構(gòu)gendisk列出如下(在include/linux/genhd.h中):
[cpp] view plain copy print?
struct gendisk { ?
? ? int major; ? ? ? ? ? ?/* 驅(qū)動(dòng)程序的主設(shè)備號(hào) */ ?
? ? int first_minor; ? ? ? /*第一個(gè)次設(shè)備號(hào)*/ ?
? ? int minors; ? ? ? ? ?/*次設(shè)備號(hào)的最大數(shù)量,沒(méi)有分區(qū)的設(shè)備,此值為1 */ ?
? ? char disk_name[32]; ?/* 主設(shè)備號(hào)驅(qū)動(dòng)程序的名字*/ ?
? ? struct hd_struct **part; ? /* 分區(qū)列表,由次設(shè)備號(hào)排序 */ ?
? ? struct block_device_operations *fops; ?/*塊設(shè)備操作函數(shù)集*/ ?
? ? struct request_queue *queue; ? ? ? ? /*請(qǐng)求隊(duì)列*/ ?
? ? struct blk_scsi_cmd_filter cmd_filter; ?
? ? void *private_data; ? ? ? ? ? ? ? ? /*私有數(shù)據(jù)*/ ?
? ? sector_t capacity; ? ? /* 函數(shù)set_capacity設(shè)置的容量,以扇區(qū)為單位*/ ?
? ? int flags; ? ? ? ? ? ? ? ? /*設(shè)置驅(qū)動(dòng)器狀態(tài)的標(biāo)志,如:可移動(dòng)介質(zhì)為?
GENHD_FL_REMOVABLE*/ ?
? ? struct device dev; ? ? ? ? ? ? ? ? /*從設(shè)備驅(qū)動(dòng)模型基類結(jié)構(gòu)device繼承*/ ?
? ? struct kobject *holder_dir; ?
? ? struct kobject *slave_dir; ?
?struct timer_rand_state *random; ?
? ? int policy; ??
? ? atomic_t sync_io; ? ? ? ?/* RAID */ ?
? ? unsigned long stamp; ?
? ? int in_flight; ?
#ifdef ?CONFIG_SMP ?
? ? struct disk_stats *dkstats; ? ?
#else ?
/*硬盤(pán)統(tǒng)計(jì)信息,如:讀或?qū)懙纳葏^(qū)數(shù)、融合的扇區(qū)數(shù)、在請(qǐng)求隊(duì)列的時(shí)間等*/ ?
? ? struct disk_stats dkstats; ?
#endif ?
? ? struct work_struct async_notify; ?
#ifdef ?CONFIG_BLK_DEV_INTEGRITY ?
? ? struct blk_integrity *integrity; ? /*用于數(shù)據(jù)完整性擴(kuò)展*/ ?
#endif ?
}; ?
Linux內(nèi)核提供了一組函數(shù)來(lái)操作gendisk,主要包括:
分配gendisk
struct gendisk *alloc_disk(int minors);
minors 參數(shù)是這個(gè)磁盤(pán)使用的次設(shè)備號(hào)的數(shù)量,一般也就是磁盤(pán)分區(qū)的數(shù)量,此后minors不能被修改。
增加gendisk
gendisk結(jié)構(gòu)體被分配之后,系統(tǒng)還不能使用這個(gè)磁盤(pán),需要調(diào)用如下函數(shù)來(lái)注冊(cè)這個(gè)磁盤(pán)設(shè)備:
void add_disk(struct gendisk *gd);
特別要注意的是對(duì)add_disk()的調(diào)用必須發(fā)生在驅(qū)動(dòng)程序的初始化工作完成并能響應(yīng)磁盤(pán)的請(qǐng)求之后。
?釋放gendisk
當(dāng)不再需要一個(gè)磁盤(pán)時(shí),應(yīng)當(dāng)使用如下函數(shù)釋放gendisk:
void del_gendisk(struct gendisk *gd);
設(shè)置gendisk容量
void set_capacity(struct gendisk *disk, sector_t size);
塊設(shè)備中最小的可尋址單元是扇區(qū),扇區(qū)大小一般是2的整數(shù)倍,最常見(jiàn)的大小是512字節(jié)。扇區(qū)的大小
是設(shè)備的物理屬性,扇區(qū)是所有塊設(shè)備的基本單元,塊設(shè)備 無(wú)法對(duì)比它還小的單元進(jìn)行尋址和操作,不
過(guò)許多塊設(shè)備能夠一次就傳輸多個(gè)扇區(qū)。雖然大多數(shù)塊設(shè)備的扇區(qū)大小都是512字節(jié),不過(guò)其它大小的扇
區(qū)也很常見(jiàn), 比如,很多CD-ROM盤(pán)的扇區(qū)都是2K大小。不管物理設(shè)備的真實(shí)扇區(qū)大小是多少,內(nèi)核與塊
設(shè)備驅(qū)動(dòng)交互的扇區(qū)都以512字節(jié)為單位。因此,set_capacity()函數(shù)也以512字節(jié)為單位。
分區(qū)結(jié)構(gòu)hd_struct代表了一個(gè)分區(qū)對(duì)象,它存儲(chǔ)了一個(gè)硬盤(pán)的一個(gè)分區(qū)的信息,驅(qū)動(dòng)程序初始化時(shí),從
硬盤(pán)的分區(qū)表中提取分區(qū)信息,存放在分區(qū)結(jié)構(gòu)實(shí)例中。
塊設(shè)備操作函數(shù)集結(jié)構(gòu) block_device_operations
字符設(shè)備通過(guò) file_operations 操作結(jié)構(gòu)使它們的操作對(duì)系統(tǒng)可用. 一個(gè)類似的結(jié)構(gòu)用在塊設(shè)備上是?
struct block_device_operations,
定義在 <linux/fs.h>.?
int (*open)(struct inode *inode, struct file *filp);?
int (*release)(struct inode *inode, struct file *filp);?
就像它們的字符驅(qū)動(dòng)對(duì)等體一樣工作的函數(shù); 無(wú)論何時(shí)設(shè)備被打開(kāi)和關(guān)閉都調(diào)用它們. 一個(gè)字符驅(qū)動(dòng)可
能通過(guò)啟動(dòng)設(shè)備或者鎖住門(為可移出的介質(zhì))來(lái)響應(yīng)一個(gè) open 調(diào)用. 如果你將介質(zhì)鎖入設(shè)備, 你當(dāng)然
應(yīng)當(dāng)在 release 方法中解鎖.
int (*ioctl)(struct inode *inode, struct file *filp,?
? ? ? ? ? ? ? ? ? ? ? ? ? unsigned int cmd, unsigned long arg);?
實(shí)現(xiàn) ioctl 系統(tǒng)調(diào)用的方法. 但是, 塊層首先解釋大量的標(biāo)準(zhǔn)請(qǐng)求; 因此大部分的塊驅(qū)動(dòng) ioctl 方法
相當(dāng)短.
PS:在block_device_operations中沒(méi)有實(shí)際讀或?qū)憯?shù)據(jù)的函數(shù). 在塊 I/O 子系統(tǒng), 這些操作由請(qǐng)求函
數(shù)處理
請(qǐng)求結(jié)構(gòu)request
結(jié)構(gòu)request代表了掛起的I/O請(qǐng)求,每個(gè)請(qǐng)求用一個(gè)結(jié)構(gòu)request實(shí)例描述,存放在請(qǐng)求隊(duì)列鏈表中,由
電梯算法進(jìn)行排序,每個(gè)請(qǐng)求包含1個(gè)或多個(gè)結(jié)構(gòu)bio實(shí)例
struct request { ?
? ? //用于掛在請(qǐng)求隊(duì)列鏈表的節(jié)點(diǎn),使用函數(shù)blkdev_dequeue_request訪問(wèn)它,而不能直接訪 ?
問(wèn) ?
? ? struct list_head queuelist; ??
? ? struct list_head donelist; ?/*用于掛在已完成請(qǐng)求鏈表的節(jié)點(diǎn)*/ ?
? ? struct request_queue *q; ? /*指向請(qǐng)求隊(duì)列*/ ?
? ? unsigned int cmd_flags; ? ?/*命令標(biāo)識(shí)*/ ?
? ? enum rq_cmd_type_bits cmd_type; ?/*命令類型*/ ?
? ? /*各種各樣的扇區(qū)計(jì)數(shù)*/ ?
? ?/*為提交i/o維護(hù)bio橫斷面的狀態(tài)信息,hard_*成員是塊層內(nèi)部使用的,驅(qū)動(dòng)程序不應(yīng)該改變?
它們*/ ?
? ? sector_t sector; ? ? /*將提交的下一個(gè)扇區(qū)*/ ?
? ? sector_t hard_sector; ? ? ? ?/* 將完成的下一個(gè)扇區(qū)*/ ?
? ? unsigned long nr_sectors; ?/* 整個(gè)請(qǐng)求還需要傳送的扇區(qū)數(shù)*/ ?
? ? unsigned long hard_nr_sectors; /* 將完成的扇區(qū)數(shù)*/ ?
?/*在當(dāng)前bio中還需要傳送的扇區(qū)數(shù) */ ?
? ? unsigned int current_nr_sectors; ?
? ? /*在當(dāng)前段中將完成的扇區(qū)數(shù)*/ ?
? ? unsigned int hard_cur_sectors; ?
? ? struct bio *bio; ? ? /*請(qǐng)求中第一個(gè)未完成操作的bio*、?
? ? struct bio *biotail; /*請(qǐng)求鏈表中末尾的bio*、?
? ? struct hlist_node hash; ?/*融合 hash */ ?
? ? /* rb_node僅用在I/O調(diào)度器中,當(dāng)請(qǐng)求被移到分發(fā)隊(duì)列中時(shí),?
請(qǐng)求將被刪除。因此,讓completion_data與rb_node分享空間*/ ? ? ?
? ? union { ?
? ? ? ? struct rb_node rb_node; ? /* 排序/查找*/ ?
? ? ? ? void *completion_data; ?
? ? }; ?
?request結(jié)構(gòu)體的主要成員包括:
?sector_t hard_sector;?
unsigned long hard_nr_sectors;?
unsigned int hard_cur_sectors;?
上述3個(gè)成員標(biāo)識(shí)還未完成的扇區(qū),hard_sector是第1個(gè)尚未傳輸?shù)纳葏^(qū),hard_nr_sectors是尚待完成
的扇區(qū)數(shù),hard_cur_sectors是并且當(dāng)前I/O操作中待完成的扇區(qū)數(shù)。這些成員只用于內(nèi)核塊設(shè)備層,驅(qū)
動(dòng)不應(yīng)當(dāng)使用它們。
?sector_t sector;?
unsigned long nr_sectors;?
unsigned int current_nr_sectors;?
驅(qū)動(dòng)中會(huì)經(jīng)常與這3個(gè)成員打交道,這3個(gè)成員在內(nèi)核和驅(qū)動(dòng)交互中發(fā)揮著重大作用。它們以512字節(jié)大小
為1個(gè)扇區(qū),如果硬件的扇區(qū)大小不是512字節(jié),則需要進(jìn)行相應(yīng)的調(diào)整。例如,如果硬件的扇區(qū)大小是
2048字節(jié),則在進(jìn)行硬件操作之前,需要用4來(lái)除起始扇區(qū)號(hào)。
?hard_sector、hard_nr_sectors、hard_cur_sectors與sector、nr_sectors、current_nr_sectors之間
可認(rèn)為是“副本”關(guān)系。
struct bio *bio;?
bio是這個(gè)請(qǐng)求中包含的bio結(jié)構(gòu)體的鏈表,驅(qū)動(dòng)中不宜直接存取這個(gè)成員,而應(yīng)該使用后文將介紹的
rq_for_each_bio()。
請(qǐng)求隊(duì)列結(jié)構(gòu)request_queue
每個(gè)塊設(shè)備都有一個(gè)請(qǐng)求隊(duì)列,每個(gè)請(qǐng)求隊(duì)列單獨(dú)執(zhí)行I/O調(diào)度,請(qǐng)求隊(duì)列是由請(qǐng)求結(jié)構(gòu)實(shí)例鏈接成的雙
向鏈表,鏈表以及整個(gè)隊(duì)列的信息用結(jié)構(gòu)request_queue描述,稱為請(qǐng)求隊(duì)列對(duì)象結(jié)構(gòu)或請(qǐng)求隊(duì)列結(jié)構(gòu)。
它存放了關(guān)于掛起請(qǐng)求的信息以及管理請(qǐng)求隊(duì)列(如:電梯算法)所需要的信息。結(jié)構(gòu)成員request_fn
是來(lái)自設(shè)備驅(qū)動(dòng)程序的請(qǐng)求處理函數(shù)。
請(qǐng)求隊(duì)列結(jié)構(gòu)request_queue列出如下(在/include/linux/blk_dev.h中)
太長(zhǎng)了,此處略,其實(shí)也看不懂,- -#
Bio結(jié)構(gòu)
通常1個(gè)bio對(duì)應(yīng)1個(gè)I/O請(qǐng)求,IO調(diào)度算法可將連續(xù)的bio合并成1個(gè)請(qǐng)求。所以,1個(gè)請(qǐng)求可以包含多個(gè)
bio。
內(nèi)核中塊I/O操作的基本容器由bio結(jié)構(gòu)體表示,定義 在<linux/bio.h>中,該結(jié)構(gòu)體代表了正在現(xiàn)場(chǎng)的
(活動(dòng)的)以片段(segment)鏈表形式組織的塊I/O操作。一個(gè)片段是一小 塊連續(xù)的內(nèi)存緩沖區(qū)。這樣
的好處就是不需要保證單個(gè)緩沖區(qū)一定要連續(xù)。所以通過(guò)片段來(lái)描述緩沖區(qū),即使一個(gè)緩沖區(qū)分散在內(nèi)
存的多個(gè)位置上,bio結(jié)構(gòu)體也 能對(duì)內(nèi)核保證I/O操作的執(zhí)行,這樣的就叫做聚散I/O.
bio為通用層的主要數(shù)據(jù)結(jié)構(gòu),既描述了磁盤(pán)的位置,又描述了內(nèi)存的位置,是上層內(nèi)核vfs與下層驅(qū)動(dòng)
的連接紐帶
struct bio { ?
sector_t ? ? ? ?bi_sector;//該bio結(jié)構(gòu)所要傳輸?shù)牡谝粋€(gè)(512字節(jié))扇區(qū):磁盤(pán)的位置 ?
struct bio ? ? ? ?*bi_next; ? ?//請(qǐng)求鏈表 ?
struct block_device ? ?*bi_bdev;//相關(guān)的塊設(shè)備 ?
unsigned long ? ? ? ?bi_flags//狀態(tài)和命令標(biāo)志 ?
unsigned long ? ? ? ?bi_rw; //讀寫(xiě) ?
unsigned short ? ? ? ?bi_vcnt;//bio_vesc偏移的個(gè)數(shù) ?
unsigned short ? ? ? ?bi_idx; ? ?//bi_io_vec的當(dāng)前索引 ?
unsigned short ? ? ? ?bi_phys_segments;//結(jié)合后的片段數(shù)目 ?
unsigned short ? ? ? ?bi_hw_segments;//重映射后的片段數(shù)目 ?
unsigned int ? ? ? ?bi_size; ? ?//I/O計(jì)數(shù) ?
unsigned int ? ? ? ?bi_hw_front_size;//第一個(gè)可合并的段大小; ?
unsigned int ? ? ? ?bi_hw_back_size;//最后一個(gè)可合并的段大小 ?
unsigned int ? ? ? ?bi_max_vecs; ? ?//bio_vecs數(shù)目上限 ?
struct bio_vec ? ? ? ?*bi_io_vec; ? ?//bio_vec鏈表:內(nèi)存的位置 ?
bio_end_io_t ? ? ? ?*bi_end_io;//I/O完成方法 ?
atomic_t ? ? ? ?bi_cnt; //使用計(jì)數(shù) ?
void ? ? ? ? ? ?*bi_private; //擁有者的私有方法 ?
bio_destructor_t ? ?*bi_destructor; ? ?//銷毀方法 ?
}; ?
內(nèi)存數(shù)據(jù)段結(jié)構(gòu)bio_vec
? ? ? ?結(jié)構(gòu)bio_vec代表了內(nèi)存中的一個(gè)數(shù)據(jù)段,數(shù)據(jù)段用頁(yè)、偏移和長(zhǎng)度描
述。I/O需要執(zhí)行的內(nèi)存位置用段表示,結(jié)構(gòu)bio指向了一個(gè)段的數(shù)組。
結(jié)構(gòu)bio_vec列出如下(在include/linux/bio.h中):
struct bio_vec {
? ? ? ?struct page ? ? *bv_page; ? /*數(shù)據(jù)段所在的頁(yè)*/
? ? ? ?unsigned short ?bv_len; ? ? /*數(shù)據(jù)段的長(zhǎng)度*/
? ? ? ?unsigned short ?bv_offset; ?/*數(shù)據(jù)段頁(yè)內(nèi)偏移*/
};
塊設(shè)備各個(gè)結(jié)構(gòu)體間關(guān)系
========
Linux設(shè)備驅(qū)動(dòng)--塊設(shè)備(三)之程序設(shè)計(jì)
?塊設(shè)備驅(qū)動(dòng)注冊(cè)與注銷
塊設(shè)備驅(qū)動(dòng)中的第1個(gè)工作通常是注冊(cè)它們自己到內(nèi)核,完成這個(gè)任務(wù)的函數(shù)是 register_blkdev(),其
原型為:
int register_blkdev(unsigned int major, const char *name);
major 參數(shù)是塊設(shè)備要使用的主設(shè)備號(hào),name為設(shè)備名,它會(huì)在/proc/devices中被顯示。 如果major為
0,內(nèi)核會(huì)自動(dòng)分配一個(gè)新的主設(shè)備號(hào)register_blkdev()函數(shù)的返回值就是這個(gè)主設(shè)備號(hào)。如果返回1個(gè)
負(fù)值,表明發(fā)生了一個(gè)錯(cuò)誤。
與register_blkdev()對(duì)應(yīng)的注銷函數(shù)是unregister_blkdev(),其原型為:
int unregister_blkdev(unsigned int major, const char *name);
這里,傳遞給register_blkdev()的參數(shù)必須與傳遞給register_blkdev()的參數(shù)匹配,否則這個(gè)函數(shù)返
回-EINVAL。
塊設(shè)備的請(qǐng)求隊(duì)列操作
標(biāo)準(zhǔn)的請(qǐng)求處理程序能排序請(qǐng)求,并合并相鄰的請(qǐng)求,如果一個(gè)塊設(shè)備希望使用標(biāo)準(zhǔn)的請(qǐng)求處理程序,
那它必須調(diào)用函數(shù)blk_init_queue來(lái)初始化請(qǐng)求隊(duì)列。當(dāng)處理在隊(duì)列上的請(qǐng)求時(shí),必須持有隊(duì)列自旋鎖
。初始化請(qǐng)求隊(duì)列
request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);
該函數(shù)的第1個(gè)參數(shù)是請(qǐng)求處理函數(shù)的指針,第2個(gè)參數(shù)是控制訪問(wèn)隊(duì)列權(quán)限的自旋鎖,這個(gè)函數(shù)會(huì)發(fā)生
內(nèi)存分配的行為,故它可能會(huì)失敗,函數(shù)調(diào)用成
功時(shí),它返回指向初始化請(qǐng)求隊(duì)列的指針,否則,返回NULL。這個(gè)函數(shù)一般在塊設(shè)備驅(qū)動(dòng)的模塊加載函
數(shù)中調(diào)用。清除請(qǐng)求隊(duì)列
void blk_cleanup_queue(request_queue_t * q);
這個(gè)函數(shù)完成將請(qǐng)求隊(duì)列返回給系統(tǒng)的任務(wù),一般在塊設(shè)備驅(qū)動(dòng)模塊卸載函數(shù)中調(diào)用。
?
提取請(qǐng)求
struct request *elv_next_request(request_queue_t *queue);?
上述函數(shù)用于返回下一個(gè)要處理的請(qǐng)求(由 I/O 調(diào)度器決定),如果沒(méi)有請(qǐng)求則返回NULL。
去除請(qǐng)求
void blkdev_dequeue_request(struct request *req);?
上述函數(shù)從隊(duì)列中去除1個(gè)請(qǐng)求。如果驅(qū)動(dòng)中同時(shí)從同一個(gè)隊(duì)列中操作了多個(gè)請(qǐng)求,它必須以這樣的方式
將它們從隊(duì)列中去除。
?
分配“請(qǐng)求隊(duì)列”
request_queue_t *blk_alloc_queue(int gfp_mask);
對(duì)于FLASH、RAM盤(pán)等完全隨機(jī)訪問(wèn)的非機(jī)械設(shè)備,并不需要進(jìn)行復(fù)雜的I/O調(diào)度,這個(gè)時(shí)候,應(yīng)該使用上
述函數(shù)分配1個(gè)“請(qǐng)求隊(duì)列”,并使用如下函數(shù)來(lái)綁定“請(qǐng)求隊(duì)列”和“制造請(qǐng)求”函數(shù)。
void blk_queue_make_request(request_queue_t * q,?
make_request_fn * mfn);
void blk_queue_hardsect_size(request_queue_t *queue,?
unsigned short max);?
該函數(shù)用于告知內(nèi)核塊設(shè)備硬件扇區(qū)的大小,所有由內(nèi)核產(chǎn)生的請(qǐng)求都是這個(gè)大小的倍數(shù)并且被正確對(duì)
界。但是,內(nèi)核塊設(shè)備層和驅(qū)動(dòng)之間的通信還是以512字節(jié)扇區(qū)為單位進(jìn)行。
?
步驟:
在塊設(shè)備驅(qū)動(dòng)的模塊加載函數(shù)中通常需要完成如下工作:
① 分配、初始化請(qǐng)求隊(duì)列,綁定請(qǐng)求隊(duì)列和請(qǐng)求函數(shù)。
② 分配、初始化gendisk,給gendisk的major、fops、queue等成
員賦值,最后添加gendisk。
③ 注冊(cè)塊設(shè)備驅(qū)動(dòng)。
在塊設(shè)備驅(qū)動(dòng)的模塊卸載函數(shù)中通常需要與模塊加載函數(shù)相反的工作:
① 清除請(qǐng)求隊(duì)列。
② 刪除gendisk和對(duì)gendisk的引用。
③ 刪除對(duì)塊設(shè)備的引用,注銷塊設(shè)備驅(qū)動(dòng)。
總結(jié):
塊設(shè)備的I/O操作方式與字符設(shè)備存在較大的不同,因而引入了
request_queue、request、bio等一系列數(shù)據(jù)結(jié)構(gòu)。在整個(gè)塊設(shè)備的I/O操作中,貫穿于始終的就是“請(qǐng)
求”,字符設(shè)備的I/O操作則是直接進(jìn)行不繞彎,
塊設(shè)備的I/O操作會(huì)排隊(duì)和整合。
驅(qū)動(dòng)的任務(wù)是處理請(qǐng)求,對(duì)請(qǐng)求的排隊(duì)和整合由I/O調(diào)度算法解決,因此,塊設(shè)備驅(qū)動(dòng)的核心就是請(qǐng)求處
理函數(shù)或“制造請(qǐng)求”函數(shù)。
盡管在塊設(shè)備驅(qū)動(dòng)中仍然存在block_device_operations結(jié)構(gòu)體及其成員函數(shù),但其不再包含讀寫(xiě)一類的
成員函數(shù),而只是包含打開(kāi)、釋放及I/O控制等
與具體讀寫(xiě)無(wú)關(guān)的函數(shù)。塊設(shè)備驅(qū)動(dòng)的結(jié)構(gòu)相當(dāng)復(fù)雜的,但幸運(yùn)的是,塊設(shè)備不像字符設(shè)備那么包羅萬(wàn)
象,它通常就是存儲(chǔ)設(shè)備,而且驅(qū)動(dòng)的主體已經(jīng)
由Linux內(nèi)核提供,針對(duì)一個(gè)特定的硬件系統(tǒng),驅(qū)動(dòng)工程師所涉及到的工作往往只是編寫(xiě)少量的與硬件直
接交互的代碼。
#include <linux/init.h> ? ?
#include <linux/module.h> ? ?
#include <linux/kernel.h> ? ?
#include <linux/fs.h> ?
#include <asm/uaccess.h> ?
#include <linux/spinlock.h> ?
#include <linux/sched.h> ?
#include <linux/types.h> ?
#include <linux/fcntl.h> ?
#include <linux/hdreg.h> ?
#include <linux/genhd.h> ?
#include <linux/blkdev.h> ?
??
#define MAXBUF 1024 ??
??
??
#define BLK_MAJOR 253 ?
??
char blk_dev_name[]="blk_dev"; ?
static char flash[1024*16]; ?
??
??
int major; ?
spinlock_t lock; ?
struct gendisk *gd; ?
??
??
??
/*塊設(shè)備數(shù)據(jù)傳輸*/ ?
static void blk_transfer(unsigned long sector, unsigned long nsect, char *buffer, int?
write) ?
{ ?
? ? int read = !write; ?
? ? if(read) ?
? ? { ?
? ? ? ? memcpy(buffer, flash+sector*512, nsect*512); ?
? ? } ?
? ? else ?
? ? { ?
? ? ? ? memcpy(flash+sector*512, buffer, nsect*512); ?
? ? } ?
} ?
??
/*塊設(shè)備請(qǐng)求處理函數(shù)*/ ?
static void blk_request_func(struct request_queue *q) ?
{ ?
? ? struct request *req; ?
? ? while((req = elv_next_request(q)) != NULL) ? ?
? ? { ?
? ? ? ? if(!blk_fs_request(req)) ?
? ? ? ? { ?
? ? ? ? ? ? end_request(req, 0); ?
? ? ? ? ? ? continue; ?
? ? ? ? } ?
? ? ? ? ??
? ? ? ? blk_transfer(req->sector, req->current_nr_sectors, req->buffer, rq_data_dir(req)); ?
? ? ? ? /*rq_data_dir從request獲得數(shù)據(jù)傳送的方向*/ ?
? ? ? ? /*req->current_nr_sectors 在當(dāng)前段中將完成的扇區(qū)數(shù)*/ ?
? ? ? ? /*req->sector 將提交的下一個(gè)扇區(qū)*/ ?
? ? ? ? end_request(req, 1); ?
? ? } ?
} ?
??
/*strcut block_device_operations*/ ?
static ?int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long?
arg) ?
{ ?
? ? ? ?return -ENOTTY; ?
} ?
??
static int blk_open (struct block_device *dev , fmode_t no) ?
{ ?
? ? printk("blk mount succeed\n"); ?
? ? return 0; ?
} ?
static int blk_release(struct gendisk *gd , fmode_t no) ?
{ ?
? ? printk("blk umount succeed\n"); ?
? ? return 0; ?
} ?
struct block_device_operations blk_ops= ?
{ ?
? ? .owner = THIS_MODULE, ?
? ? .open = blk_open, ?
? ? .release = blk_release, ?
? ? .ioctl = blk_ioctl, ?
}; ?
??
//----------------------------------------------- ?
??
static int __init block_module_init(void) ?
{ ?
? ? ??
? ? ??
? ? if(!register_blkdev(BLK_MAJOR, blk_dev_name)) //注冊(cè)一個(gè)塊設(shè)備 ?
? ? { ?
? ? ? ? major = BLK_MAJOR; ? ?
? ? ? ? printk("regiser blk dev succeed\n"); ?
? ? } ?
? ? else ?
? ? { ?
? ? ? ? return -EBUSY; ?
? ? } ?
? ? gd = alloc_disk(1); ?//分配一個(gè)gendisk,分區(qū)是一個(gè) ?
? ? spin_lock_init(&lock); //初始化一個(gè)自旋鎖 ?
? ? gd->major = major; ?
? ? gd->first_minor = 0; ? //第一個(gè)次設(shè)備號(hào) ?
? ? gd->fops = &blk_ops; ? //關(guān)聯(lián)操作函數(shù) ?
??
? ? gd->queue = blk_init_queue(blk_request_func, &lock); //初始化請(qǐng)求隊(duì)列并關(guān)聯(lián)到gendisk ?
??
? ? snprintf(gd->disk_name, 32, "blk%c", 'a'); ? ?
? ? blk_queue_hardsect_size(gd->queue, 512); ?//設(shè)置扇區(qū)大小512字節(jié) ?
? ? set_capacity(gd, 32); ?//設(shè)置塊設(shè)備大小 512*32=16K ?
? ? add_disk(gd); ?
? ? printk("gendisk init success!\n"); ?
? ? return 0; ?
} ?
static void __exit block_module_exit(void) ?
{ ?
? ? blk_cleanup_queue(gd->queue); ?
? ? del_gendisk(gd); ??
? ? unregister_blkdev(BLK_MAJOR, blk_dev_name); ?
? ? printk("block module exit succeed!\n"); ?
} ?
??
module_init(block_module_init); ?
module_exit(block_module_exit); ?
??
MODULE_LICENSE("GPL"); ?
MODULE_AUTHOR("gec");?
========
linux之塊設(shè)備驅(qū)動(dòng)
? ? Linux操作系統(tǒng)有兩類主要的設(shè)備文件:
1.字符設(shè)備:以字節(jié)為單位進(jìn)行順序I/O操作的設(shè)備,無(wú)需緩沖區(qū)且被直接讀寫(xiě)。
2.塊設(shè)備:只能以塊單位接收輸入返回,對(duì)于I/O請(qǐng)求有對(duì)應(yīng)的緩沖區(qū),可以隨機(jī)訪問(wèn),塊設(shè)備的訪問(wèn)位
置必須能夠在介質(zhì)的不同區(qū)間前后移動(dòng)。在塊設(shè)備中,最小的可尋址單元是扇區(qū),扇區(qū)的大小一般是2的
整數(shù)倍,常見(jiàn)的大小為512個(gè)字節(jié)。
? ?上圖是一個(gè)塊設(shè)備操作的分層實(shí)現(xiàn)圖
1.當(dāng)一個(gè)進(jìn)程被Read時(shí),內(nèi)核會(huì)通過(guò)VFS層去讀取要讀的文件塊有沒(méi)有被cache了,這個(gè)cache由一個(gè)
buffer_head結(jié)構(gòu)讀取。如果要讀取的文件塊還沒(méi)有被cache,則就要從文件系統(tǒng)中去讀取,通過(guò)一個(gè)
address_space結(jié)構(gòu)來(lái)引用,如果調(diào)用文件系統(tǒng)讀取函數(shù)去讀取一個(gè)扇區(qū)的數(shù)據(jù)。當(dāng)它從磁盤(pán)讀出數(shù)據(jù)時(shí)
,將數(shù)據(jù)頁(yè)連入到cache中,當(dāng)下一次在讀取時(shí),就不需要從磁盤(pán)去讀取了。Read完后將請(qǐng)求初始化成一
個(gè)bio結(jié)構(gòu),并提交給通用塊層。
2.它通過(guò)submit_bio()去完成,通用層在調(diào)用相應(yīng)的設(shè)備IO調(diào)度器,這個(gè)調(diào)度器的調(diào)度算法,將這個(gè)bio
合并到已經(jīng)存在的request中,或者創(chuàng)建一個(gè)新的request,并將創(chuàng)建的插入到請(qǐng)求隊(duì)列中,最后就剩下
塊設(shè)備驅(qū)動(dòng)層來(lái)完成后面的所有工作。
內(nèi)核中塊得I/O操作的是由bio結(jié)構(gòu)表示的
點(diǎn)擊(此處)折疊或打開(kāi)
struct bio {
? ? sector_t ? ? ? ?bi_sector; ? ?/*該BIO結(jié)構(gòu)所要傳輸?shù)牡谝粋€(gè)(512字節(jié))扇區(qū)*/
? ? struct bio ? ? ? ?*bi_next; ? ?/*請(qǐng)求鏈表*/
? ? struct block_device ? ?*bi_bdev;/*相關(guān)的塊設(shè)備*/
? ? unsigned long ? ? ? ?bi_flags; ? ?/*狀態(tài)和命令的標(biāo)志*/
? ? unsigned long ? ? ? ?bi_rw; ? ? ? ?/*讀寫(xiě)*/
? ? unsigned short ? ? ? ?bi_vcnt; ? ?/* bio_vec的偏移個(gè)數(shù) */
? ? unsigned short ? ? ? ?bi_idx; ? ? ? ?/* bvl_vec */
? ? unsigned short ? ? ? ?bi_phys_segments;
? ? /* Number of segments after physical and DMA remapping
? ? ?* hardware coalescing is performed.
? ? ?*/
? ? unsigned short ? ? ? ?bi_hw_segments;
? ? unsigned int ? ? ? ?bi_size; ? ?/* residual I/O count */
? ? /*
? ? ?* To keep track of the max hw size, we account for the
? ? ?* sizes of the first and last virtually mergeable segments
? ? ?* in this bio
? ? ?*/
? ? unsigned int ? ? ? ?bi_hw_front_size;
? ? unsigned int ? ? ? ?bi_hw_back_size;
? ? unsigned int ? ? ? ?bi_max_vecs; ? ?/* max bvl_vecs we can hold */
? ? struct bio_vec ? ? ? ?*bi_io_vec; ? ?/* the actual vec list */
? ? bio_end_io_t ? ? ? ?*bi_end_io;
? ? atomic_t ? ? ? ?bi_cnt; ? ? ? ?/* pin count */
? ? void ? ? ? ? ? ?*bi_private;
? ? bio_destructor_t ? ?*bi_destructor; ? ?/* destructor */
};
此結(jié)構(gòu)體的目的主要是正在執(zhí)行的I/O操作,其中的bi_io_vecs、bi_vcnt、bi_idx三者都可以相互找到
。bio_vec描述一個(gè)特定的片段,片段所在的物理頁(yè),塊在物理頁(yè)中的偏移頁(yè),整個(gè)bio_io_vec結(jié)構(gòu)表示
一個(gè)完整的緩沖區(qū)。
點(diǎn)擊(此處)折疊或打開(kāi)
struct bio_vec {
? ? struct page ? ?*bv_page;
? ? unsigned int ? ?bv_len;
? ? unsigned int ? ?bv_offset;
};
當(dāng)一個(gè)塊被調(diào)用內(nèi)存時(shí),要儲(chǔ)存在一個(gè)緩沖區(qū),每個(gè)緩沖區(qū)與一個(gè)塊對(duì)應(yīng),所以每一個(gè)緩沖區(qū)獨(dú)有一個(gè)
對(duì)應(yīng)的描述符,該描述符用buffer_head結(jié)構(gòu)表示
點(diǎn)擊(此處)折疊或打開(kāi)
struct buffer_head {
? ? unsigned long b_state; ? ? ? ?/* buffer state bitmap (see above) */
? ? struct buffer_head *b_this_page;/* circular list of page's buffers */
? ? struct page *b_page; ? ? ? ?/* the page this bh is mapped to */
? ? sector_t b_blocknr; ? ? ? ?/* start block number */
? ? size_t b_size; ? ? ? ? ? ?/* size of mapping */
? ? char *b_data; ? ? ? ? ? ?/* pointer to data within the page */
? ? struct block_device *b_bdev;
? ? bh_end_io_t *b_end_io; ? ? ? ?/* I/O completion */
? ? ?void *b_private; ? ? ? ?/* reserved for b_end_io */
? ? struct list_head b_assoc_buffers; /* associated with another mapping */
? ? struct address_space *b_assoc_map; ? ?/* mapping this buffer is
? ? ? ? ? ? ? ? ? ? ? ? ?associated with */
? ? atomic_t b_count; ? ? ? ?/* users using this buffer_head */
};
下面來(lái)看看塊設(shè)備的核心ll_rw_block函數(shù)
點(diǎn)擊(此處)折疊或打開(kāi)
void ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
{
? ? int i;
? ? for (i = 0; i < nr; i ) {
? ? ? ? struct buffer_head *bh = bhs[i];
? ? ? ? if (!trylock_buffer(bh))
? ? ? ? ? ? continue;
? ? ? ? if (rw == WRITE) {
? ? ? ? ? ? if (test_clear_buffer_dirty(bh)) {
? ? ? ? ? ? ? ? bh->b_end_io = end_buffer_write_sync;
? ? ? ? ? ? ? ? get_bh(bh);
? ? ? ? ? ? ? ? submit_bh(WRITE, bh);
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? if (!buffer_uptodate(bh)) {
? ? ? ? ? ? ? ? bh->b_end_io = end_buffer_read_sync;
? ? ? ? ? ? ? ? get_bh(bh);
? ? ? ? ? ? ? ? submit_bh(rw, bh);
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? unlock_buffer(bh);
? ? }
}
請(qǐng)求塊設(shè)備驅(qū)動(dòng)將多個(gè)物理塊讀出或者寫(xiě)到塊設(shè)備,塊設(shè)備的讀寫(xiě)都是在塊緩沖區(qū)中進(jìn)行。
點(diǎn)擊(此處)折疊或打開(kāi)
int submit_bh(int rw, struct buffer_head * bh)
{
? ? struct bio *bio;
? ? int ret = 0;
? ? BUG_ON(!buffer_locked(bh));
? ? BUG_ON(!buffer_mapped(bh));
? ? BUG_ON(!bh->b_end_io);
? ? BUG_ON(buffer_delay(bh));
? ? BUG_ON(buffer_unwritten(bh));
? ? /*
? ? ?* Only clear out a write error when rewriting
? ? ?*/
? ? if (test_set_buffer_req(bh) && (rw & WRITE))
? ? ? ? clear_buffer_write_io_error(bh);
? ? /*
? ? ?* from here on down, it's all bio -- do the initial mapping,
? ? ?* submit_bio -> generic_make_request may further map this bio around
? ? ?*/
? ? bio = bio_alloc(GFP_NOIO, 1);
? ? bio->bi_sector = bh->b_blocknr * (bh->b_size >> 9);
? ? bio->bi_bdev = bh->b_bdev;
? ? bio->bi_io_vec[0].bv_page = bh->b_page;
? ? bio->bi_io_vec[0].bv_len = bh->b_size;
? ? bio->bi_io_vec[0].bv_offset = bh_offset(bh);
? ? bio->bi_vcnt = 1;
? ? bio->bi_idx = 0;
? ? bio->bi_size = bh->b_size;
? ? bio->bi_end_io = end_bio_bh_io_sync;
? ? bio->bi_private = bh;
? ? bio_get(bio);
? ? submit_bio(rw, bio);
? ? if (bio_flagged(bio, BIO_EOPNOTSUPP))
? ? ? ? ret = -EOPNOTSUPP;
? ? bio_put(bio);
? ? return ret;
這個(gè)函數(shù)主要是調(diào)用submit_bio,最終調(diào)用generic_make_request去完成將bio傳遞給驅(qū)動(dòng)去處理。
點(diǎn)擊(此處)折疊或打開(kāi)
void generic_make_request(struct bio *bio)
{
? ? struct bio_list bio_list_on_stack;
? ? if (!generic_make_request_checks(bio))
? ? ? ? return;
? ? if (current->bio_list) {
? ? ? ? bio_list_add(current->bio_list, bio);
? ? ? ? return;
? ? }
? ? BUG_ON(bio->bi_next);
? ? bio_list_init(&bio_list_on_stack);
? ? current->bio_list = &bio_list_on_stack;
? ? do {
? ? ? ? struct request_queue *q = bdev_get_queue(bio->bi_bdev);
? ? ? ? q->make_request_fn(q, bio);
? ? ? ? bio = bio_list_pop(current->bio_list);
? ? } while (bio);
? ? current->bio_list = NULL; /* deactivate */
}
這個(gè)函數(shù)主要是取出塊設(shè)備相應(yīng)的隊(duì)列中的每個(gè)設(shè)備,在調(diào)用塊設(shè)備驅(qū)動(dòng)的make_request,如果沒(méi)有指
定make_request就調(diào)用內(nèi)核默認(rèn)的__make_request,這個(gè)函數(shù)主要作用就是調(diào)用I/O調(diào)度算法將bio合并
,或插入到隊(duì)列中合適的位置中去。
整個(gè)流程為:
那么request_fn指向那個(gè)函數(shù)呢?在內(nèi)核中搜搜request_fn,發(fā)現(xiàn)
request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
{
? ? return blk_init_queue_node(rfn, lock, -1);
}
EXPORT_SYMBOL(blk_init_queue);
request_queue_t *
blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
{
? ? request_queue_t *q = blk_alloc_queue_node(GFP_KERNEL, node_id);
? ? if (!q)
? ? ? ? return NULL;
? ? q->node = node_id;
? ? if (blk_init_free_list(q)) {
? ? ? ? kmem_cache_free(requestq_cachep, q);
? ? ? ? return NULL;
? ? }
? ? if (!lock) {
? ? ? ? spin_lock_init(&q->__queue_lock);
? ? ? ? lock = &q->__queue_lock;
? ? }
? ? q->request_fn ? ? ? ?= rfn;
? ? q->prep_rq_fn ? ? ? ?= NULL;
? ? q->unplug_fn ? ? ? ?= generic_unplug_device;
? ? q->queue_flags ? ? ? ?= (1 << QUEUE_FLAG_CLUSTER);
? ? q->queue_lock ? ? ? ?= lock;
? ? blk_queue_segment_boundary(q, 0xffffffff);
? ? blk_queue_make_request(q, __make_request);
? ? blk_queue_max_segment_size(q, MAX_SEGMENT_SIZE);
? ? blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);
? ? blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);
? ? q->sg_reserved_size = INT_MAX;
? ? /*
? ? ?* all done
? ? ?*/
? ? if (!elevator_init(q, NULL)) {
? ? ? ? blk_queue_congestion_threshold(q);
? ? ? ? return q;
? ? }
? ? blk_put_queue(q);
? ? return NULL;
}
原來(lái)request_queue的make_request_fn指向__make_request()函數(shù),這個(gè)函數(shù)復(fù)雜I/O調(diào)度,并對(duì)bio做
些合并等。下面來(lái)看看__make_request()做了些什么?
由上面可以分析得出,其中有一個(gè)很重要的結(jié)構(gòu)體
struct request {
? ? struct list_head queuelist;//連接這個(gè)請(qǐng)求到請(qǐng)求隊(duì)列.?
//追蹤請(qǐng)求硬件完成的扇區(qū)的成員. 第一個(gè)尚未被傳送的扇區(qū)被存儲(chǔ)到 hard_sector, 已經(jīng)傳送的扇區(qū)
總數(shù)在 ha//rd_nr_sectors, 并且在當(dāng)前 bio 中剩余的扇區(qū)數(shù)是 hard_cur_sectors. 這些成員打算只
用在塊子系統(tǒng); 驅(qū)動(dòng)//不應(yīng)當(dāng)使用它們.
? ? struct request_queue *q;
? ? sector_t hard_sector;?
? ? unsigned long hard_nr_sectors;?
? ? unsigned int hard_cur_sectors;
? ? struct bio *bio;//bio 是給這個(gè)請(qǐng)求的 bio 結(jié)構(gòu)的鏈表. 你不應(yīng)當(dāng)直接存取這個(gè)成員; 使用?
rq_for_each_bio(后面描述) 代替.
? ? unsigned short nr_phys_segments;//被這個(gè)請(qǐng)求在物理內(nèi)存中占用的獨(dú)特段的數(shù)目, 在鄰近頁(yè)已
被合并后
? ? char *buffer;//隨著深入理解,可見(jiàn)到這個(gè)成員僅僅是在當(dāng)前 bio 上調(diào)用 bio_data 的結(jié)果.
};
request_queue只是一個(gè)請(qǐng)求隊(duì)列,通過(guò)可以找到requeue,然后通過(guò)bio結(jié)構(gòu)體對(duì)應(yīng)的page讀取物理內(nèi)存
中的信息。
下面看看內(nèi)核使用的塊設(shè)備的例子
static int jsfd_init(void)
{
? ? static DEFINE_SPINLOCK(lock);
? ? struct jsflash *jsf;
? ? struct jsfd_part *jdp;
? ? int err;
? ? int i;
? ? if (jsf0.base == 0)
? ? ? ? return -ENXIO;
? ? err = -ENOMEM;
//1. 分配gendisk: alloc_disk
? ? for (i = 0; i < JSF_MAX; i++) {
? ? ? ? struct gendisk *disk = alloc_disk(1);
? ? ? ? if (!disk)
? ? ? ? ? ? goto out;
? ? ? ? jsfd_disk[i] = disk;
? ? }
//2. 設(shè)置,分配/設(shè)置隊(duì)列: request_queue_t ?// 它提供讀寫(xiě)能力
? ? if (register_blkdev(JSFD_MAJOR, "jsfd")) {
? ? ? ? err = -EIO;
? ? ? ? goto out;
? ? }
? ? jsf_queue = blk_init_queue(jsfd_do_request, &lock);
? ? if (!jsf_queue) {
? ? ? ? err = -ENOMEM;
? ? ? ? unregister_blkdev(JSFD_MAJOR, "jsfd");
? ? ? ? goto out;
? ? }
//3. 設(shè)置gendisk其他信息 ? ? ? ? ? ? // 它提供屬性: 比如容量
? ? for (i = 0; i < JSF_MAX; i++) {
? ? ? ? struct gendisk *disk = jsfd_disk[i];
? ? ? ? if ((i & JSF_PART_MASK) >= JSF_NPART) continue;
? ? ? ? jsf = &jsf0; ? ?/* actually, &jsfv[i >> JSF_PART_BITS] */
? ? ? ? jdp = &jsf->dv[i&JSF_PART_MASK];
? ? ? ? disk->major = JSFD_MAJOR;
? ? ? ? disk->first_minor = i;
? ? ? ? sprintf(disk->disk_name, "jsfd%d", i);
? ? ? ? disk->fops = &jsfd_fops;
? ? ? ? set_capacity(disk, jdp->dsize >> 9);
? ? ? ? disk->private_data = jdp;
? ? ? ? disk->queue = jsf_queue;
//4 注冊(cè): add_disk
? ? ? ? add_disk(disk);
? ? ? ? set_disk_ro(disk, 1);
? ? }
? ? return 0;
out:
? ? while (i--)
? ? ? ? put_disk(jsfd_disk[i]);
? ? return err;
}
========
Linux 塊設(shè)備驅(qū)動(dòng) 實(shí)例
?任務(wù):用一片虛擬地址連續(xù)的內(nèi)存空間模擬一個(gè)塊設(shè)備,并為其寫(xiě)一個(gè)驅(qū)動(dòng)
/*
?* Sample disk driver, from the beginning.
?*/
#include <linux/autoconf.h>
#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 sbull_major = 0;
module_param(sbull_major, int, 0);
static int hardsect_size = 512;
module_param(hardsect_size, int, 0);
static int nsectors = 25600; ? ?/* How big the drive is */
module_param(nsectors, int, 0);
static int ndevices = 1;
module_param(ndevices, int, 0);
/*
?* The different "request modes" we can use.
?*/
enum {
? ? RM_SIMPLE ?= 0, ? ?/* The extra-simple request function */
? ? RM_FULL ? ?= 1, ? ?/* The full-blown version */
? ? RM_NOQUEUE = 2, ? ?/* Use make_request */
};
//static int request_mode = RM_FULL;
static int request_mode = RM_SIMPLE;
//static int request_mode = RM_SIMPLE;
module_param(request_mode, int, 0);
/*
?* Minor number and partition management.
?*/
#define SBULL_MINORS ? ?16
#define MINOR_SHIFT ? ?4
#define DEVNUM(kdevnum) ? ?(MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT
/*
?* We can tweak our hardware sector size, but the kernel talks to us
?* in terms of small sectors, always.
?*/
#define KERNEL_SECTOR_SIZE ? ?512
/*
?* After this much idle time, the driver will simulate a media change.
?*/
#define INVALIDATE_DELAY ? ?60*HZ
/*
?* The internal representation of our device.
?*/
struct sbull_dev {
? ? ? ? int size; ? ? ? ? ? ? ? ? ? ? ? /* Device size in sectors */
? ? ? ? // data 是本程序模擬的塊設(shè)備,是一片連續(xù)的虛擬空間
? ? ? ? // 在初始化函數(shù)里分配的虛擬地址連續(xù)的內(nèi)存空間
? ? ? ? u8 *data; ? ? ? ? ? ? ? ? ? ? ? /* The data array */
? ? ? ? short users; ? ? ? ? ? ? ? ? ? ?/* How many users */
? ? ? ? short media_change; ? ? ? ? ? ? /* Flag a media change? */
? ? ? ? spinlock_t lock; ? ? ? ? ? ? ? ?/* For mutual exclusion */
? ? ? ? struct request_queue *queue; ? ?/* The device request queue */
? ? ? ? struct gendisk *gd; ? ? ? ? ? ? /* The gendisk structure */
? ? ? ? struct timer_list timer; ? ? ? ?/* For simulated media changes */
};
static struct sbull_dev *Devices = NULL;
/*
?* Handle an I/O request.
?*/
static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,
? ? ? ? unsigned long nsect, char *buffer, int write)
{
? ? unsigned long offset = sector*KERNEL_SECTOR_SIZE; ? ? // 需要讀寫(xiě)的扇區(qū)的偏移地址
? ? unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE; ? ? ? ?// 需要讀寫(xiě)的字節(jié)數(shù)
? ??
? ? if ((offset + nbytes) > dev->size) { ? ? ?// 判斷輸入?yún)?shù)是否合法,是否超出邊界
? ? ? ? printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
? ? ? ? return;
? ? }
? ? // 實(shí)際的讀寫(xiě)操作
? ? // 由于本程序是用一片連續(xù)的內(nèi)存空間模擬塊設(shè)備
? ? // 所以這里對(duì)硬件(內(nèi)存空間)的讀寫(xiě)操作,就是復(fù)制內(nèi)存
? ? // 在具體點(diǎn),就是下面的memcpy
? ? // 具體的項(xiàng)目,需修改為具體的接口函數(shù)
? ? if (write)
? ? ? ? // 寫(xiě)
? ? ? ? memcpy(dev->data + offset, buffer, nbytes);
? ? else
? ? ? ? // 讀
? ? ? ? memcpy(buffer, dev->data + offset, nbytes);
}
/*The simple form of the request function.*/
static void sbull_request(struct request_queue *q)
{
? ? struct request *req;
? ? // 服務(wù)完隊(duì)列上的所有請(qǐng)求
? ? while ((req = elv_next_request(q)) != NULL) { ?// elv_next_request :從隊(duì)列上去一個(gè)下來(lái)
? ? ? ? struct sbull_dev *dev = req->rq_disk->private_data;
? ? ? ? if (! blk_fs_request(req)) {
? ? ? ? ? ? printk (KERN_NOTICE "Skip non-fs request\n");
? ? ? ? ? ? end_request(req, 0);
? ? ? ? ? ? continue;
? ? ? ? }
? ? ? ? sbull_transfer(dev, req->sector, req->current_nr_sectors,
? ? ? ? ? ? ? ? req->buffer, rq_data_dir(req));
? ? ? ? end_request(req, 1);
? ? }
}
/*
?* Transfer a single BIO.
?*/
static int sbull_xfer_bio(struct sbull_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);
? ? ? ? sbull_transfer(dev, sector, bio_cur_sectors(bio),
? ? ? ? ? ? ? ? buffer, bio_data_dir(bio) == WRITE);
? ? ? ? sector += bio_cur_sectors(bio);
? ? ? ? __bio_kunmap_atomic(bio, KM_USER0);
? ? }
? ? return 0; /* Always "succeed" */
}
/*
?* Transfer a full request.
?*/
static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
{
? ? struct bio *bio;
? ? int nsect = 0;
? ? // steps through each bio that makes up a request.
? ? // 遍歷
? ? __rq_for_each_bio(bio, req) {
? ? ? ? sbull_xfer_bio(dev, bio);
? ? ? ? nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
? ? }
? ? return nsect;
}
/*
?* Smarter request function that "handles clustering".
?*/
static void sbull_full_request(struct request_queue *q)
{
? ? struct request *req;
? ? int sectors_xferred;
? ? struct sbull_dev *dev = q->queuedata;
? ? printk("<0>""in %s\n",__FUNCTION__);
? ? while ((req = elv_next_request(q)) != NULL) {
? ? ? ? if (! blk_fs_request(req)) {
? ? ? ? ? ? printk (KERN_NOTICE "Skip non-fs request\n");
? ? ? ? ? ? end_request(req, 0);
? ? ? ? ? ? continue;
? ? ? ? }
? ? ? ? sectors_xferred = sbull_xfer_request(dev, req);
? ? ? ? __blk_end_request(req,0,sectors_xferred<<9);//add by lht for 2.6.27
? ? }
}
?
//The direct make request version
static int sbull_make_request(struct request_queue *q, struct bio *bio)
{
? ? struct sbull_dev *dev = q->queuedata;
? ? int status;
? ? status = sbull_xfer_bio(dev, bio);
? ? //bio_endio(bio, bio->bi_size, status);
? ? bio_endio(bio, status);
? ? return 0;
}
/*
?* Open and close.
?*/
static int sbull_open(struct inode *inode, struct file *filp)
{
? ? struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
? ? //printk("<0>" "fdfjdlksjfdlkj\n"); ? ?
? ? del_timer_sync(&dev->timer);
? ? filp->private_data = dev;
? ? spin_lock(&dev->lock);
? ? if (! dev->users)?
? ? ? ? check_disk_change(inode->i_bdev);
? ? dev->users++;
? ? spin_unlock(&dev->lock);
? ? return 0;
}
static int sbull_release(struct inode *inode, struct file *filp)
{
? ? struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
? ? spin_lock(&dev->lock);
? ? dev->users--;
? ? if (!dev->users) {
? ? ? ? dev->timer.expires = jiffies + INVALIDATE_DELAY;
? ? ? ? add_timer(&dev->timer);
? ? }
? ? spin_unlock(&dev->lock);
? ? return 0;
}
/*
?* Look for a (simulated) media change.
?*/
int sbull_media_changed(struct gendisk *gd)
{
? ? struct sbull_dev *dev = gd->private_data;
? ??
? ? return dev->media_change;
}
/*
?* Revalidate. ?WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
?* with open. ?That needs to be reevaluated.
?*/
int sbull_revalidate(struct gendisk *gd)
{
? ? struct sbull_dev *dev = gd->private_data;
? ??
? ? if (dev->media_change) {
? ? ? ? dev->media_change = 0;
? ? ? ? memset (dev->data, 0, dev->size);
? ? }
? ? return 0;
}
/*
?* The "invalidate" function runs out of the device timer; it sets
?* a flag to simulate the removal of the media.
?*/
void sbull_invalidate(unsigned long ldev)
{
? ? struct sbull_dev *dev = (struct sbull_dev *) ldev;
? ? spin_lock(&dev->lock);
? ? if (dev->users || !dev->data)?
? ? ? ? printk (KERN_WARNING "sbull: timer sanity check failed\n");
? ? else
? ? ? ? dev->media_change = 1;
? ? spin_unlock(&dev->lock);
}
/*
?* The ioctl() implementation
?*/
int sbull_ioctl (struct inode *inode, struct file *filp,
? ? ? ? ? ? ? ? ?unsigned int cmd, unsigned long arg)
{
? ? long size;
? ? struct hd_geometry geo;
? ? struct sbull_dev *dev = filp->private_data;
? ? switch(cmd) {
? ? ? ? case HDIO_GETGEO:
? ? ? ? ? ? /*
? ? ? ? ?* Get geometry: since we are a virtual device, we have to make
? ? ? ? ?* up something plausible. ?So we claim 16 sectors, four heads,
? ? ? ? ?* and calculate the corresponding number of cylinders. ?We set the
? ? ? ? ?* start of data at sector four.
? ? ? ? ?*/
? ? ? ? //printk("<0>""-------------size=%d\n",size);
? ? ? ? /****************for early version************/
? ? ? ? //size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
? ? ? ? //printk("<0>""-------------size=%d\n",size);
? ? ? ? //geo.cylinders = (size & ~0x3f) >> 6;
? ? ? ? //geo.cylinders=2000;
? ? ? ? //geo.heads = 4;
? ? ? ? //geo.sectors = 16;
? ? ? ? //geo.sectors=2560;
? ? ? ? //geo.start = 0;
? ? ? ? //if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
? ? ? ? // ? ?return -EFAULT;
? ? ? ? return 0;
? ? }
? ? return -ENOTTY; /* unknown command */
}
static int sbull_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
? ? unsigned long size;
? ? struct sbull_dev *pdev = bdev->bd_disk->private_data;
? ? size = pdev->size;
? ? geo->cylinders = (size & ~0x3f) >> 6;
? ? geo->heads ? ?= 4;
? ? geo->sectors = 16;
? ? geo->start = 0;
? ? return 0;
}
/*
?* The device operations structure.
?*/
static struct block_device_operations sbull_ops = {
? ? .owner ? ? ? ? ? = THIS_MODULE,
? ? .open ? ? ? ? ? ? ?= sbull_open,
? ? .release ? ? ?= sbull_release,
? ? .media_changed ? = sbull_media_changed,
? ? .revalidate_disk = sbull_revalidate,
? ? .ioctl ? ? ? ? ? ? = sbull_ioctl,
? ? .getgeo ? ? ? ? ? ?= sbull_getgeo,
};
/*
?* Set up our internal device.
?*/
// 初始化設(shè)備結(jié)構(gòu)體 static struct sbull_dev *Devices中的成員
static void setup_device(struct sbull_dev *dev, int which)
{
? ? /*
? ? ?* Get some memory.
? ? ?*/
? ? memset (dev, 0, sizeof (struct sbull_dev));
? ? dev->size = nsectors*hardsect_size;
? ? // 分配一片虛擬地址連續(xù)的內(nèi)存空間,作為塊設(shè)備。
? ? dev->data = vmalloc(dev->size); ??
? ? if (dev->data == NULL) {
? ? ? ? printk (KERN_NOTICE "vmalloc failure.\n");
? ? ? ? return;
? ? }
? ? spin_lock_init(&dev->lock);
? ??
? ? /*
? ? ?* The timer which "invalidates" the device.
? ? ?*/
? ? init_timer(&dev->timer);
? ? dev->timer.data = (unsigned long) dev;
? ? dev->timer.function = sbull_invalidate;
? ??
? ? /*
? ? ?* The I/O queue, depending on whether we are using our own
? ? ?* make_request function or not.
? ? ?*/
? ? switch (request_mode) {
? ? ? ? case RM_NOQUEUE:
? ? ? ? dev->queue = blk_alloc_queue(GFP_KERNEL);
? ? ? ? if (dev->queue == NULL)
? ? ? ? ? ? goto out_vfree;
? ? ? ? blk_queue_make_request(dev->queue, sbull_make_request);
? ? ? ? break;
? ? ? ? case RM_FULL:
? ? ? ? dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
? ? ? ? if (dev->queue == NULL)
? ? ? ? ? ? goto out_vfree;
? ? ? ? break;
? ? ? ? default:
? ? ? ? printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);
? ? ? ? ? ? /* fall into.. */
? ??
? ? ? ? case RM_SIMPLE:
? ? ? ? dev->queue = blk_init_queue(sbull_request, &dev->lock);
? ? ? ? if (dev->queue == NULL)
? ? ? ? ? ? goto out_vfree;
? ? ? ? break;
? ? }
? ? blk_queue_hardsect_size(dev->queue, hardsect_size);
? ? dev->queue->queuedata = dev;
? ? /*
? ? ?* And the gendisk structure.
? ? ?*/
? ? dev->gd = alloc_disk(SBULL_MINORS);
? ? if (! dev->gd) {
? ? ? ? printk (KERN_NOTICE "alloc_disk failure\n");
? ? ? ? goto out_vfree;
? ? }
? ? dev->gd->major = sbull_major;
? ? dev->gd->first_minor = which*SBULL_MINORS;
? ? dev->gd->fops = &sbull_ops;
? ? dev->gd->queue = dev->queue;
? ? dev->gd->private_data = dev;
? ? snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
? ? set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
? ? add_disk(dev->gd);
? ? return;
? out_vfree:
? ? if (dev->data)
? ? ? ? vfree(dev->data);
}
?
static int __init sbull_init(void)
{
? ? int i;
? ? /*
? ? ?* Get registered.
? ? ?*/
? ? // ? ?printk("<0>" "add by lht\n");
? ? sbull_major = register_blkdev(sbull_major, "sbull");
? ? if (sbull_major <= 0) {
? ? ? ? printk(KERN_WARNING "sbull: unable to get major number\n");
? ? ? ? return -EBUSY;
? ? }
? ? /*
? ? ?* Allocate the device array, and initialize each one.
? ? ?*/
? ? Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
? ? if (Devices == NULL)
? ? ? ? goto out_unregister;
? ? for (i = 0; i < ndevices; i++)?
? ? ? ? setup_device(Devices + i, i);
? ??
? ? return 0;
? out_unregister:
? ? unregister_blkdev(sbull_major, "sbd");
? ? return -ENOMEM;
}
static void sbull_exit(void)
{
? ? int i;
? ? for (i = 0; i < ndevices; i++) {
? ? ? ? struct sbull_dev *dev = Devices + i;
? ? ? ? del_timer_sync(&dev->timer);
? ? ? ? if (dev->gd) {
? ? ? ? ? ? del_gendisk(dev->gd);
? ? ? ? ? ? put_disk(dev->gd);
? ? ? ? }
? ? ? ? if (dev->queue) {
? ? ? ? ? ? if (request_mode == RM_NOQUEUE)
? ? ? ? ? ? // ? ?blk_put_queue(dev->queue);
? ? ? ? ? ? kobject_put(&(dev->queue)->kobj);
? ? ? ? ? ? else
? ? ? ? ? ? ? ? blk_cleanup_queue(dev->queue);
? ? ? ? }
? ? ? ? if (dev->data)
? ? ? ? ? ? vfree(dev->data);
? ? }
? ? unregister_blkdev(sbull_major, "sbull");
? ? kfree(Devices);
}
? ??
module_init(sbull_init);
module_exit(sbull_exit);
?
測(cè)試方法:
# Makefile
ifeq ($(KERNELRELEASE),)
#KERNELDIR ?= /home/lht/kernel2.6/linux-2.6.14
KERNELDIR ?= /lib/modules/$(shell uname -r)/build M=$(PWD) modules
PWD := $(shell pwd)
modules:
? ? ? ? $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
? ? ? ? $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
? ? ? ? rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
? ? obj-m := sbull.o
endif
將模塊插入內(nèi)核(2.6.27)
root@linuxidc:/source/workplace/test/sbull_linuxidc# insmod sbull.ko?
用lsmod查看模塊是否成功插入內(nèi)核
root@linuxidc:/source/workplace/test/sbull_linuxidc# lsmod | grep sbu
sbull ? ? ? ? ? ? ? ? ?13452 ?0?
出現(xiàn)上面結(jié)果,說(shuō)明成功了
用ls查看/dev下是否有sbull設(shè)備
root@linuxidc:/source/workplace/test/sbull_linuxidc# ls /dev | grep sbu
sbulla
出現(xiàn)上面結(jié)果,說(shuō)明有了,如果沒(méi)有,用命令
mknod /dev/sbulla b 254 0
手動(dòng)創(chuàng)建
至此,已經(jīng)有一個(gè)塊設(shè)備了
下面用fdisk對(duì)虛擬塊設(shè)備分區(qū)
root@linuxidc:/source/workplace/test/sbull_linuxidc# fdisk /dev/sbulla?
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x14d0973f.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.
Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)
Command (m for help): n ? ? ? ?這里選擇n,新建
Command action
? ?e ? extended
? ?p ? primary partition (1-4) ?這里選p,主分區(qū)
p
Partition number (1-4): 1 ? 這里選1,第一個(gè)分區(qū)
First cylinder (1-400, default 1): 1
Last cylinder, +cylinders or +size{K,M,G} (1-400, default 400):?
Using default value 400
Command (m for help): w ?這里選w,保存并推出
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
接著將其格式化為ext2
root@linuxidc:/source/workplace/test/sbull_linuxidc# mkfs.ext2 /dev/sbulla1?
mke2fs 1.41.3 (12-Oct-2008)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
3200 inodes, 12792 blocks
639 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=13107200
2 block groups
8192 blocks per group, 8192 fragments per group
1600 inodes per group
Superblock backups stored on blocks:?
? ? ? ? 8193
Writing inode tables: done ? ? ? ? ? ? ? ? ? ? ? ? ? ?
Writing superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 29 mounts or
180 days, whichever comes first. ?Use tune2fs -c or -i to override.
新建一個(gè)文件夾,作為此處模擬的塊設(shè)備的掛載點(diǎn)
root@linuxidc:/source/workplace/test/sbull_linuxidc# ls /mnt/
hgfs ?initrd
root@linuxidc:/source/workplace/test/sbull_linuxidc# mkdir /mnt/sbulla1
掛載
root@linuxidc:/source/workplace/test/sbull_linuxidc# mount /dev/sbulla1 /mnt/sbulla1
進(jìn)入目錄,新建一個(gè)文件,測(cè)試一下
root@linuxidc:/source/workplace/test/sbull_linuxidc# cd /mnt/sbulla1/
root@linuxidc:/mnt/sbulla1# ls
lost+found
root@linuxidc:/mnt/sbulla1# echo hi > hello.c
root@linuxidc:/mnt/sbulla1# ls
hello.c ?lost+found
root@linuxidc:/mnt/sbulla1# cat hello.c?
hi
root@linuxidc:/mnt/sbulla1#
========
linux下的塊設(shè)備驅(qū)動(dòng)(一)
塊設(shè)備的驅(qū)動(dòng)比字符設(shè)備的難,這是因?yàn)閴K設(shè)備的驅(qū)動(dòng)和內(nèi)核的聯(lián)系進(jìn)一步增大,但是同時(shí)塊設(shè)備的訪問(wèn)的幾個(gè)基本結(jié)構(gòu)和字符還是有相似之處的。
有一句話必須記住:對(duì)于存儲(chǔ)設(shè)備(硬盤(pán)~~帶有機(jī)械的操作)而言,調(diào)整讀寫(xiě)的順序作用巨大,因?yàn)樽x
寫(xiě)連續(xù)的扇區(qū)比分離的扇區(qū)快。
但是同時(shí):SD卡和U盤(pán)這類設(shè)備沒(méi)有機(jī)械上的限制,所以像上面說(shuō)的進(jìn)行連續(xù)扇區(qū)的調(diào)整顯得就沒(méi)有必要
了。
?
先說(shuō)一下對(duì)于硬盤(pán)這類設(shè)備的簡(jiǎn)單的驅(qū)動(dòng)。
在linux的內(nèi)核中,使用gendisk結(jié)構(gòu)來(lái)表示一個(gè)獨(dú)立的磁盤(pán)設(shè)備或者分區(qū)。這個(gè)結(jié)構(gòu)中包含了磁盤(pán)的主
設(shè)備號(hào),次設(shè)備號(hào)以及設(shè)備名稱。
在國(guó)嵌給的歷程中,對(duì)gendisk這個(gè)結(jié)構(gòu)體的填充是在simp_blkdev_init函數(shù)中完成的。在對(duì)gendisk這
個(gè)結(jié)構(gòu)填充之前要對(duì)其進(jìn)行分配空間。具體代碼如下:
simp_blkdev_disk = alloc_disk(1);
? ? ? ? if (!simp_blkdev_disk) {
? ? ? ? ? ? ? ? ret = -ENOMEM;
? ? ? ? ? ? ? ? goto err_alloc_disk;
? ? ? ? }
這里的alloc_disk函數(shù)是在內(nèi)核中實(shí)現(xiàn)的,它后面的參數(shù)1代表的是使用次設(shè)備號(hào)的數(shù)量,這個(gè)數(shù)量是不
能被修改的。
在分配好了關(guān)于gendisk的空間以后就開(kāi)始對(duì)gendisk里面的成員進(jìn)行填充。具體代碼如下:
strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME); //宏定義simp_blkdev
? ? ? ? simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR; //主設(shè)備號(hào)
? ? ? ? simp_blkdev_disk->first_minor = 0; //次設(shè)備號(hào)
? ? ? ? simp_blkdev_disk->fops = &simp_blkdev_fops; //主要結(jié)構(gòu)
? ? ? ? simp_blkdev_disk->queue = simp_blkdev_queue;
? ? ? ? set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9); //宏定義(16*1024*1024),實(shí)際
上就是這個(gè)結(jié)構(gòu)體。
在填充好gendisk這個(gè)結(jié)構(gòu)以后向內(nèi)核中 注冊(cè)這個(gè)磁盤(pán)設(shè)備。具體代碼如下:
add_disk(simp_blkdev_disk);
在LDD中說(shuō),想內(nèi)核中注冊(cè)設(shè)備的必須在gendisk這個(gè)結(jié)構(gòu)體已經(jīng)填充好了以后,我們以前的字符設(shè)備的
時(shí)候也是這么做的,不知道為什么LDD在這里強(qiáng)調(diào)了這個(gè)。
當(dāng)不需要一個(gè)磁盤(pán)的時(shí)候要釋放gendisk,釋放部分的代碼在函數(shù)simp_blkdev_exit中實(shí)現(xiàn)的。具體的釋
放代碼如下:
del_gendisk(simp_blkdev_disk);
在simp_blkdev_exit中同時(shí)還有put_disk(simp_blkdev_disk),這個(gè)是用來(lái)進(jìn)行操作gendisk的引用計(jì)數(shù)
。simp_blkdev_exit還實(shí)現(xiàn)了blk_cleanup_queue清除請(qǐng)求隊(duì)列的這個(gè)函數(shù)。終于說(shuō)到請(qǐng)求隊(duì)列了。
?
在說(shuō)等待隊(duì)列之前先要明確幾個(gè)概念:
①用戶希望對(duì)硬盤(pán)數(shù)據(jù)做的事情叫做請(qǐng)求,這個(gè)請(qǐng)求和IO請(qǐng)求是一樣的,所以IO請(qǐng)求來(lái)自于上層。
②每一個(gè)IO請(qǐng)求對(duì)應(yīng)內(nèi)核中的一個(gè)bio結(jié)構(gòu)。
③IO調(diào)度算法可以將連續(xù)的bio(也就是用戶的對(duì)硬盤(pán)數(shù)據(jù)的相鄰簇的請(qǐng)求)合并成一個(gè)request。
④多個(gè)request就是一個(gè)請(qǐng)求隊(duì)列,這個(gè)請(qǐng)求隊(duì)列的作用就是驅(qū)動(dòng)程序響應(yīng)用戶的需求的隊(duì)列。
?請(qǐng)求隊(duì)列在國(guó)嵌的程序中的simp_blkdev_queue
下面先說(shuō)一下硬盤(pán)這類帶有機(jī)械的存儲(chǔ)設(shè)備的驅(qū)動(dòng)。
這類驅(qū)動(dòng)中用戶的IO請(qǐng)求對(duì)應(yīng)于硬盤(pán)上的簇可能是連續(xù)的,可能是不連續(xù)的,連續(xù)的當(dāng)然好,如果要是
不連續(xù)的,那么IO調(diào)度器就會(huì)對(duì)這些BIO進(jìn)行排序(例如老謝說(shuō)的電梯調(diào)度算法),合并成一個(gè)request
,然后再接收請(qǐng)求,再合并成一個(gè)request,多個(gè)request之后那么我們的請(qǐng)求隊(duì)列就形成了,然后就可
以向驅(qū)動(dòng)程序提交了。
在硬盤(pán)這類的存儲(chǔ)設(shè)備中,請(qǐng)求隊(duì)列的初始化代碼如下:
simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL);
老謝說(shuō),在這種情況下首先調(diào)用的是內(nèi)核中的make_requst函數(shù),然后再調(diào)用自己定義的
simp_blkdev_do_request。追了一下內(nèi)核代碼,會(huì)發(fā)現(xiàn)make_requst內(nèi)核代碼如下所示:
static int make_request(struct request_queue *q, struct bio * bio)
?
具體的就不貼了,不過(guò)可以知道這個(gè)make_request的作用就是使用IO調(diào)度器對(duì)多個(gè)bio的訪問(wèn)順序進(jìn)行了
優(yōu)化調(diào)整合并為一個(gè)request。也就是在執(zhí)行完成了這個(gè)函數(shù)之后才去正式的執(zhí)行內(nèi)核的請(qǐng)求隊(duì)列。
合并后的request其實(shí)還是一個(gè)結(jié)構(gòu),這個(gè)結(jié)構(gòu)用來(lái)表征IO的請(qǐng)求,這個(gè)結(jié)構(gòu)在內(nèi)核中有具體的定義。
請(qǐng)求是一個(gè)結(jié)構(gòu),同時(shí)請(qǐng)求隊(duì)列也是一個(gè)結(jié)構(gòu),這個(gè)請(qǐng)求隊(duì)列在內(nèi)核中的結(jié)構(gòu)定義如下:
struct request_queue
{
/*
* Together with queue_head for cacheline sharing
*/
struct list_head queue_head;
struct request *last_merge;
struct elevator_queue *elevator;
/*
* the queue request freelist, one for reads and one for writes
*/
struct request_list rq;
request_fn_proc *request_fn;
make_request_fn *make_request_fn;
prep_rq_fn *prep_rq_fn;
unplug_fn *unplug_fn;
merge_bvec_fn *merge_bvec_fn;
prepare_flush_fn *prepare_flush_fn;
softirq_done_fn *softirq_done_fn;
rq_timed_out_fn *rq_timed_out_fn;
dma_drain_needed_fn *dma_drain_needed;
lld_busy_fn *lld_busy_fn;
/*
* Dispatch queue sorting
*/
sector_t end_sector;
struct request *boundary_rq;
/*
* Auto-unplugging state
*/
struct timer_list unplug_timer;
int unplug_thresh; /* After this many requests */
unsigned long unplug_delay; /* After this many jiffies */
struct work_struct unplug_work;
struct backing_dev_info backing_dev_info;
/*
* The queue owner gets to use this for whatever they like.
* ll_rw_blk doesn't touch it.
*/
void *queuedata;
/*
* queue needs bounce pages for pages above this limit
*/
gfp_t bounce_gfp;
/*
* various queue flags, see QUEUE_* below
*/
unsigned long queue_flags;
/*
* protects queue structures from reentrancy. ->__queue_lock should
* _never_ be used directly, it is queue private. always use
* ->queue_lock.
*/
spinlock_t __queue_lock;
spinlock_t *queue_lock;
/*
* queue kobject
*/
struct kobject kobj;
/*
* queue settings
*/
unsigned long nr_requests; /* Max # of requests */
unsigned int nr_congestion_on;
unsigned int nr_congestion_off;
unsigned int nr_batching;
void *dma_drain_buffer;
unsigned int dma_drain_size;
unsigned int dma_pad_mask;
unsigned int dma_alignment;
struct blk_queue_tag *queue_tags;
struct list_head tag_busy_list;
unsigned int nr_sorted;
unsigned int in_flight[2];
unsigned int rq_timeout;
struct timer_list timeout;
struct list_head timeout_list;
struct queue_limits limits;
/*
* sg stuff
*/
unsigned int sg_timeout;
unsigned int sg_reserved_size;
int node;
#ifdef CONFIG_BLK_DEV_IO_TRACE
struct blk_trace *blk_trace;
#endif
/*
* reserved for flush operations
*/
unsigned int ordered, next_ordered, ordseq;
int orderr, ordcolor;
struct request pre_flush_rq, bar_rq, post_flush_rq;
struct request *orig_bar_rq;
struct mutex sysfs_lock;
#if defined(CONFIG_BLK_DEV_BSG)
struct bsg_class_device bsg_dev;
#endif
};
LDD說(shuō),請(qǐng)求隊(duì)列實(shí)現(xiàn)了一個(gè)插入接口,這個(gè)接口允許使用多個(gè)IO調(diào)度器,大部分IO調(diào)度器批量累計(jì)IO請(qǐng)
求,并將它們排列為遞增或者遞減的順序提交給驅(qū)動(dòng)。
多個(gè)連續(xù)的bio會(huì)合并成為一個(gè)request,多個(gè)request就成為了一個(gè)請(qǐng)求隊(duì)列,這樣bio的是直接的也是
最基本的請(qǐng)求,bio這個(gè)結(jié)構(gòu)的定義如下:
struct bio { sector_t ? ? ? ? ? ?bi_sector;
? ? ? ?struct bio ? ? ? ? ?*bi_next; ? ?/* request queue link */
? ? ? ?struct block_device *bi_bdev; /* target device */
? ? ? ?unsigned long ? ? ? bi_flags; ? ?/* status, command, etc */ unsigned long ? ? ??
bi_rw; ? ? ? /* low bits: r/w, high: priority */
? ? ? ?unsigned int bi_vcnt; ? ? /* how may bio_vec's */
? ? ? ?unsigned int bi_idx; /* current index into bio_vec array */
? ? ? ?unsigned int bi_size; ? ? /* total size in bytes */
? ? ? ?unsigned short bi_phys_segments; /* segments after physaddr coalesce*/ unsigned?
short bi_hw_segments; /* segments after DMA remapping */ unsigned int bi_max; ? ? /* max?
bio_vecs we can hold used as index into pool */ struct bio_vec ? *bi_io_vec; ?/* the actual?
vec list */
? ? ? ?bio_end_io_t *bi_end_io; ?/* bi_end_io (bio) */
? ? ? ?atomic_t bi_cnt; ? ? /* pin count: free when it hits zero */ void ? ? ? ? ??
? *bi_private;
? ? ? ?bio_destructor_t *bi_destructor; /* bi_destructor (bio) */ };
需要注意的是,在bio這個(gè)結(jié)構(gòu)中最重要的是bio.vec這個(gè)結(jié)構(gòu)。同時(shí)還有許多操作bio的宏,這些都是內(nèi)
核給實(shí)現(xiàn)好了的。
請(qǐng)求隊(duì)列的實(shí)現(xiàn):
首先使用 while ((req = elv_next_request(q)) != NULL)進(jìn)行循環(huán)檢測(cè),看看到底傳來(lái)的IO請(qǐng)求是個(gè)
什么。
然后進(jìn)行讀寫(xiě)區(qū)域的判定:
if ((req->sector + req->current_nr_sectors) << 9
? ? ? ? ? ? ? ? ? ? ? ? > SIMP_BLKDEV_BYTES) {
? ? ? ? ? ? ? ? ? ? ? ? printk(KERN_ERR SIMP_BLKDEV_DISKNAME
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ": bad request: block=%llu, count=%u\n",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (unsigned long long)req->sector,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? req->current_nr_sectors);
//結(jié)束本次請(qǐng)求。
? ? ? ? ? ? ? ? ? ? ? ? end_request(req, 0);
? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? }
在進(jìn)行讀寫(xiě)區(qū)域的判定的時(shí)候涉及到了很多l(xiāng)inux的編程習(xí)慣。
sector表示要訪問(wèn)的第一個(gè)扇區(qū)。
current_nr_sectors表示預(yù)計(jì)訪問(wèn)扇區(qū)的數(shù)目。
這里的左移九位其實(shí)就是乘以512。
這樣((req->sector + req->current_nr_sectors) << 9就計(jì)算出可以預(yù)計(jì)要訪問(wèn)的扇區(qū)的大小。進(jìn)行了
一次判斷。
如果上面的判斷沒(méi)有超出范圍,那么就可以對(duì)請(qǐng)求的這一部分塊設(shè)備進(jìn)行操作了。
simp_blkdev_disk = alloc_disk(1);
? ? ? ? if (!simp_blkdev_disk) {
? ? ? ? ? ? ? ? ret = -ENOMEM;
? ? ? ? ? ? ? ? goto err_alloc_disk;
? ? ? ? }
? ? ? ? strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);
? ? ? ? simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;
? ? ? ? simp_blkdev_disk->first_minor = 0;
? ? ? ? simp_blkdev_disk->fops = &simp_blkdev_fops;
? ? ? ? simp_blkdev_disk->queue = simp_blkdev_queue;
? ? ? ? set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9);
? ? ? ? add_disk(simp_blkdev_disk);
? ? ? ? return 0;
關(guān)于gendisk結(jié)構(gòu)的內(nèi)存分配和成員的填充和硬盤(pán)類塊設(shè)備是一樣的。
由于SD卡和U盤(pán)屬于一類非機(jī)械類的設(shè)備,所以我們不需要那么復(fù)雜的調(diào)度算法,也就是不需要把io請(qǐng)求
進(jìn)行排序,所以我們需要自己為自己分配一個(gè)請(qǐng)求隊(duì)列。具體代碼如下:
?
simp_blkdev_queue = blk_alloc_queue(GFP_KERNEL);
需要注意一下的是在硬盤(pán)類塊設(shè)備的驅(qū)動(dòng)中這個(gè)函數(shù)的原型是blk_init_queue
(simp_blkdev_do_request, NULL);
在這種情況下其實(shí)并沒(méi)有調(diào)用內(nèi)核的make_request(這個(gè)函數(shù)的功能上文說(shuō)過(guò)),也就是說(shuō)我們接下來(lái)
要綁定的simp_blkdev_make_request這個(gè)函數(shù)和make_request是同一級(jí)別的函數(shù)。這里就沒(méi)有涉及到算
法調(diào)度的問(wèn)題了。
然后需要進(jìn)行的工作是:綁定制造請(qǐng)求函數(shù)和請(qǐng)求隊(duì)列。具體代碼如下:
blk_queue_make_request(simp_blkdev_queue, simp_blkdev_make_request);
把gendisk的結(jié)構(gòu)成員都添加好了以后就可以執(zhí)行add_disk(simp_blkdev_disk);這個(gè)函數(shù)把這塊分區(qū)添
加進(jìn)內(nèi)核了。
整體列出制造請(qǐng)求部分的函數(shù)。
//這個(gè)條件是在判斷當(dāng)前正在運(yùn)行的內(nèi)核版本。
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
? ? ? ? ? ? ? ? bio_endio(bio, 0, -EIO);
#else
? ? ? ? ? ? ? ? bio_endio(bio, -EIO);
#endif
? ? ? ? ? ? ? ? return 0;
? ? ? ? }
? ? ? ? dsk_mem = simp_blkdev_data + (bio->bi_sector << 9);
//遍歷
? ? ? ? bio_for_each_segment(bvec, bio, i) {
? ? ? ? ? ? ? ? void *iovec_mem;
? ? ? ? ? ? ? ? switch (bio_rw(bio)) {
? ? ? ? ? ? ? ? case READ:
? ? ? ? ? ? ? ? case READA:
? ? ? ? ? ? ? ? ? ? ? ? iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
? ? ? ? ? ? ? ? ? ? ? ? memcpy(iovec_mem, dsk_mem, bvec->bv_len);
? ? ? ? ? ? ? ? ? ? ? ? kunmap(bvec->bv_page);
? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case WRITE:
? ? ? ? ? ? ? ? ? ? ? ? iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
? ? ? ? ? ? ? ? ? ? ? ? memcpy(dsk_mem, iovec_mem, bvec->bv_len);
? ? ? ? ? ? ? ? ? ? ? ? kunmap(bvec->bv_page);
? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ? ? ? ? ? printk(KERN_ERR SIMP_BLKDEV_DISKNAME
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ": unknown value of bio_rw: %lu\n",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bio_rw(bio));
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
? ? ? ? ? ? ? ? ? ? ? ? bio_endio(bio, 0, -EIO);
#else
? ? ? ? ? ? ? ? ? ? ? ? bio_endio(bio, -EIO);
#endif
? ? ? ? ? ? ? ? ? ? ? ? return 0;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? dsk_mem += bvec->bv_len;
? ? ? ? }
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
? ? ? ? bio_endio(bio, bio->bi_size, 0);
#else
? ? ? ? bio_endio(bio, 0);
#endif
? ? ? ? return 0;
}
========
?Linux塊設(shè)備驅(qū)動(dòng)程序原理
?1.4 ?塊設(shè)備驅(qū)動(dòng)程序
1.4.1 ?Linux塊設(shè)備驅(qū)動(dòng)程序原理(1)
顧名思義,塊設(shè)備驅(qū)動(dòng)程序就是支持以塊的方式進(jìn)行讀寫(xiě)的設(shè)備。塊設(shè)備和字符設(shè)備最大的區(qū)別在于讀
寫(xiě)數(shù)據(jù)的基本單元不同。塊設(shè)備讀寫(xiě)數(shù)據(jù)的基本單元為塊,例如磁盤(pán)通常為一個(gè)sector,而字符設(shè)備的
基本單元為字節(jié)。從實(shí)現(xiàn)角度來(lái)看,字符設(shè)備的實(shí)現(xiàn)比較簡(jiǎn)單,內(nèi)核例程和用戶態(tài)API一一對(duì)應(yīng),這種映
射關(guān)系由字符設(shè)備的file_operations維護(hù)。塊設(shè)備接口則相對(duì)復(fù)雜,讀寫(xiě)API沒(méi)有直接到塊設(shè)備層,而
是直接到文件系統(tǒng)層,然后再由文件系統(tǒng)層發(fā)起讀寫(xiě)請(qǐng)求。
block_device結(jié)構(gòu)代表了內(nèi)核中的一個(gè)塊設(shè)備。它可以表示整個(gè)磁盤(pán)或一個(gè)特定的分區(qū)。當(dāng)這個(gè)結(jié)構(gòu)代
表一個(gè)分區(qū)時(shí),它的bd_contains成員指向包含這個(gè)分區(qū)的設(shè)備,bd_part成員指向設(shè)備的分區(qū)結(jié)構(gòu)。當(dāng)
這個(gè)結(jié)構(gòu)代表一個(gè)塊設(shè)備時(shí),bd_disk成員指向設(shè)備的gendisk結(jié)構(gòu)。
struct block_device { ?
? ? dev_t ? ? ? ? ? bd_dev; ?
? ? struct inode * ?bd_inode; ? /*分區(qū)結(jié)點(diǎn)*/ ?
? ? int ? ? ? ? bd_openers; ?
? ? struct semaphore ? ?bd_sem; /*打開(kāi)/關(guān)閉鎖*/ ?
? ? struct semaphore ? ?bd_mount_sem; ? /* 加載互斥鎖*/ ?
? ? struct list_head ? ?bd_inodes; ?
? ? void * ? ? ?bd_holder; ?
? ? int ? ? ? ? bd_holders; ?
? ? struct block_device * ? bd_contains; ?
? ? unsigned ? ? ? ?bd_block_size;//分區(qū)塊大小 ?
? ? struct hd_struct * ?bd_part; ?
? ? unsigned ? ? ? ?bd_part_count;//打開(kāi)次數(shù) ?
? ? int ? ? ? ? bd_invalidated; ?
? ? struct gendisk * ? ?bd_disk; ?
? ? struct list_head ? ?bd_list; ?
? ? struct backing_dev_info *bd_inode_backing_dev_info; ?
? ? unsigned long ? bd_private; ?
}; ?
gendisk是一個(gè)單獨(dú)的磁盤(pán)驅(qū)動(dòng)器的內(nèi)核表示。內(nèi)核還使用gendisk來(lái)表示分區(qū)。
struct gendisk { ?
? ? int major; ? ? ? ? ?//主設(shè)備號(hào) ?
? ? int first_minor; ? ??
? ? int minors; ? ? ? ? //最大的次設(shè)備號(hào)數(shù)量,如果設(shè)備不能分區(qū),該值為1 ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ??
? ? char disk_name[32]; //主設(shè)備名 ?
? ? struct hd_struct **part; ? ?//分區(qū)信息,有minors個(gè) ?
? ? struct block_device_operations *fops;//設(shè)備操作 ?
? ? struct request_queue *queue; ? ?//設(shè)備管理I/O請(qǐng)求 ?
? ? void *private_data; ?
? ? sector_t capacity; ?
? ? int flags; ?
? ? char devfs_name[64]; ?
? ? int number; ?
? ? struct device *driverfs_dev; ?
? ? struct kobject kobj; ?
? ? struct timer_rand_state *random; ?
? ? int policy; ?
? ? atomic_t sync_io; ? ??
? ? unsigned long stamp, stamp_idle; ?
? ? int in_flight; ?
#ifdef ?CONFIG_SMP ?
? ? struct disk_stats *dkstats; ?
#else ?
? ? struct disk_stats dkstats; ?
#endif ?
}; ?
gendisk結(jié)構(gòu)的操作函數(shù)包括以下幾個(gè):
struct gendisk *alloc_disk(int minors); ? ? //分配磁盤(pán) ?
void add_disk(struct gendisk *disk); ? ? ? ?//增加磁盤(pán)信息 ?
void unlink_gendisk(struct gendisk *disk) ? //刪除磁盤(pán)信息 ?
void delete_partition(struct gendisk *disk, int part); ?//刪除分區(qū) ?
void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags)
;//添加分區(qū) ?
1.4.1 ?Linux塊設(shè)備驅(qū)動(dòng)程序原理(2)
block_device_operations結(jié)構(gòu)是塊設(shè)備對(duì)應(yīng)的操作接口,是連接抽象的塊設(shè)備操作與具體塊設(shè)備操作之
間的樞紐。
struct block_device_operations { ?
? ? int (*open) (struct inode *, struct file *); ?
? ? int (*release) (struct inode *, struct file *); ?
? ? int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); ?
? ? long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); ?
? ? long (*compat_ioctl) (struct file *, unsigned, unsigned long); ?
? ? int (*direct_access) (struct block_device *, sector_t, unsigned long *); ?
? ? int (*media_changed) (struct gendisk *); ?
? ? int (*revalidate_disk) (struct gendisk *); ?
? ? int (*getgeo)(struct block_device *, struct hd_geometry *); ?
? ? struct module *owner; ?
}; ?
block_device_operations并不能完全提供文件操作全部的API,實(shí)際上只提供了open、release等函數(shù),
其他的文件操作依賴于def_blk_fops:
const struct file_operations def_blk_fops = { ?
? ? .open ? = blkdev_open, ?
? ? .release ? ?= blkdev_close, ?
? ? .llseek = block_llseek, ?
? ? .read ? ? ? = do_sync_read, ?
? ? .write ?= do_sync_write, ?
? ? .aio_read ? = generic_file_aio_read, ?
? ? .aio_write= generic_file_aio_write_nolock, ?
? ? .mmap ? = generic_file_mmap, ?
? ? .fsync ?= block_fsync, ?
? ? .unlocked_ioctl = block_ioctl, ?
#ifdef CONFIG_COMPAT ?
? ? .compat_ioctl ? = compat_blkdev_ioctl, ?
#endif ?
? ? .splice_read ? ? ? ?= generic_file_splice_read, ?
? ? .splice_write ? = generic_file_splice_write, ?
}; ?
系統(tǒng)對(duì)塊設(shè)備進(jìn)行讀寫(xiě)操作時(shí),通過(guò)塊設(shè)備通用的讀寫(xiě)操作函數(shù)將一個(gè)請(qǐng)求保存在該設(shè)備的操作請(qǐng)求隊(duì)
列(request queue)中,然后調(diào)用這個(gè)塊設(shè)備的底層處理函數(shù),對(duì)請(qǐng)求隊(duì)列中的操作請(qǐng)求進(jìn)行逐一執(zhí)行
。request_queue結(jié)構(gòu)描述了塊設(shè)備的請(qǐng)求隊(duì)列,該結(jié)構(gòu)定義如下:
struct request_queue ?
{ ?
? ? struct list_head ? ?queue_head; ?
? ? struct request ? ? ?*last_merge; ?
? ? elevator_t ? ? ?elevator; ?
? ? /*請(qǐng)求隊(duì)列列表*/ ?
? ? struct request_list ? ? rq; ?
? ? request_fn_proc ? ? *request_fn; ?
? ? merge_request_fn ? ?*back_merge_fn; ?
? ? merge_request_fn ? ?*front_merge_fn; ?
? ? merge_requests_fn ? *merge_requests_fn; ?
? ? make_request_fn ? ? *make_request_fn; ?
? ? prep_rq_fn ? ? ? ? ?*prep_rq_fn; ?
? ? unplug_fn ? ? ? ? ? *unplug_fn; ?
? ? merge_bvec_fn ? ? ? *merge_bvec_fn; ?
? ? activity_fn ? ? ? ? *activity_fn; ?
? ? /*自動(dòng)卸載狀態(tài)*/ ?
? ? struct timer_list ? unplug_timer; ?
? ? int ? ? ? ? unplug_thresh; ? ?
? ? unsigned long ? ? ? unplug_delay; ? /*自動(dòng)卸載延時(shí)*/ ?
? ? struct work_struct ?unplug_work; ?
? ? struct backing_dev_info backing_dev_info; ?
? ? void ? ? ? ? ? ? ? ?*queuedata; ?
? ? void ? ? ? ? ? ? ? ?*activity_data; ?
? ? unsigned long ? ? ? bounce_pfn; ?
? ? int ? ? ? ? ? ? bounce_gfp; ?
? ? unsigned long ? ? ? queue_flags;//各種隊(duì)列標(biāo)志 ?
? ? /*保護(hù)隊(duì)列結(jié)構(gòu),避免重入*/ ?
? ? spinlock_t ? ? ? ? ?*queue_lock; ?
? ? /* 請(qǐng)求的核心結(jié)構(gòu)*/ ?
? ? struct kobject kobj; ?
? ? /*請(qǐng)求的配置*/ ?
? ? unsigned long ? ? ? nr_requests; ? ?/* 請(qǐng)求的最大數(shù)*/ ?
? ? unsigned int ? ? ? ?nr_congestion_on; ?
? ? unsigned int ? ? ? ?nr_congestion_off; ?
? ? unsigned short ? ? ?max_sectors; ?
? ? unsigned short ? ? ?max_phys_segments; ?
? ? unsigned short ? ? ?max_hw_segments; ?
? ? unsigned short ? ? ?hardsect_size; ?
? ? unsigned int ? ? ? ?max_segment_size; ?
? ? unsigned long ? ? ? seg_boundary_mask; ?
? ? unsigned int ? ? ? ?dma_alignment; ?
? ? struct blk_queue_tag ? ?*queue_tags; ?
? ? atomic_t ? ? ? ?refcnt; ?
? ? unsigned int ? ? ? ?in_flight; ?
? ? /*sg 參數(shù)配置*/ ?
? ? unsigned int ? ? ? ?sg_timeout; ?
? ? unsigned int ? ? ? ?sg_reserved_size; ?
}; ?
請(qǐng)求隊(duì)列相關(guān)的處理函數(shù)包括:
//創(chuàng)建隊(duì)列時(shí)提供了一個(gè)自旋鎖。 ?
request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock); ?
//獲得隊(duì)列中第一個(gè)未完成的請(qǐng)求。 ?
struct request *elv_next_request(request_queue_t *q); ?
void end_request(struct request *req, int uptodate);//請(qǐng)求完成 ?
void blk_stop_queue(request_queue_t *queue); //停止請(qǐng)求 ?
void blk_start_queue(request_queue_t *queue); //開(kāi)始請(qǐng)求 ?
void blk_cleanup_queue(request_queue_t *);//清除請(qǐng)求隊(duì)列?
1.4.2 ?簡(jiǎn)單的塊設(shè)備驅(qū)動(dòng)程序?qū)嵗?
向內(nèi)核注冊(cè)和注銷一個(gè)塊設(shè)備可使用如下函數(shù):
int register_blkdev(unsigned int major, const char *name); ?
int unregister_blkdev(unsigned int major, const char *name);?
例1.10 ?簡(jiǎn)單的塊設(shè)備驅(qū)動(dòng)程序?qū)嵗?
代碼見(jiàn)光盤(pán)\src\1drivermodel\1-10block。核心代碼如下所示:
static struct request_queue *Queue; ?
//自定義塊設(shè)備結(jié)構(gòu) ?
static struct simpleblockdevice ??
{ ?
? ? unsigned long size; ?
? ? spinlock_t lock; ?
? ? u8 *data; ?
? ? struct gendisk *gd; ?
} Device; ?
//處理I/O請(qǐng)求 ?
static void simpleblocktransfer(struct simpleblockdevice *dev, unsigned long sector, ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? unsigned long nsect, char *buffer, int write) ?
{ ?
? ? unsigned long offset = sector*hardsect_size; ?
? ? unsigned long nbytes = nsect*hardsect_size; ?
? ? //判斷I/O請(qǐng)求是否超出范圍 ?
? ? if ((offset + nbytes) > dev->size) ?
? ? { ?
? ? ? ? printk (KERN_NOTICE "sbd: Beyond-end write (%ld %ld)\n", offset, nbytes); ?
? ? ? ? return; ?
? ? } ?
? ? if (write) ?
? ? ? ? memcpy(dev->data + offset, buffer, nbytes); ?
? ? else ?
? ? ? ? memcpy(buffer, dev->data + offset, nbytes); ?
} ?
//簡(jiǎn)單請(qǐng)求處理 ?
static void simpleblockrequest(struct request_queue *q) ?
{ ?
? ? struct request *req; ?
? ? //獲取下一個(gè)請(qǐng)求 ?
? ? while ((req = elv_next_request(q)) != NULL) ??
? ? { ?
? ? ? ? if (! blk_fs_request(req)) ??
? ? ? ? { ?
? ? ? ? ? ? printk (KERN_NOTICE "Skip non-CMD request\n"); ?
? ? ? ? ? ? end_request(req, 0); ?
? ? ? ? ? ? continue; ?
? ? ? ? } ?
? ? ? ? simpleblocktransfer(&Device, req->sector, req->current_nr_sectors, ?
? ? ? ? ? ? req->buffer, rq_data_dir(req)); ?
? ? ? ? end_request(req, 1); ?
? ? } ?
} ?
//簡(jiǎn)單的塊設(shè)備ioctl函數(shù) ?
int simpleblockioctl (struct inode *inode, struct file *filp,unsigned int cmd, unsigned?
long arg) ?
{ ?
? ? long size; ?
? ? struct hd_geometry geo; ?
? ? switch(cmd) ??
? ? { ?
? ? //獲取磁盤(pán)信息 ?
? ? case HDIO_GETGEO: ?
? ? ? ? size = Device.size*(hardsect_size/KERNEL_SECTOR_SIZE); ?
? ? ? ? geo.cylinders = (size & ~0x3f) >> 6; ?
? ? ? ? geo.heads = 4; ?
? ? ? ? geo.sectors = 16; ?
? ? ? ? geo.start = 4; ?
? ? ? ? if (copy_to_user((void *) arg, &geo, sizeof(geo))) ?
? ? ? ? ? ? return -EFAULT; ?
? ? ? ? return 0; ?
? ? } ?
? ? return -ENOTTY; /* 未知命令 */ ?
} ?
//設(shè)備操作結(jié)構(gòu) ?
static struct block_device_operations simpleblockops = { ?
? ? .owner ? ? ? ? ? = THIS_MODULE, ?
? ? .ioctl ? ? ? = simpleblockioctl?
}; ?
static int __init simpleblockinit(void) ?
{ ?
? ? Device.size = nsectors*hardsect_size; ?
? ? spin_lock_init(&Device.lock); ?
? ? Device.data = vmalloc(Device.size); ?
? ? if (Device.data == NULL) ?
? ? ? ? return -ENOMEM; ?
? ? //初始化請(qǐng)求隊(duì)列,配置處理函數(shù)為sbd_request ?
? ? Queue = blk_init_queue(simpleblockrequest, &Device.lock); ?
? ? if (Queue == NULL) ?
? ? ? ? goto out; ?
? ? blk_queue_hardsect_size(Queue, hardsect_size); ?
? ? //注冊(cè)塊設(shè)備 ?
? ? major_num = register_blkdev(major_num, "sbd"); ?
? ? if (major_num <= 0) { ?
? ? ? ? printk(KERN_WARNING "sbd: unable to get major number\n"); ?
? ? ? ? goto out; ?
? ? } ?
? ? Device.gd = alloc_disk(16); ?
? ? if (! Device.gd) ?
? ? ? ? goto out_unregister; ?
? ? Device.gd->major = major_num; ?
? ? Device.gd->first_minor = 0; ?
? ? Device.gd->fops = &simpleblockops; ?
? ? Device.gd->private_data = &Device; ?
? ? strcpy (Device.gd->disk_name, "sbd0"); ?
? ? //配置容量 ?
? ? set_capacity(Device.gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE)); ?
? ? Device.gd->queue = Queue; ?
? ? add_disk(Device.gd); ?
? ? return 0; ?
out_unregister: ?
? ? unregister_blkdev(major_num, "sbd"); ?
out: ?
? ? vfree(Device.data); ?
? ? return -ENOMEM; ?
} ?
static void __exit simpleblockexit(void) ?
{ ?
? ? del_gendisk(Device.gd); ?
? ? put_disk(Device.gd); ?
? ? unregister_blkdev(major_num, "sbd"); ?
? ? blk_cleanup_queue(Queue); ?
? ? vfree(Device.data); ?
} ?
module_init(simpleblockinit); ?
module_exit(simpleblockexit); ?
運(yùn)行結(jié)果如下:
[root@/home]#cat /proc/filesystems ?
nodev ? sysfs ?
nodev ? rootfs ?
nodev ? bdev ?
nodev ? proc ?
nodev ? binfmt_misc ?
nodev ? debugfs ?
nodev ? securityfs ?
nodev ? sockfs ?
nodev ? usbfs ?
nodev ? pipefs ?
nodev ? anon_inodefs ?
nodev ? futexfs ?
nodev ? tmpfs ?
nodev ? inotifyfs ?
? ? ? ? ext3 ?
? ? ? ? cramfs ?
nodev ? ramfs ?
? ? ? ? msdos ?
? ? ? ? vfat ?
? ? ? ? iso9660 ?
nodev ? nfs ?
nodev ? nfs4 ?
nodev ? mqueue ?
nodev ? rpc_pipefs ?
[root@/home]#insmod demo.ko ?
?sbd0: unknown partition table ?
[root@/home]#mknod /dev/sbd b 253 0 ?
[root@/home]#./mkfs.ext3 /dev/sbd ?
mke2fs 1.40.9 (27-Apr-2008) ?
Filesystem label= ?
OS type: Linux ?
Block size=1024 (log=0) ?
Fragment size=1024 (log=0) ?
1280 inodes, 5120 blocks ?
256 blocks (5.00%) reserved for the super user ?
First data block=1?
Maximum filesystem blocks=5242880?
1 block group ?
8192 blocks per group, 8192 fragments per group ?
1280 inodes per group ?
?
Writing inode tables: done ?
Creating journal (1024 blocks): done ?
Writing superblocks and filesystem accounting information: done ?
?
This filesystem will be automatically checked every 39 mounts or ?
180 days, whichever comes first. ?Use tune2fs -c or -i to override. ?
[root@/home]#mount -t ext3 /dev/sbd /mnt/u ?
kjournald starting. ?Commit interval 5 seconds ?
EXT3 FS on sbd0, internal journal ?
EXT3-fs: mounted filesystem with ordered data mode. ?
[root@/home]#df ?
Filesystem ? ? ? ? ? 1k-blocks ? ? ?Used Available Use% Mounted on ?
rootfs ? ? ? ? ? ? ? ? 2063504 ? 1191136 ? ?767548 ?61% / ?
/dev/root ? ? ? ? ? ? ?2063504 ? 1191136 ? ?767548 ?61% / ?
/dev/sbd ? ? ? ? ? ? ? ? ?4955 ? ? ?1063 ? ? ?3636 ?23% /mnt/u ?
[root@/home]#cd /mnt/u ?
[root@/mnt/u]#ls ?
lost+found ?
========
總結(jié)
以上是生活随笔為你收集整理的Linux块设备驱动总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux安全学习总结
- 下一篇: linux内核技术文章