app: open,read,write,“1.txt”
---------------------------------------------------------------------------------文件的讀寫
文件系統: vfat,ext2,ext3, yaffs2, jffs2 (把文件的讀寫轉換爲對扇區的讀寫)
----------------------------ll_rw_block--------------------------------------扇區的讀寫
1.把“讀寫”放入隊列
2.調用隊列的處理函數(優化/調順序)
塊設備驅動程序
---------------------------------------------------------------------------------
硬件: 硬盤,flash
ll_rw_block 源碼分析:
for (i = 0; i < nr; i++) {
struct buffer_head *bh = bhs[i];
submit_bh(rw, bh);
struct bio *bio;
submit_bio(rw, bio);
//通用的構造請求:使用bio來構造請求(request)
generic_make_request(bio);
__generic_make_request(bio);
request_queue *q = bdev_get_queue(bio->bi_bdev);//找到隊列
//調用隊列的“構造請求函數”
ret = q->make_request_fn(q, bio);
//默認的函數
__make_request
// 先嚐試合併
elv_merge(q,&req,bio);
//如果合併不成,使用bio構造請求
init_request_from_bio(req,bio);
//把請求放入隊列
add_request(q,req)
//執行隊列
generic_unplug_device
//調用隊列的“處理函數”
q->request_fn(q);
如何寫塊設備的驅動程序:
1.分配 gendisk 結構體:alloc_disk
2.設置
2.1.分配、設置隊列:reuest_queue_t //提供讀寫能力
blk_init_queue
2.2設置 gendisk 其他信息 //提供屬性
3.註冊
==============================================================================================================================
程序書寫思路:
1、分配一個gendisk結構體:
static struct gendisk *ramblock_disk;
ramblock_disk = alloc_disk(16);//參數爲次設 備號個數(分區個數+1)
在linux內核中,使用gendisk(通用磁盤)來表示一個獨立的磁盤設備。同一個磁盤的各個分區共享一個主設備號。而次設備號不同。
2、設置:
2.1首先設置一個隊列,它能夠提供讀寫能力
static struct request_queue *ramblock_queue;
ramblock_queue = blk_init_queue(do_ram_block_request, &ram_block_lock);
其中:
do_ram_block_request 請求處理函數,數據的一些操作在本函數中進行
ram_block_lock 訪問隊列權限自旋鎖
2.2設置其他屬性;
major = register_blkdev(0,"ramblock"); /*cat /proc/devices */
ramblock_disk->major =major;
ramblock_disk->first_minor = 0;
sprintf(ramblock_disk->disk_name, "ramblock");
ramblock_disk->fops = &ram_block_fops;
set_capacity(ramblock_disk,RAMBLOCK_SIZE/512);
說明:
>通過註冊這個塊設備,獲得這個設備的主設備號。
>first_minor 爲第一個次設備號。
>ram_block_fops 塊設備操作結構體:
static struct block_device_operations ram_block_fops = {
.owner = THIS_MODULE,
.getgeo = ramblock_getgeo,
};
>>ramblock_getgeo 這個函數用來填充驅動器信息。
內容如下:
static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
/*容量 = heads*cylinders*sectors*512 */
geo->heads = 2;//面 隨便定
geo->sectors = RAMBLOCK_SIZE/2/32/512;//扇區
geo->cylinders =32;//環 隨便定
return 0;
}
>>>這個的柱面數、環數可以隨便填寫(? 真的隨便嗎?),但是扇區數必須是對的。有一個計算公式:
容量 = heads*cylinders*sectors*512
>set_capacity 設置gendisk容量,參數是扇區個數。塊設備的最小尋址單元是扇區,扇區大小一般是2的整數倍,最常見的大小是512個字節,扇區的大小是設備的物理屬性,扇區是所有塊設備的的基本單元。不管無力設備的真實扇區的大小是多少,內核與塊設備進行交互的扇區都以512字節爲單位。因此set_capacity 函數也是以512字節爲單位的。
3、硬件的相關操作:
由於本次的框架程序是以內存當作塊設備,所以硬件的相關操作可以看作對內存空間的申請,因此工作比較簡單;
static unsigned char *ramblock_buf;
/*3.硬件相關的操作*/
ramblock_buf = kzalloc(RAMBLOCK_SIZE,GFP_KERNEL);
if (!ramblock_buf)
return -ENOMEM;
4、註冊:
add_disk(ramblock_disk);
注意:對add_disk的調用必須發生在驅動程序的初始化工作完成之後並且能夠響應磁盤的請求之後。
這樣一個驅動程序框架就算完成了,但是具體的工作還沒有幹,下面填充:do_ram_block_request這個函數,是在設置隊列提到的:
static void do_ram_block_request (struct request_queue * q)
{
static int r_cnt = 0;
static int w_cnt = 0;
struct request *req;
while ((req = elv_next_request(q)) != NULL)
{
/*數據傳輸三要素: 源,長度,目的*/
/*源/目的:*/
unsigned long offset = req->sector<<9;//*512 這個扇區
/*目的/源:*/
//req->buffer
/*長度*/
unsigned len = req->current_nr_sectors<<9;//*512
if(rq_data_dir(req) == READ)
{
// printk("read %d \n",++r_cnt);
memcpy(req->buffer,ramblock_buf+offset,len);
}
else
{
// printk("write %d \n",++w_cnt);
memcpy(ramblock_buf+offset,req->buffer,len);
}
end_request(req, 1); /* wrap up, 0 = fail, 1 = success */
}
}
這裏涉及了數據的操作,數據操作一般都會有三個要素,這是韋東山經常說的: 源、目的、長度。
>通過電梯算法,獲得這個隊列的每個內容。
>獲得偏移值:扇區*512
> 獲得長度:當前要傳送的扇區數目*512
>rq_data_dir這個宏從請求中抽取傳送的方向
==============================================================================================================================
測試方法:
1、安裝驅動
2、格式化 ramdisk: mkdosfs /dev/ramblock
3、ls /dev/ramblock
4、分區:
輸入w生效。
5、可以通過 ls -l /dev/ramblock*察看分區:
brw-rw---- 1 root root 13, 0 Jan 1 00:02 /dev/ramblock
brw-rw---- 1 root root 13, 1 Jan 1 00:02 /dev/ramblock1
brw-rw---- 1 root root 13, 2 Jan 1 00:02 /dev/ramblock2
6、可以通過mount /dev/ramblock1 /tmp/ 將新的分區掛接到 /tmp/上
=======================================================================================================================
完整的驅動程序代碼:驅動代碼地址