块设备驱动程序-内存盘

块设备驱动程序

为什么需要块设备驱动

块设备驱动程序是针对类似FLASH这类的设备,这类设备的写操作一般基本单位不是一个字节,而是一个块,写一个字节就要先把这个扇区先读回来再写。

总的来说,就是合并同类型操作,优化操作。先不执行,放入队列,优化后再执行

mark

mark

mark

小结

  • 应用程序通过文件系统做种调用到ll_rw_block来实现块设备文件的读写,所谓块设备就是根据硬件的限制优化了读写的顺序,将读写先放入队列然后执行.

  • 这里有个统一的管理结构gendisk,包含了一个执行队列queue

  • ll_rw_block最终会调用队列中的执行函数gendisk->queue->request_fn也就是说读写函数实际是在这里实现的,其他都是框架做好了的

框架分析

ll_rw_block

我们从入口函数 ll_rw_block===low-level access to block devices开始分析,该文件在fs\buffer.c

  • buffer_head 包含了数据传输三要素源目的长度
  • rw 读、写
  • nr 多少个传输块,也就是多少个buffer_head
void ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
//rw:读写标志位,  nr:bhs[]长度,  bhs[]:要读写的数据数组
{
      int i; 
      for (i = 0; i < nr; i++) {
      struct buffer_head *bh = bhs[i];                 //获取nr个buffer_head
       ... ...
       if (rw == WRITE || rw == SWRITE) {
              if (test_clear_buffer_dirty(bh)) {
              ... ...
              submit_bh(WRITE, bh);                //提交WRITE写标志的buffer_head   
         continue;
              }}
       else {
              if (!buffer_uptodate(bh)) {
              ... ...
              submit_bh(rw, bh);               //提交其它标志的buffer_head
              continue;
              }}
              unlock_buffer(bh); }
}

struct buffer_head {
    unsigned long b_state;          //缓冲区状态标志 
    struct buffer_head *b_this_page;    //页面中的缓冲区 
    struct page *b_page;           //存储缓冲区位于哪个页面
    sector_t b_blocknr;           //逻辑块号
    size_t b_size;              //块的大小
    char *b_data;               //页面中的缓冲区

    struct block_device *b_bdev;     //块设备,来表示一个独立的磁盘设备

    bh_end_io_t *b_end_io;         //I/O完成方法
 
    void *b_private;             //完成方法数据
 
    struct list_head b_assoc_buffers;   //相关映射链表

    /* mapping this buffer is associated with */
    struct address_space *b_assoc_map;   
    atomic_t b_count;             //缓冲区使用计数 
};

submit_bh

接着分析提交传输块submit_bh,通过bh来构造bio,然后调用submit_bio()提交bio

int submit_bh(int rw, struct buffer_head * bh)
{
       struct bio *bio;                    //定义一个bio(block input output),也就是块设备i/o
       ... ...
       bio = bio_alloc(GFP_NOIO, 1);      //分配bio
      /*根据buffer_head(bh)构造bio */
       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;             //设置i/o回调函数
       bio->bi_private = bh;                               //指向哪个缓冲区
       ... ...
       submit_bio(rw, bio);                           //提交bio
       ... ...
}

submit_bio

然后提交bio

void submit_bio(int rw, struct bio *bio)
{
    ...
	generic_make_request(bio);
}

generic_make_request

可以看到如果current链表为空,执行__generic_make_request,否则加入到current

void generic_make_request(struct bio *bio)
{
 if (current->bio_tail) {                   // current->bio_tail不为空,表示有bio正在提交
              *(current->bio_tail) = bio;     //将当前的bio放到之前的bio->bi_next里面
              bio->bi_next = NULL;    //更新bio->bi_next=0;
              current->bio_tail = &bio->bi_next; //然后将当前的bio->bi_next放到current->bio_tail里,使下次的bio就会放到当前bio->bi_next里面了

              return;    }

BUG_ON(bio->bi_next);
       do {
              current->bio_list = bio->bi_next;
              if (bio->bi_next == NULL)
                     current->bio_tail = &current->bio_list;
              else
                     bio->bi_next = NULL;

              __generic_make_request(bio);           //调用__generic_make_request()提交bio
              bio = current->bio_list;
       } while (bio);
       current->bio_tail = NULL; /* deactivate */
}

__generic_make_request

获取队列并提交队列和bio

 static inline void __generic_make_request(struct bio *bio)
{
request_queue_t *q;    
int ret;  
 ... ...
       do {
              q = bdev_get_queue(bio->bi_bdev);  //通过bio->bi_bdev获取申请队列q
              ... ...
              ret = q->make_request_fn(q, bio);             //提交申请队列q和bio
       } while (ret);
}

make_request_fn

这个具体的队列函数是一个函数指针,在blk_queue_make_request初始化

blk_init_queue_node()
{
    blk_queue_make_request(q, __make_request);
}
void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn)
{
    q->make_request_fn = mfn;
    ..
}

__make_request

  1. 执行电梯排序算法elv_merge,如果合并失败则单独将bio加入队列
  2. __generic_unplug_device是执行函数
static int __make_request(request_queue_t *q, struct bio *bio)
{

  struct request *req;          //块设备本身的队列
  ... ...
//(1)将之前的申请队列q和传入的bio,通过排序,合并在本身的req队列中
  el_ret = elv_merge(q, &req, bio);
  ... ...

  init_request_from_bio(req, bio);        //合并失败,单独将bio放入req队列
  add_request(q, req);                  //单独将之前的申请队列q放入req队列
  ... ...
  __generic_unplug_device(q);      //(2) 执行申请队列的处理函数     
 }

__generic_unplug_device.

这个涉及到了具体的例子,可以参考下例子drivers\block\xd.c

void __generic_unplug_device(request_queue_t *q)
{      if (unlikely(blk_queue_stopped(q)))
              return;
       if (!blk_remove_plug(q))
              return;
       q->request_fn(q);         
}

实际的例子如下:

//请求队列成员
struct request_queue
{
    ...
	request_fn_proc		*request_fn;
}
	
//定义了这么一个结构体的队列
static struct request_queue *xd_queue;	
static int __init xd_init(void)
{
	//初始化这个队列
	xd_queue = blk_init_queue(do_xd_request, &xd_lock);
}

//初始化队列的函数原型,也就是 do_xd_request 就是函数request_fn
//  *rfn: request_fn_proc结构体,用来执行申请队列中的处理函数
//  *lock:队列访问权限的自旋锁(spinlock),该锁需要通过DEFINE_SPINLOCK()函数来定义
request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
{
	return blk_init_queue_node(rfn, lock, -1);
}
request_queue_t *
blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
{...
	q->request_fn		= rfn;
}

do_xd_request

也就是说实际的队列函数就是do_xd_request,这里可以看到先使用elv_next_request的电梯算法取出队列,然后执行读写xd_readwrite

//实际的队列执行函数如下do_xd_request
static void do_xd_request (request_queue_t * q)
{
  struct request *req;            

  if (xdc_busy)
      return;

  while ((req = elv_next_request(q)) != NULL)    //(1)while获取申请队列中的需要处理的申请
  {
    int res = 0;
    ... ...
   for (retry = 0; (retry < XD_RETRIES) && !res; retry++)         
        res = xd_readwrite(rw, disk, req->buffer, block, count);
                 //将获取申请req的buffer成员 读写到disk扇区中,当读写失败返回0,成功返回1

   end_request(req, res);         //申请队列中的的申请已处理结束,当res=0,表示读写失败
    }
}

自己写程序实现内存盘

这里实现一个内存盘,参考程序如下,这里具体的设备都是由gendisk所控制

drivers/block/xd.c 
drivers/block/z2ram.c 
struct gendisk {
        int major;                   //设备主设备号,等于register_blkdev()函数里的major 
        int first_minor;             //起始次设备号,等于0,则表示此设备号从0开始的 
        int minors;              //分区(次设备)数量,当使用alloc_disk()时,就会自动设置该成员
        char disk_name[32];            //块设备名称, 等于register_blkdev()函数里的name
 
        struct hd_struct **part;    /*分区表的信息*/
        int part_uevent_suppress;
        struct block_device_operations *fops; //块设备操作函数
        struct request_queue *queue;  //请求队列,用于管理该设备IO请求队列的指针*
        void *private_data;                    /*私有数据*/
        sector_t capacity;               /*扇区数,512字节为1个扇区,描述设备容量*/
        ....
    };
  1. 分配gendisk结构

    ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */
    
  2. 分配设置队列,队列提供实际的读写函数,这个会赋值到gendisk

    static DEFINE_SPINLOCK(ramblock_lock);
    ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
    //具体的读写函数,需要取出队列
    static void do_ramblock_request (request_queue_t * q)
    {
        struct request *req;
        static int cnt=0;
        printk("do_ramblock_request %d\n", ++cnt);
        while ((req = elv_next_request(q)) != NULL) {
            ....
            end_request(req, 1);
        }
    
    }
    
  3. 注册块设备驱动,对比注册字符设备驱动,这么并没有提供file operation操作

    major = register_blkdev(0, "ramblock");  /* cat /proc/devices */
    
  4. 设置gendisk参数,包括先前的队列

    ramblock_disk->major       = major;					//主设备号
    ramblock_disk->first_minor = 0;						//扇区起始的编号
    sprintf(ramblock_disk->disk_name, "ramblock");
    ramblock_disk->fops        = &ramblock_fops;		//这个是必备的
    set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);  //内核认为扇区是512字节大小的
    ramblock_disk->queue = ramblock_queue;
    
    static struct block_device_operations ramblock_fops = {
    	.owner	= THIS_MODULE,
    };
    
  5. 注册这个gendisk

    add_disk
    

1th队列获取执行

这里最关键的函数在队列的读取执行函数,需要取出队列,否则程序执行后卡死

static void do_ramblock_request (request_queue_t * q)
{
    struct request *req;
    static int cnt=0;
    printk("do_ramblock_request %d\n", ++cnt);
    while ((req = elv_next_request(q)) != NULL) {
        ....
        end_request(req, 1);
    }

}

完整的程序

/* 参考:
 * drivers\block\xd.c
 * drivers\block\z2ram.c
 */

#include <linux/module.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>


struct gendisk *ramblock_disk ;


static struct request_queue *ramblock_queue;
static DEFINE_SPINLOCK(ramblock_lock);

static int major;
#define RAMBLOCK_SIZE (1024*1024)
static struct block_device_operations ramblock_fops = {
	.owner	= THIS_MODULE,
};

static void do_ramblock_request (request_queue_t * q)
{
    struct request *req;
    static int cnt=0;
    printk("do_ramblock_request %d\n", ++cnt);
    while ((req = elv_next_request(q)) != NULL) {
        end_request(req, 1);
    }

}
static int __init ramblock_init(void)
{
    /* 1. 分配一个gendisk结构体 */
	ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */
    //2. set queue
    ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
    //3. set gendisk
    major=register_blkdev(0, "ramblock");
    ramblock_disk->major       = major;					//主设备号
    ramblock_disk->first_minor = 0;						//扇区起始的编号
    sprintf(ramblock_disk->disk_name, "ramblock");
    ramblock_disk->fops        = &ramblock_fops;		//这个是必备的
    set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);  //内核认为扇区是512字节大小的
    ramblock_disk->queue = ramblock_queue;
    add_disk(ramblock_disk);

}

static void ramblock_exit(void)
{
	unregister_blkdev(major, "ramblock");
	del_gendisk(ramblock_disk);
	put_disk(ramblock_disk);
	blk_cleanup_queue(ramblock_queue);
}

module_init(ramblock_init);
module_exit(ramblock_exit);
MODULE_LICENSE("GPL");

测试

# insmod ramblock.ko
 ramblock:do_ramblock_request 1
 unknown partition table

# 可以尝试读取文件来达到队列的执行
# hexdump /dev/ramblock
do_ramblock_request 2
    
# ls /dev/ramblock* -l
brw-rw----    1 0        0        254,   0 Jan  1 00:26 /dev/ramblock
# cat /proc/devices
Block devices:
254 ramblock

2th 内存盘实现

  1. 分配一块内存给这个内存盘kzalloc

    //声明一个块内存
    static unsigned char *ramblock_buf;
    ramblock_buf = kzalloc (RAMBLOCK_SIZE, GFP_KERNEL); //分配内存
    //kfree(ramblock_buf);
    
  2. 提供相应的读写函数

    static void do_ramblock_request (request_queue_t * q)
    {
        struct request *req;
        static int cnt=0;
        printk("do_ramblock_request %d\n", ++cnt);
        while ((req = elv_next_request(q)) != NULL) {
    
    		/* 数据传输三要素: 源,目的,长度 */
    		/* 源/目的: */
            // which sector
    		unsigned long offset = req->sector * 512;
    
    		/* 目的/源: */
    		// req->buffer
            
    		/* 长度: */		
            // 当前要传输多少个扇区
    		unsigned long len = req->current_nr_sectors * 512;
    
    		if (rq_data_dir(req) == READ)
    		{
    			memcpy(req->buffer, ramblock_buf+offset, len);
    		}
    		else
    		{
    			memcpy(ramblock_buf+offset, req->buffer, len);
    		}	
            end_request(req, 1);
        }
    }
    

测试

  1. 安装工具mkdosfs

    1. 下载dosfstools_2.11.orig.tar.gz

    2. 创建临时目录mkdir tmp, mv dosfstools_2.11.orig.tar.gz tmp/

    3. 解压cd tmp && tar zxvf dosfstools_2.11.orig.tar.gz

    4. 编译 cd dosfstools-2.11/ && make CC=arm-linux-gcc

    5. 复制,注意这里是应用程序复制

      cp tmp/dosfstools-2.11/mkdosfs/mkdosfs  /usr/sbin/
      
  2. 加载驱动,依然提示无分区,因为使用kzalloc是初始化0,确实没有分区表

  3. 搞个分区

    # mkdosfs /dev/ramblock
    mkdosfs 2.11 (12 Mar 2005)
    do_ramblock_request 2
    do_ramblock_request 3
    unable to get drive geometry, usindo_ramblock_request 4
    g default 255/63#
    
  4. 挂载mount /dev/ramblock /tmp/

  5. 这个时候可以读写文件了vi a.c,写完保存后卸载umount /tmp

  6. 这个时候tmp下是空的了,再挂载a.c恢复了

  7. 可以保存成一个文件,cat /dev/ramblock > /mnt/ramblock.bin

  8. 在虚拟机中查看这个文件

    # -o loop 回还设备:可将一个普通文件当成一个块设备文件挂接
    sudo mount -o loop ramblock.bin /mnt
    

完整代码

/* 参考:
 * drivers\block\xd.c
 * drivers\block\z2ram.c
 */
#include <linux/module.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>

//声明一个块内存
static unsigned char *ramblock_buf;

struct gendisk *ramblock_disk ;


static struct request_queue *ramblock_queue;
static DEFINE_SPINLOCK(ramblock_lock);

static int major;
#define RAMBLOCK_SIZE (1024*1024)
static struct block_device_operations ramblock_fops = {
	.owner	= THIS_MODULE,
};


static void do_ramblock_request (request_queue_t * q)
{
    struct request *req;
    static int cnt=0;
    printk("do_ramblock_request %d\n", ++cnt);
    while ((req = elv_next_request(q)) != NULL) {

		/* 数据传输三要素: 源,目的,长度 */
		/* 源/目的: */
        // which sector
		unsigned long offset = req->sector * 512;

		/* 目的/源: */
		// req->buffer

		/* 长度: */		
        // 当前要传输多少个扇区
		unsigned long len = req->current_nr_sectors * 512;

		if (rq_data_dir(req) == READ)
		{
			memcpy(req->buffer, ramblock_buf+offset, len);
		}
		else
		{
			memcpy(ramblock_buf+offset, req->buffer, len);
		}	

        end_request(req, 1);
    }

}

static int __init ramblock_init(void)
{

    /* 1. 分配一个gendisk结构体 */
	ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */

    //2. set queue
    ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock)

    //3. set gendisk
    major=register_blkdev(0, "ramblock");
    ramblock_disk->major       = major;					//主设备号
    ramblock_disk->first_minor = 0;						//扇区起始的编号
    sprintf(ramblock_disk->disk_name, "ramblock");
    ramblock_disk->fops        = &ramblock_fops;		//这个是必备的
    set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);  //内核认为扇区是512字节大小的
    ramblock_disk->queue = ramblock_queue;

    ramblock_buf = kzalloc (RAMBLOCK_SIZE, GFP_KERNEL); //分配内存
    add_disk(ramblock_disk);


}
static void ramblock_exit(void)
{
	unregister_blkdev(major, "ramblock");
	del_gendisk(ramblock_disk);
	put_disk(ramblock_disk);
	blk_cleanup_queue(ramblock_queue);
    kfree(ramblock_buf);
}
module_init(ramblock_init);
module_exit(ramblock_exit);
MODULE_LICENSE("GPL");

3th查看电梯排序

我们可以在电梯算法获取队列中打印出来这个顺序

static void do_ramblock_request (request_queue_t * q)
{
    struct request *req;
    static int r_cnt=0,w_cnt=0;
    //printk("do_ramblock_request %d\n", ++cnt);
    while ((req = elv_next_request(q)) != NULL) {

		/* 数据传输三要素: 源,目的,长度 */
		/* 源/目的: */
        // which sector
		unsigned long offset = req->sector * 512;

		/* 目的/源: */
		// req->buffer
        
		/* 长度: */		
        // 当前要传输多少个扇区
		unsigned long len = req->current_nr_sectors * 512;

		if (rq_data_dir(req) == READ)
		{
            printk("do_rambloc_request read %d\n", ++r_cnt);
			memcpy(req->buffer, ramblock_buf+offset, len);
		}
		else
		{
            printk("do_rambloc_request read %d\n", ++w_cnt);
			memcpy(ramblock_buf+offset, req->buffer, len);
		}	
        end_request(req, 1);
    }
}

测试

  1. 安装模块读取了一次

    # insmod ramblock.ko
     ramblock:read 1
     unknown partition table
    
  2. 格式化中的读写

    # mkdosfs /dev/ramblock
    mkdosfs 2.11 (12 Mar 2005)
    read 2
    read 3
    unable to get drive geometry, using default 255/63write 1
    write 2
    write 3
    write 4
    write 5
    
  3. 挂载时读取了多次

    
    # mount /dev/ramblock  /tmp
    read 4
    ...
    read 42
    
  4. 复制文件到内存盘,并没有立即写

    # cp Makefile  /tmp/
    read 43
    
  5. 强制同步系统调用或者等一会

    # 等一会
    # write 6
    write 7
    write 8
    write 9
    
  6. 删除文件也是一样的

    # rm /tmp/Makefile
    # sync
    write 10
    write 11
    write 12
    

4th 添加磁盘信息

古老的硬盘中有以下信息,由gendisk->struct block_device_operations *fops提供接口

容量 = 磁头 * 柱面 * 扇区 * 512 ( 容量 = heads * sectors * cylinders * 512 )
磁头:即有多少面。这里假设有 2 面。
柱面:有多少环,这里假设有 32 环。
扇区:一环里有多少个扇区,这个得通过“公式”计算出来。

也就是说我们需要设置gendiskfopsgetgeo接口

int (*getgeo)(struct block_device *, struct hd_geometry *)
    
    
static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
	/* 容量=heads*cylinders*sectors*512 */
	geo->heads     = 2;
	geo->cylinders = 32;
	geo->sectors   = RAMBLOCK_SIZE/2/32/512;
	return 0;
}
static struct block_device_operations ramblock_fops = {
	.owner	= THIS_MODULE,
	.getgeo	= ramblock_getgeo,
};

测试

这里使用fdisk,输入m获得信息

 #fdisk /dev/ramblock
 Command (m for help): m
Command (m for help): m
Command Action
a       toggle a bootable flag
b       edit bsd disklabel
c       toggle the dos compatibility flag
d       delete a partition
l       list known partition types
n       add a new partition
o       create a new empty DOS partition table
p       print the partition table
q       quit without saving changes
s       create a new empty Sun disklabel
t       change a partition's system id
u       change display/entry units
v       verify the partition table
w       write table to disk and exit
x       extra functionality (experts only)

  1. n来创建分区,继续输入p输入分区编号,这里支持4个分区

    Command (m for help): n
    Command action
       e   extended
       p   primary partition (1-4)
    p
    
    
  2. 输入编号1,来设置第一个分区,这里有显示1--32就是上面设置的柱面

    Partition number (1-4): 1
    First cylinder (1-32, default 1):
    
  3. 设置这个柱面的柱面范围

    Partition number (1-4): 1
    First cylinder (1-32, default 1): 1
    Last cylinder or +size or +sizeM or +sizeK (1-32, default 32): 5
    
    
  4. 设置完成退出到主菜单,输入p查看分区表p print the partition table

    
    Command (m for help): p
    Disk /dev/ramblock: 1 MB, 1048576 bytes
    2 heads, 32 sectors/track, 32 cylinders
    Units = cylinders of 64 * 512 = 32768 bytes
    
            Device Boot      Start         End      Blocks  Id System
    /dev/ramblock1               1           5         144  83 Linux
    
    
  5. 继续输入n设置分区表

    Command (m for help): n
    Command action
       e   extended
       p   primary partition (1-4)
    p
    Partition number (1-4): 2
    First cylinder (6-32, default 6): 6
    Last cylinder or +size or +sizeM or +sizeK (6-32, default 32): 32
    
    
  6. 查看完成的分区表

    Command (m for help): p
    Disk /dev/ramblock: 1 MB, 1048576 bytes
    2 heads, 32 sectors/track, 32 cylinders
    Units = cylinders of 64 * 512 = 32768 bytes
    
            Device Boot      Start         End      Blocks  Id System
    /dev/ramblock1               1           5         144  83 Linux
    /dev/ramblock2               6          32         864  83 Linux
    
    
  7. 输入w保存

    Command (m for help): w
    The partition table has been altered!
    write 1
     ramblock:read 14
     ramblock1 ramblock2
    
  8. 查看这个设备文件,创建了多个分区,0表示整个磁盘,1表示第一个分区

    # ls /dev/ramblock* -l
    brw-rw----    1 0        0        254,   0 Jan  1 15:47 /dev/ramblock
    brw-rw----    1 0        0        254,   1 Jan  1 15:47 /dev/ramblock1
    brw-rw----    1 0        0        254,   2 Jan  1 15:47 /dev/ramblock2
    
  9. 可以分别挂载格式化了

    # mkdosfs /dev/ramblock1
    mkdosfs 2.11 (12 Mar 2005)
    read 15
    read 16
    write 2
    write 3
    write 4
    write 5
    write 6
    # mkdosfs /dev/ramblock2
    mkdosfs 2.11 (12 Mar 2005)
    read 17
    read 18
    write 7
    write 8
    write 9
    write 10
    write 11
    # mkdosfs /dev/ramblock0
    mkdosfs 2.11 (12 Mar 2005)
    /dev/ramblock0: No such file or directory
    # mkdosfs /dev/ramblock
    mkdosfs 2.11 (12 Mar 2005)
    read 19
    read 20
    write 12
    write 13
    write 14
    write 15
    write 16
    
    
  10. 修改磁盘属性,默认是linux,使用指令t,具体的格式L查看

    Command (m for help): t
    Selected partition 1
    Hex code (type L to list codes): L
    
     0 Empty                  1b Hidden Win95 FAT32     9f BSD/OS
     1 FAT12                  1c Hidden W95 FAT32 (LBA) a0 Thinkpad hibernation
     4 FAT16 <32M             1e Hidden W95 FAT16 (LBA) a5 FreeBSD
     5 Extended               3c Part.Magic recovery    a6 OpenBSD
     6 FAT16                  41 PPC PReP Boot          a8 Darwin UFS
     7 HPFS/NTFS              42 SFS                    a9 NetBSD
     a OS/2 Boot Manager      63 GNU HURD or SysV       ab Darwin boot
     b Win95 FAT32            80 Old Minix              b7 BSDI fs
     c Win95 FAT32 (LBA)      81 Minix / old Linux      b8 BSDI swap
     e Win95 FAT16 (LBA)      82 Linux swap             be Solaris boot
     f Win95 Ext'd (LBA)      83 Linux                  eb BeOS fs
    11 Hidden FAT12           84 OS/2 hidden C: drive   ee EFI GPT
    12 Compaq diagnostics     85 Linux extended         ef EFI (FAT-12/16/32)
    14 Hidden FAT16 <32M      86 NTFS volume set        f0 Linux/PA-RISC boot
    16 Hidden FAT16           87 NTFS volume set        f2 DOS secondary
    17 Hidden HPFS/NTFS       8e Linux LVM              fd Linux raid autodetect
    Hex code (type L to list codes): c
    Changed system type of partition 1 to c (Win95 FAT32 (LBA))
    
    Command (m for help): p
    
    Disk /dev/ramblock: 1 MB, 1048576 bytes
    2 heads, 32 sectors/track, 32 cylinders
    Units = cylinders of 64 * 512 = 32768 bytes
    
            Device Boot      Start         End      Blocks  Id System
    /dev/ramblock1               1          32        1008   c Win95 FAT32 (LBA)
    
    
posted @ 2018-12-21 13:50  zongzi10010  阅读(455)  评论(0编辑  收藏  举报