塊設備驅動——ramblock【轉】

轉自:https://www.cnblogs.com/linux-37ge/p/10207385.html

一. 什麼是塊設備、

    1.1. 一種具有一定結構的隨機存取設備,對這種設備的讀寫是按塊進行的,他使用緩衝區來存放暫時的數據,待條件成熟後,從緩存一次性寫入設備或者從設備一次性讀到緩衝區。可以隨機訪問,塊設備的訪問位置必須能夠在介質的不同區間前後移動

    1.2. 塊設備與字符設備差異

        1.2.1. 塊和字符是兩種不同的訪問設備的策略

        1.2.2. 同一個設備可以同時支持塊和字符兩種訪問策略

        1.2.3. 塊設備本身驅動層支持緩衝區,而字符設備驅動層沒有緩衝

        1.2.4. 塊設備驅動最適合存儲設備

    1.3. 與塊設備相關的幾個單位    

        1.3.1. 扇區(Sectors):概念來自於早期磁盤,在硬盤、DVD中還有用,在Nand/SD中已經沒意義了,但爲了設備的統一,扇區是任何塊設備硬件對數據處理的基本單位。通常,1個扇區的大小爲512byte或512的整數倍。(對設備而言)

        1.3.2. 塊 (Blocks):由Linux制定對內核或文件系統等數據處理的基本單位。通常,1個塊由1個或多個扇區組成。(對Linux操作系統而言)

        1.3.3. 段(Segments):由若干個相鄰的塊組成。是Linux內存管理機制中一個內存頁或者內存頁的一部分。

        1.3.4. 頁(Page),概念來自於內核,是內核內存映射管理的基本單位。linux內核的頁式內存映射名稱來源於此。

 

二. 塊設備的驅動框圖

    2.1. 框圖從宏觀分爲三層

        2.1.1. 虛擬文件系統層(VFS)

            a. VFS是linux系統內核的軟件層,由內核開發者已經編寫完成。

            b. VFS是文件系統和Linux 內核的接口,VFS以統一數據結構管理各種邏輯文件系統,接受用戶層對文件系統的各種操作

            c. 向上,對應用層提供一個標準的文件操作接口;

            d. 對下,對文件系統提供一個標準的接口,以便其他操作系統的文件系統可以方便的移植到Linux上;

        2.1.2. 內核空間層

            2.1.2. 內核空間層又細分爲三層

                a. 通用塊層(generic Block Layer)

                    負責維持一個I/O請求在上層文件系統與底層物理磁盤之間的關係。在通用塊層中,通常用一個bio結構體來對應一個I/O請求

                b. IO調度層

                    當多個請求提交給塊設備時,執行效率依賴於請求的順序。如果所有的請求是同一個方向(如:寫數據),執行效率是最大的。內核在調用塊設備驅動程序例程處理請求之前,先收集I/O請求並將請求排序,然後,將連續扇區操作的多個請求進行合併以提高執行效率(內核算法會自己做,不用你管),對I/O請求排序的算法稱爲電梯算法(elevator algorithm)。電梯算法在I/O調度層完成。內核提供了不同類型的電梯算法,電梯算法有

  1 noop(實現簡單的FIFO,基本的直接合並與排序),

  2 anticipatory(延遲I/O請求,進行臨界區的優化排序),

  3 Deadline(針對anticipatory缺點進行改善,降低延遲時間),

  4 Cfq(均勻分配I/O帶寬,公平機制)

  PS:其實IO調度層(包括請求合併排序算法)是不需要用戶管的,內核已經做好。

  映射層(Mapping Layer):起映射作用,將文件訪問映射爲設備的訪問。

  VFS:對各種文件系統進行統一封裝,爲用戶程序訪問文件提供統一的接口,包含ext2,FAT,NFS,設備文件。

  磁盤緩存(Caches):將訪問頻率很高的文件放入其中。

  相關數據結構

  block_device:      描述一個分區或整個磁盤對內核的一個塊設備實例

  gendisk:               描述一個通用硬盤(generic hard disk)對象。

  hd_struct:             描述分區應有的分區信息

  bio:                        描述塊數據傳送時怎樣完成填充或讀取塊給driver

  request:                描述向內核請求一個列表準備做隊列處理。

  request_queue:  描述內核申請request資源建立請求鏈表並填寫BIO形成隊列。

            c. 塊設備驅動

                塊設備驅動:在Linux中,驅動對塊設備的輸入或輸出(I/O)操作,都會向塊設備發出一個請求,在驅動中用request結構體描述。但對於一些磁盤設備而言請求的速度很慢,這時候內核就提供一種隊列的機制把這些I/O請求添加到隊列中(即:請求隊列),在驅動中用request_queue結構體描述。在向塊設備提交這些請求前內核會先執行請求的合併和排序預操作,以提高訪問的效率,然後再由內核中的I/O調度程序子系統來負責提交  I/O 請求,調度程序將磁盤資源分配給系統中所有掛起的塊 I/O  請求,其工作是管理塊設備的請求隊列,決定隊列中的請求的排列順序以及什麼時候派發請求到設備。

三. 實例分析

    3.1. 實例中用ram虛擬出一個塊設備,並非真正的塊設備

    3.2. do_my_ramblock_request函數用於響應request

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>


#define RAMBLOCK_SIZE (1024*1024) // 1MB,2048扇區

static struct gendisk *my_ramblock_disk; // 磁盤設備的結構體
static struct request_queue *my_ramblock_queue; // 等待隊列
static DEFINE_SPINLOCK(my_ramblock_lock);
static int major;
static unsigned char *my_ramblock_buf; // 虛擬塊設備的內存指針


static void do_my_ramblock_request(struct request_queue *q)
{

struct request *req;
static int r_cnt = 0; //實驗用,打印出驅動讀與寫的調度方法
static int w_cnt = 0;

req = blk_fetch_request(q);

while (NULL != req)
{
unsigned long start = blk_rq_pos(req) *512;
unsigned long len = blk_rq_cur_bytes(req);

if(rq_data_dir(req) == READ)
{
// 讀請求
memcpy(req->buffer, my_ramblock_buf + start, len); //讀操作,
printk("do_my_ramblock-request read %d times\n", r_cnt++);
}
else
{
// 寫請求
memcpy( my_ramblock_buf+start, req->buffer, len); //寫操作
printk("do_my_ramblock request write %d times\n", w_cnt++);
}

if(!__blk_end_request_cur(req, 0))
{
req = blk_fetch_request(q);
}
}
}


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("11111blk mount succeed\n");
return 0;
}
static int blk_release(struct gendisk *gd , fmode_t no)
{
printk("11111blk umount succeed\n");
return 0;
}

static const struct block_device_operations my_ramblock_fops =
{
.owner = THIS_MODULE,
.open = blk_open,
.release = blk_release,
.ioctl = blk_ioctl,
};

static int my_ramblock_init(void)
{
major = register_blkdev(0, "my_ramblock");
if (major < 0)
{
printk("fail to regiser my_ramblock\n");
return -EBUSY;
}

// 實例化
my_ramblock_disk = alloc_disk(1); //次設備個數 ,分區個數 +1

//分配設置請求隊列,提供讀寫能力
my_ramblock_queue = blk_init_queue(do_my_ramblock_request, &my_ramblock_lock);
//設置硬盤屬性
my_ramblock_disk->major = major;
my_ramblock_disk->first_minor = 0;
my_ramblock_disk->fops = &my_ramblock_fops;
sprintf(my_ramblock_disk->disk_name, "my_ramblcok"); // /dev/name
my_ramblock_disk->queue = my_ramblock_queue;
set_capacity(my_ramblock_disk, RAMBLOCK_SIZE / 512);
/* 硬件相關操作 */
my_ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
add_disk(my_ramblock_disk); // 向驅動框架註冊一個disk或者一個partation的接口

return 0;
}


static void my_ramblock_exit(void)
{
unregister_blkdev(major, "my_ramblock");
del_gendisk(my_ramblock_disk);
put_disk(my_ramblock_disk);
blk_cleanup_queue(my_ramblock_queue);
kfree(my_ramblock_buf);
}

module_init(my_ramblock_init);
module_exit(my_ramblock_exit);

MODULE_LICENSE("GPL");


複製代碼
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>


#define RAMBLOCK_SIZE (1024*1024)               // 1MB,2048扇區

static struct gendisk *my_ramblock_disk;        // 磁盤設備的結構體
static struct request_queue *my_ramblock_queue;    // 等待隊列
static DEFINE_SPINLOCK(my_ramblock_lock);
static int major;
static unsigned char *my_ramblock_buf;            // 虛擬塊設備的內存指針


static void do_my_ramblock_request(struct request_queue *q)
{

    struct request *req;
    static int r_cnt = 0;             //實驗用,打印出驅動讀與寫的調度方法
    static int w_cnt = 0;
    
    req = blk_fetch_request(q);
    
    while (NULL != req)
    {
        unsigned long start = blk_rq_pos(req) *512;
        unsigned long len = blk_rq_cur_bytes(req);
        
        if(rq_data_dir(req) == READ)
        {
            // 讀請求
            memcpy(req->buffer, my_ramblock_buf + start, len);     //讀操作,
            printk("do_my_ramblock-request read %d times\n", r_cnt++);
        }
        else
        {
            // 寫請求
            memcpy( my_ramblock_buf+start, req->buffer, len);     //寫操作
            printk("do_my_ramblock request write %d times\n", w_cnt++);
        }

        if(!__blk_end_request_cur(req, 0)) 
        {
            req = blk_fetch_request(q);
        }
    }
}


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("11111blk mount succeed\n");
    return 0;
}
static int blk_release(struct gendisk *gd , fmode_t no)
{
    printk("11111blk umount succeed\n");
    return 0;
}

static const struct block_device_operations my_ramblock_fops =
{
    .owner         = THIS_MODULE,
    .open         = blk_open,
    .release     = blk_release,
    .ioctl         = blk_ioctl,
};

static int my_ramblock_init(void)
{
    major = register_blkdev(0, "my_ramblock");
    if (major < 0)
    {
        printk("fail to regiser my_ramblock\n");
        return -EBUSY;
    }
    
    // 實例化
    my_ramblock_disk = alloc_disk(1);        //次設備個數 ,分區個數 +1
    
    //分配設置請求隊列,提供讀寫能力
    my_ramblock_queue = blk_init_queue(do_my_ramblock_request, &my_ramblock_lock);
    //設置硬盤屬性 
    my_ramblock_disk->major = major;
    my_ramblock_disk->first_minor = 0;
    my_ramblock_disk->fops = &my_ramblock_fops;
    sprintf(my_ramblock_disk->disk_name, "my_ramblcok");        // /dev/name
    my_ramblock_disk->queue = my_ramblock_queue;
    set_capacity(my_ramblock_disk, RAMBLOCK_SIZE / 512);
    /* 硬件相關操作 */
    my_ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
    add_disk(my_ramblock_disk);                // 向驅動框架註冊一個disk或者一個partation的接口
    
    return 0;
}


static void my_ramblock_exit(void)
{
    unregister_blkdev(major, "my_ramblock");
    del_gendisk(my_ramblock_disk);
    put_disk(my_ramblock_disk);
    blk_cleanup_queue(my_ramblock_queue);
    kfree(my_ramblock_buf); 
}

module_init(my_ramblock_init);
module_exit(my_ramblock_exit);

MODULE_LICENSE("GPL");
複製代碼

五. 實例實驗

    5.1. 模塊安裝(insmod)

[root@musk210 driver_test]# lsmod
Module Size Used by Not tainted
[root@musk210 driver_test]# insmod blkdev.ko
[root@musk210 driver_test]# lsmod
Module Size Used by Not tainted
blkdev 1955 0
[root@musk210 driver_test]#


複製代碼
[root@musk210 driver_test]# lsmod
Module                  Size  Used by    Not tainted
[root@musk210 driver_test]# insmod blkdev.ko 
[root@musk210 driver_test]# lsmod
Module                  Size  Used by    Not tainted
blkdev                  1955  0 
[root@musk210 driver_test]#
複製代碼

    5.2. 查看信息

        5.2.1. cat /proc/devices  

[root@musk210 driver_test]# cat /proc/devices
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
81 video4linux
89 i2c
90 mtd
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
251 hidraw
252 s3c_bc
253 pvrsrvkm
254 rtc

Block devices:
1 ramdisk
259 blkext
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
253 my_ramblock
254 device-mapper
[root@musk210 driver_test]#


複製代碼
[root@musk210 driver_test]# cat /proc/devices 
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 21 sg
 29 fb
 81 video4linux
 89 i2c
 90 mtd
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
251 hidraw
252 s3c_bc
253 pvrsrvkm
254 rtc

Block devices:
  1 ramdisk
259 blkext
  7 loop
  8 sd
 31 mtdblock
 65 sd
 66 sd
 67 sd
 68 sd
 69 sd
 70 sd
 71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
253 my_ramblock
254 device-mapper
[root@musk210 driver_test]#
複製代碼

         5.2.2. cat /proc/partitions

[root@musk210 driver_test]# cat /proc/partitions
major minor #blocks name

179 0 3817472 mmcblk0
179 1 264759 mmcblk0p1
179 2 264759 mmcblk0p2
179 3 104412 mmcblk0p3
179 4 3158463 mmcblk0p4
179 8 7761920 mmcblk1
179 9 7726620 mmcblk1p1
253 0 1024 my_r

複製代碼
[root@musk210 driver_test]# cat /proc/partitions 
major minor  #blocks  name

 179        0    3817472 mmcblk0
 179        1     264759 mmcblk0p1
 179        2     264759 mmcblk0p2
 179        3     104412 mmcblk0p3
 179        4    3158463 mmcblk0p4
 179        8    7761920 mmcblk1
 179        9    7726620 mmcblk1p1
 253        0       1024 my_r
複製代碼

          5.2.3. ls /dev/my*

[root@musk210 driver_test]# ls /dev/my*
/dev/my_ramblcok
[root@musk210 driver_test]#
[root@musk210 driver_test]# ls /dev/my*
/dev/my_ramblcok
[root@musk210 driver_test]#

        5.2.4. lsmod

[root@musk210 driver_test]# lsmod
Module                  Size  Used by    Not tainted
blkdev                  1955  0

[root@musk210 driver_test]# lsmod
Module                  Size  Used by    Not tainted
blkdev                  1955  0

    5.3. 掛載測試

        5.3.1. 掛載前要先格式化

            a. 格式化:mkfs.ext2 /dev/my_ramblock

[root@musk210 driver_test]# mkfs.ext2 /dev/my_ramblcok
[ 5587.367230] 11111blk mount succeed
Filesystem label=[ 5587.372541] do_my_ramblock-request read 0 times
[ 5587.376912] do_my_ramblock-request read 1 times
[ 5587.381302] do_my_ramblock-request read 2 times
[ 5587.385774] do_my_ramblock-request read 3 times
[ 5587.390276] do_my_ramblock-request read 4 times
[ 5587.394779] do_my_ramblock-request read 5 times
[ 5587.399285] do_my_ramblock-request read 6 times
[ 5587.403796] do_my_ramblock-request read 7 times
[ 5587.408303] do_my_ramblock-request read 8 times
[ 5587.412854] do_my_ramblock request write 0 times
[ 5587.417321] do_my_ramblock request write 1 times
[ 5587.421906] do_my_ramblock request write 2 times
[ 5587.426498] do_my_ramblock request write 3 times
[ 5587.431089] do_my_ramblock request write 4 times
[ 5587.435684] do_my_ramblock request write 5 times
[ 5587.440277] do_my_ramblock request write 6 times
[ 5587.444868] do_my_ramblock request write 7 times
[ 5587.449462] do_my_ramblock request write 8 times
[ 5587.454133] 11111blk umount succeed

OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
128 inodes, 1024 blocks
51 blocks (5%) reserved for the super user
First data block=1
Maximum filesystem blocks=262144
1 block groups
8192 blocks per group, 8192 fragments per group
128 inodes per group
[root@musk210 driver_test]#


複製代碼
[root@musk210 driver_test]# mkfs.ext2 /dev/my_ramblcok 
[ 5587.367230] 11111blk mount succeed
Filesystem label=[ 5587.372541] do_my_ramblock-request read 0 times
[ 5587.376912] do_my_ramblock-request read 1 times
[ 5587.381302] do_my_ramblock-request read 2 times
[ 5587.385774] do_my_ramblock-request read 3 times
[ 5587.390276] do_my_ramblock-request read 4 times
[ 5587.394779] do_my_ramblock-request read 5 times
[ 5587.399285] do_my_ramblock-request read 6 times
[ 5587.403796] do_my_ramblock-request read 7 times
[ 5587.408303] do_my_ramblock-request read 8 times
[ 5587.412854] do_my_ramblock request write 0 times
[ 5587.417321] do_my_ramblock request write 1 times
[ 5587.421906] do_my_ramblock request write 2 times
[ 5587.426498] do_my_ramblock request write 3 times
[ 5587.431089] do_my_ramblock request write 4 times
[ 5587.435684] do_my_ramblock request write 5 times
[ 5587.440277] do_my_ramblock request write 6 times
[ 5587.444868] do_my_ramblock request write 7 times
[ 5587.449462] do_my_ramblock request write 8 times
[ 5587.454133] 11111blk umount succeed

OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
128 inodes, 1024 blocks
51 blocks (5%) reserved for the super user
First data block=1
Maximum filesystem blocks=262144
1 block groups
8192 blocks per group, 8192 fragments per group
128 inodes per group
[root@musk210 driver_test]#
複製代碼

        5.3.2. 掛載到/tmp目錄下

            a. 掛載:  mount -t ext2 /dev/my_ramblcok /tmp

[root@musk210 driver_test]# mount -t ext2 /dev/my_ramblcok /tmp/
[ 5695.893226] 11111blk mount succeed
[ 5695.895247] do_my_ramblock-request read 9 times
[ 5695.899752] do_my_ramblock-request read 10 times
[ 5695.904346] do_my_ramblock-request read 11 times
[ 5695.908912] do_my_ramblock request write 9 times


複製代碼
[root@musk210 driver_test]# mount -t ext2 /dev/my_ramblcok /tmp/
[ 5695.893226] 11111blk mount succeed
[ 5695.895247] do_my_ramblock-request read 9 times
[ 5695.899752] do_my_ramblock-request read 10 times
[ 5695.904346] do_my_ramblock-request read 11 times
[ 5695.908912] do_my_ramblock request write 9 times
複製代碼

        5.3.3. 去/tmp目錄

[root@musk210 tmp]# ls
[ 5757.874625] do_my_ramblock-request read 12 times
[ 5757.877941] do_my_ramblock-request read 13 times
lost+found
[root@musk210 tmp]# touch a.txt
[ 5768.346032] do_my_ramblock-request read 14 times


複製代碼
[root@musk210 tmp]# ls
[ 5757.874625] do_my_ramblock-request read 12 times
[ 5757.877941] do_my_ramblock-request read 13 times
lost+found
[root@musk210 tmp]# touch a.txt
[ 5768.346032] do_my_ramblock-request read 14 times
複製代碼

 

參考《朱老師.塊設備驅動介紹》

參閱文獻:http://www.51testing.com/html/42/n-3710242.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章