天天看點

Linux下驅動開發_塊裝置驅動開發(記憶體模拟存儲)

theme: scrolls-light

highlight: vs2015

一、前言

塊裝置驅動塊是Linux下3大裝置驅動架構之一,塊裝置主要是針對存儲類型的裝置設計的驅動,配合檔案系統完成資料存儲。在應用層的cp、cd、touch、vim、mount等等可以操作檔案,可以操作目錄的指令都會通過檔案系統,通過塊裝置驅動完成對底層儲存設備的通路,實作資料讀取或者寫入。

是以大緻總結下:塊裝置驅動的目的是給Linux檔案系統提供底層接口。

二、編寫塊裝置驅動的思路

既然學到了驅動開發,了解到塊裝置開發。 那麼看這篇文章的小夥伴應該在單片機裡裸機方式寫過一些flash驅動、SD卡驅動。 對于flash儲存設備而言,要存取資料,根據晶片的手冊我們主要是封裝一個寫資料函數和讀取函數,封裝好了這兩個函數才友善上層應用的調用。 對于flash而言常見的讀寫機關一般是頁、扇區。容量大的flash比如SD卡,讀寫最小機關規定為扇區。  扇區一般大小規定為512位元組,那麼底層要封裝好的函數就是讀扇區,寫扇區函數。 這兩個函數完成與flash空間互動,實作資料存儲。

在Linux下完成塊裝置驅動編寫,主要是要完成來至檔案系統的存儲請求,檔案系統讓你把資料存到那個扇區,你驅動就去存,檔案系統讓你從那個扇區讀取輸出來,驅動就去讀取。 隻要是完美的處理好了檔案系統的請求,那麼應用層工作就是一切順利的。 檔案系統不需要管你把資料存在什麼裝置上。是SD卡?是FlashW25Q64是eeprom?還是RAM記憶體裡?對檔案系統而言不關系,它隻關心存進去的資料下次可以完美的讀取出來便是。

那麼為了友善介紹塊裝置的驅動開發,我這裡會先用malloc在驅動申請一塊記憶體來當做FLASH裝置,這樣就不需要接任何硬體,降低了難度,純軟體的方式了解驅動架構運作流程。

下面這張圖是解釋應用層 使用檔案目錄操作指令操作塊裝置時,與底層驅動之間的調用大緻過程。

Linux下驅動開發_塊裝置驅動開發(記憶體模拟存儲)

塊裝置與字元裝置比較:

(1) 塊裝置裝置節點名稱自己定義的,沒有标準。

(2)塊裝置的主裝置号可以動态配置設定,次裝置号時通過檔案系統對塊裝置分區時,自動填充。

/dev/sdb fdisk指令進行分區。 /dev/sdb1 /dev/sdb2

(2)在塊裝置驅動裡可以設定最大支援的分區數量

塊裝置處理資料的方式

Linux下驅動開發_塊裝置驅動開發(記憶體模拟存儲)

下面是塊裝置驅動的資料結構:

Linux下驅動開發_塊裝置驅動開發(記憶體模拟存儲)
塊裝置注冊與登出函數

1. 注冊函數
int register_blkdev(unsigned int major, const char *name)
函數功能介紹: 注冊一個新的塊裝置
函數參數介紹:
@major:塊裝置的主裝置号[1..255]。 如果 major = 0,表示嘗試配置設定未使用的主裝置号,傳回值就表示配置設定
成功的主裝置号。
@name:新塊裝置的名稱。 注意: 該名稱必須保證在系統中是唯一的。
注冊示例:
int Tiny4412_block_major = register_blkdev(0, "Tiny4412_block");


2. 登出函數
void unregister_blkdev(unsigned int major, const char *name)
函數功能介紹: 登出已注冊的塊裝置。
函數參數介紹:
@major: 主裝置号
@name: 裝置名稱
登出示例:
unregister_blkdev(Tiny4412_block_major, "Tiny4412_block");      

三、塊裝置的示例代碼

3.1 驅動代碼

#include <linux/module.h> 
#include <linux/blkdev.h> 
#include <linux/hdreg.h> 
#include <linux/version.h>
#include <linux/vmalloc.h>

/*
*     insmod tiny4412_blkdev.ko 
*     # or insmod tiny4412_blkdev.ko size=numK/M/G/T 
*     fdisk /dev/tiny4412_blkdev # create 2 patitions 
*     mkfs.ext2 /dev/tiny4412_blkdev1 
*     mkfs.ext2 /dev/tiny4412_blkdev2 
*     mount /dev/tiny4412_blkdev1 /mnt/temp1/ 
*     mount /dev/tiny4412_blkdev2 /mnt/temp2/ 
*     # play in /mnt/temp1/ and /mnt/temp2/ 
*     umount /mnt/temp1/ 
*     umount /mnt/temp2/ 
*     rmmod tiny4412_blkdev.ko 
* 
*/
static int Tiny4412_block_major=0;
static struct request_queue *tiny4412_blkdev_queue; 
static struct gendisk *tiny4412_blkdev_disk;

#define TINY4412_BLK_DEV_BYTES        (1024*1024*50)  /*設定塊裝置的大小*/
static unsigned char *sizeof_p;

/*
* Handle an I/O request.
* 實作扇區的讀寫

unsigned long sector:  目前扇區位置
unsigned long nsect :  扇區讀寫數量
char *buffer        :  讀寫的緩沖區指針
int write           :  是讀還是寫
*/
static void Tiny4412_block_dev_sector_read_write(unsigned long sector,unsigned long nsect, char *buffer, int write)
{
        /*塊裝置最小機關是一個扇區,一個扇區的位元組數是512位元組*/
        unsigned long offset = sector;  /*寫入資料的位置*/
        unsigned long nbytes = nsect;   /*寫入的長度*/
        if((offset + nbytes)>TINY4412_BLK_DEV_BYTES)
        {
            printk("寫超出範圍,強制結束(%ld %ld)\n", offset, nbytes);
            return;
        }
        if(write) /*為真,表示是寫*/
        memcpy(sizeof_p + offset, buffer, nbytes);
        else      /*讀操作*/
        memcpy(buffer,sizeof_p + offset, nbytes);
}

/*
處理請求
*/
static int tiny4412_blkdev_make_request(struct request_queue *q, struct bio *bio) 
{ 
    int dir; 
    unsigned long long dsk_offset; 
    struct bio_vec *bvec; 
    int i; 
    void *iovec_mem;
    
    /*判斷讀寫方向*/
    if(bio_data_dir(bio) == WRITE) dir = 1;
    else dir = 0;
    dsk_offset = bio->bi_sector << 9;
    bio_for_each_segment(bvec, bio, i) 
    { 
        iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset; 
        
        //起始位置,長度,源資料,方向
        Tiny4412_block_dev_sector_read_write(dsk_offset,bvec->bv_len,iovec_mem,dir);
        
        kunmap(bvec->bv_page);
        dsk_offset += bvec->bv_len; 
    }
    bio_endio(bio, 0); 
    return 0;
}


struct block_device_operations tiny4412_blkdev_fops = 
{ 
    .owner= THIS_MODULE, 
};


static int __init tiny4412_blkdev_init(void) 
{ 
    sizeof_p=vmalloc(TINY4412_BLK_DEV_BYTES);
    if(sizeof_p==NULL)
    {
        printk("空間申請失敗!\n");
        return 0;
    }
    /*動态配置設定請求隊列*/
    tiny4412_blkdev_queue = blk_alloc_queue(GFP_KERNEL);
    
    /*綁定請求隊列*/
    blk_queue_make_request(tiny4412_blkdev_queue,tiny4412_blkdev_make_request);
    
    /*動态配置設定次裝置号結構*/
    /*每一個磁盤(分區)都是使用一個gendisk結構儲存*/
    tiny4412_blkdev_disk = alloc_disk(64); 
    
    /*磁盤名稱指派*/
    strcpy(tiny4412_blkdev_disk->disk_name, "tiny4412_blkdev"); 

    /*注冊一個塊裝置,自動配置設定主裝置号*/
    Tiny4412_block_major = register_blkdev(0,"Tiny4412_block");
    printk("Tiny4412_block_major=%d\n",Tiny4412_block_major);
    
    tiny4412_blkdev_disk->major=Tiny4412_block_major;      /*主裝置号*/
    tiny4412_blkdev_disk->first_minor = 0;                 /*次裝置号*/
    tiny4412_blkdev_disk->fops = &tiny4412_blkdev_fops;   /*檔案操作結合*/
    tiny4412_blkdev_disk->queue = tiny4412_blkdev_queue;  /*處理資料請求的隊列*/
    
    /*設定磁盤結構 capacity 的容量*/
    /*注意: 塊裝置的大小使用扇區作為機關設定,而扇區的大小預設是512位元組。
      cat /sys/block/xxxx/size 可以檢視到設定的大小
      把位元組為機關的大小轉換為以扇區為機關時,我們需要除以512,或者右移9位
    */
    set_capacity(tiny4412_blkdev_disk,TINY4412_BLK_DEV_BYTES>>9); 
    
    //添加磁盤資訊到核心
    add_disk(tiny4412_blkdev_disk);
    return 0;
}

static void __exit tiny4412_blkdev_exit(void) 
{ 
    //删除磁盤
    del_gendisk(tiny4412_blkdev_disk);
    
    put_disk(tiny4412_blkdev_disk); 
    
    //清除隊列
    blk_cleanup_queue(tiny4412_blkdev_queue);
    
    /*登出塊裝置*/
    unregister_blkdev(Tiny4412_block_major, "Tiny4412_block");
    
    vfree(sizeof_p);
}

module_init(tiny4412_blkdev_init); 
module_exit(tiny4412_blkdev_exit);
MODULE_LICENSE("GPL");      

3.2 安裝測試

[root@wbyq code]#insmod tiny4412_block_device.ko

[ 6920.590000] tiny4412_sda: unknown partition table

[ 6920.590000] 塊裝置注冊成功!

[root@wbyq code]#ls /dev/tiny4412_sda -l

brw-rw---- 1 root root 253, 0 Nov 14 2018 /dev/tiny4412_sda

[root@wbyq code]#mkfs.ext2 /dev/tiny4412_sda //格式化塊裝置

Filesystem label=

OS type: Linux

Block size=1024 (log=0)

Fragment size=1024 (log=0)

2560 inodes, 10240 blocks

512 blocks (5%) reserved for the super user

First data block=1

Maximum filesystem blocks=262144

2 block groups

8192 blocks per group, 8192 fragments per group

1280 inodes per group

Superblock backups stored on blocks:

8193

[root@wbyq code]#mount /dev/tiny4412_sda /mnt/ //挂載塊裝置

[root@wbyq code]#cd /mnt/

[root@wbyq mnt]#ls

lost+found

[root@wbyq mnt]#mkdir 123

[root@wbyq mnt]#ls

123 lost+found

[root@wbyq mnt]#touch 123.c

[root@wbyq mnt]#ls

123 123.c lost+found

[root@wbyq mnt]#cd /

[root@wbyq ]#umount /mnt/







[root@wbyq ]#cat /sys/class/block/tiny4412_sda/size

20480




[root@wbyq ]#mount /dev/tiny4412_sda /mnt/

[root@wbyq ]#df -h

Filesystem Size Used Available Use% Mounted on

192.168.10.11:/work/rootfs/

46.8G 15.5G 28.9G 35% /

/dev/tiny4412_sda 9.7M 14.0K 9.2M 0% /mnt

 

 作業:

1. 看懂塊裝置架構,使用的模拟的記憶體。

2. 加入SD卡的驅動,配合塊裝置架構,完成完整的塊裝置驅動編寫。      

3.3 分區之後的情況

[root@wbyq code]#ls /dev/tiny4412_sda

tiny4412_sda tiny4412_sda1

[root@wbyq code]#ls /dev/tiny4412_sda1

[root@wbyq code]#ls /dev/tiny4412_sda* -l

brw-rw---- 1 root root 253, 0 Nov 14 2018 /dev/tiny4412_sda

brw-rw---- 1 root root 253, 1 Nov 14 2018 /dev/tiny4412_sda1      

繼續閱讀