• 块设备驱动程序-内存盘


    块设备驱动程序

    为什么需要块设备驱动

    块设备驱动程序是针对类似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开始分析,该文件在fsuffer.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.

    这个涉及到了具体的例子,可以参考下例子driverslockxd.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
      ", ++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
    ", ++cnt);
        while ((req = elv_next_request(q)) != NULL) {
            ....
            end_request(req, 1);
        }
    
    }
    

    完整的程序

    /* 参考:
     * driverslockxd.c
     * driverslockz2ram.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
    ", ++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
      ", ++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
      

    完整代码

    /* 参考:
     * driverslockxd.c
     * driverslockz2ram.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
    ", ++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
    ", ++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
    ", ++r_cnt);
    			memcpy(req->buffer, ramblock_buf+offset, len);
    		}
    		else
    		{
                printk("do_rambloc_request read %d
    ", ++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)
      
      
  • 相关阅读:
    LeetCode 45 Jump Game II
    LeetCode 54. Spiral Matrix
    LeetCode 53. Maximum Subarray
    LeetCode 52. N-Queens II
    智齿的秘密
    《婚姻故事》观影笔记
    为什么在linux系统下安装anaconda的时候会报错
    pandas时间序列学习笔记
    极大似然估计和最小二乘法
    粗糙集学习笔记
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10155345.html
Copyright © 2020-2023  润新知