精华内容
下载资源
问答
  • 各个层次说明由通用层(Generic Block Layer)负责维持一个I/O请求在上层文件系统与底层物理磁盘之间的关系。在通用层中,通常用一个bio结构体来对应一个I/O请求。 在Linux中,驱动对设备的输入或输出

    一、块设备框架

    以下内容摘自http://blog.csdn.net/jianchi88/article/details/7212370.

    1、块设备框架图

    块设备框架图

    avatar

    2、说明

    • 各个层次说明

      • 由通用块层(Generic Block Layer)负责维持一个I/O请求在上层文件系统与底层物理磁盘之间的关系。在通用块层中,通常用一个bio结构体来对应一个I/O请求。
      • 在Linux中,驱动对块设备的输入或输出(I/O)操作,都会向块设备发出一个请求,在IO调度层中用request结构体描述。
      • 但对于一些磁盘设备而言请求的速度很慢,这时候内核就提供一种队列的机制把这些I/O请求(request结构体)添加到队列中(即:请求队列),在驱动中用request_queue结构体描述。在向块设备提交这些请求前内核会先执行请求的合并和排序预操作,以提高访问的效率,然后再由内核中的I/O调度程序子系统来负责提交 I/O 请求, 调度程序将磁盘资源分配给系统中所有挂起的块 I/O 请求,其工作是管理块设备的请求队列,决定队列中的请求的排列顺序以及什么时候派发请求到设备。
      • Linux提供了一个gendisk数据结构体,用来表示一个独立的磁盘设备或分区,用于对底层物理磁盘进行访问。在gendisk中有一个类似字符设备中file_operations的硬件操作结构指针,是block_device_operations结构体。
    • 整体数据流程说明
      文件系统——》通用块层——》IO调度层——》块设备驱动层

      • 文件系统会向通用块层发起块读写请求
      • 通用块层将读写请求封装成bio结构体(block io),下发到IO调度层
      • IO调度层将bio封装成request结构体,并将request添加到对应块设备(gendisk)的请求队列(request_queue)中,并做一些预处理。
      • IO调度层对请求队列(request_queue)中的request进行预处理之后,调用请求队列(request_queue)的回调函数来对队列中的IO(request)进行处理
      • 而请求队列(request_queue)以及request处理回调函数则是由块设备驱动层提供,也就是块设备驱动层会从请求队列(request_queue)提取request并且进行实质性的处理。
    • 各个层次的请求(IO)以及请求队列对应的结构体

      • 通用块层:struct bio
      • IO调度层:struct request(封装了struct bio)、struct request_queue
      • 块设备驱动层:
        各类块设备驱动层对于IO请求的封装是不一样的。以sd card的mmc块设备驱动层为例:
        struct mmc_queue_req(封装了struct request)、struct mmc_queue(封装了struct request_queue)。

    3、mmc块设备驱动层

    块设备驱动有很多个种类,对于sd card来说,使用的是mmc块设备驱动层。
    这部分内容可以参考《[mmc subsystem] 概念与框架
    mmc块设备驱动层已经属于mmc subsystem结构的一部分。其框架图如下:

    avatar
    (上图摘自蜗窝科技http://www.wowotech.net/comm/mmc_framework_arch.html

    • 其主要分成三个层次:
      • mmc card drivers层(mmc_blk层)
        向上:为mmc card(如sd card)实现对应的request_queue以及gendisk并注册,生成的对应的块设备文件。
        请求处理:对上层传下来的request进行提取处理
        向下:向mmc core层提交mmc请求
      • mmc core层(只提取部分内容)
        可以参考《[mmc subsystem] mmc core(第一章)——概述
        向上:为mmc card drivers提供注册mmc driver的接口。为mmc card drivers提供发起mmc请求的接口。
        请求处理:异步mmc请求的处理
        向下:向mmc host层提交mmc请求
      • mmc host层(只提取部分内容)
        可以参考《[mmc subsystem] host(第一章)——概述
        对mmc core下发的mmc请求进行处理,然后通过mmc总线发起实际的通讯内容。

    5、总结

    综上,对于sd card而言,整个数据流是:
    文件系统——》通用块层——》IO调度层——》mmc_blk层——》mmc core层——》mmc host层——》mmc硬件总线——》sd card。
    而这里我们要学习的就是mmc_blk层。
    对应代码是drivers/mmc/card/block.c、driver/mmc/card/queue.c
    同时,我们也知道了mmc_blk层的核心内容是实现块设备,为了实现块设备,实现块设备的核心就是要实现对应的gendisk以及request_queue。后续就是围绕这个内容对mmc_blk设备驱动进行展开。

    二、mmc_blk实现块设备核心框架

    1、实现块设备的驱动框架

    通过第一节的内容,可以知道实现块设备主要是实现如下内容

    • 创建struct request_queue
      请求队列,IO调度层会把IO请求(struct request)往对应块设备的这个结构体里面挂,然后调用request_queue里面的回调函数对request进行处理。
      可以调用blk_init_queue来创建一个request_queue。
      创建方法eg:blk_init_queue(mmc_request_fn, lock),其中,mmc_request_fn则是从request_queue提取request并进行处理的回调函数。

    • 创建struct gendisk
      struct gendisk用来代表一个独立的磁盘设备或者分区。一个块设备对应一个gendisk。块设备驱动通过创建对应的gendisk并注册来产生对应的块设备节点。
      可以调用alloc_disk来分配一个gendisk。
      分配方法eg:alloc_disk(perdev_minors),perdev_minors表示分配给每个块设备的从设备号数量

    • 初始化struct gendisk
      主要初始化如下内容:

      • struct gendisk->major,块设备的主设备号,对于mmcblk块设备来说,其主设备号是MMC_BLOCK_MAJOR,179
      • struct gendisk->first_minor,分配的第一个从设备号
      • struct gendisk->fops,块设备的操作函数,对于mmcblk来说,是mmc_bdops
      • struct gendisk->private_data,私有数据指针,一般关联到对应块设备驱动的数据结构体
      • struct gendisk->driverfs_dev,父sys节点
      • struct gendisk->flags
      • struct gendisk->disk_name,块设备名
    • 关联struct gendisk和struct request_queue
      因为IO调度层里面的request_queue一般有不止一个。当数据从通用块层下来的时候,IO调度层只知道要写到哪个gendisk,然后根据gendisk里面获取到对应的request_queue,然后再request往request_queue里面放。
      所以就要求gendisk必须和request_queue关联起来。
      关联方法:struct gendisk->queuerequest_queue.

    • 注册块设备
      不管前面的流程怎么样,最终的目的都是注册一个块设备到系统中。
      可以通过调用add_disk(gendisk)来注册块设备。

    2、mmc_blk实现块设备的驱动框架补充

    核心框架就是上述“实现块设备的驱动框架”的部分。

    以下要特别注意,方便理解代码。

    • 根据mmc_blk的实现方式,有如下注意点:

      • 构造mmc_queue_req作为一个MMC IO请求,把从request_queue提取request封装到这里面来。
      • 构造mmc_queue作为块设备的请求队列,把request_queue封装到mmc_queue中。同时存储了当前正在处理以及上一次正在处理的MMC IO请求mmc_queue_req。
      • 构造mmc_blk_data作为块设备的私有数据,同时把mmc块设备请求对应mmc_queue以及块设备gendisk存储到mmc_blk_data中。
      • 同时mmc_blk_data也存放了mmc_blk驱动相关的部分内容。
    • 因此mmc_blk实现块设备的整体流程如下

      • 创建struct mmc_blk_data
      • 初始化mmc_blk_data
      • 初始化struct mmc_blk_data->mmc_queue
      • 创建struct mmc_blk_data->mmc_queue->request_queue(创建struct request_queue)
      • 分配和初始化struct mmc_blk_data->gendisk(创建struct gendisk & 初始化struct gendisk)
      • 关联struct mmc_blk_data->gendisk 和 mmc_blk_data->mmc_queue->request_queue (关联struct gendisk和struct request_queue)
      • 注册mmc_blk_data->gendisk到系统中 (注册块设备)

    三、mmc_blk的设备驱动模型实现

    对应代码:drivers/mmc/card/block.c

    1、mmc bus结构

    注意,这里指的是软件结构,而不是指硬件结构。
    建议先参考《[mmc subsystem] mmc core(第三章)——bus模块说明
    mmc结构体如下图所示

    avatar
    (上图摘自蜗窝科技http://www.wowotech.net/comm/mmc_framework_arch.html

    从硬件上来看,每一个mmc host对应一条实际的mmc总线。
    但是mmc subsystem只存在一条虚拟的mmc bus。并且mmc host并不会作为这个设备驱动总线模型的一个部分。
    相应的:
    * 在mmc bus上挂载的device是由mmc core根据实际mmc设备抽象出来的card设备。
    例如在sd.c中,mmc_attach_sd会构造mmc_card,并且调用mmc_add_card(struct mmc_card *card)将mmc_card挂在了mmc_bus上。
    而mmc_card就是mmc_bus上的device。

    • 在mmc bus上挂载的driver是在card目录下实现的card driver,用于驱动虚拟card设备、对接其他subsystem,实现其实际的功能。
      例如在block.c中,mmc_blk_init会构造一个mmc_driver,并且调用mmc_register_driver(struct mmc_driver *drv)将mmc_driver挂在了mmc_bus上。
      而mmc_driver就是mmc_bus上driver。

    根据mmc_bus的匹配规则(mmc_bus_match),只要有mmc_card注册到mmc_bus上,就会匹配所有mmc_driver,也就是所有mmc_driver的probe都会执行。
    然后mmc_driver就开始为mmc_card实现对应的功能了,比如mmc_blk这个mmc_driver就是为mmc_card实现块设备的功能。

    2、mmc_blk驱动模型结构实现

    • 根据上述,mmc_blk驱动模型结构要包括如下两点

      • 构造对应的struct mmc_driver
      • 调用mmc_register_driver(struct mmc_driver *drv)将其注册到mmc_bus上
    • 代码如下

    static struct mmc_driver mmc_driver = {
        .drv        = {
            .name   = "mmcblk",
        },
        .probe      = mmc_blk_probe,
        .remove     = mmc_blk_remove,
        .suspend    = mmc_blk_suspend,
        .resume     = mmc_blk_resume,
        .shutdown   = mmc_blk_shutdown,
    };
    
    static int __init mmc_blk_init(void)
    {
        res = mmc_register_driver(&mmc_driver);   
             // mmc_driver注册到mmc_bus上,当有mmc_card注册到mmc_bus上时,其probe方法就会执行。
    }
    
    module_init(mmc_blk_init);

    随后可以查询到mmkblk driver的节点为/sys/bus/mmc/drivers/mmcblk,内容如下

    root@:/sys/bus/mmc/drivers/mmcblk # ls
    ls
    bind
    mmc0:0001    // 通过该driver probe成功的mmc_card,这里是一个emmc设备
    mmc1:e624    // 通过该driver probe成功的mmc_card,这里是一个sd设备
    uevent
    unbind

    mmc_blk_probe就是为mmc_card(比如sd card、emmc)实现存储设备的功能的核心,也就是将mmc_card实现出对应块设备的实现所在,也就是这里要学习的入口函数。后续会从mmc_blk_probe展开对“mmc_blk层为sd card创建块设备流程”的代码说明。

    四、mmc_blk层为sd card创建块设备流程(mmc_blk_probe)

    0、节点

    • 块设备的dev节点
    root@:/dev/block # ls -l | grep mmcblk
    brw------- root     root     179,   0 1970-01-03 03:46 mmcblk0
    brw------- root     root     179,   1 1970-01-03 03:46 mmcblk0p1
    brw------- root     root     179,  10 1970-01-03 03:46 mmcblk0p10
    brw------- root     root     179,  11 1970-01-03 03:46 mmcblk0p11
    brw------- root     root     179,  12 1970-01-03 03:48 mmcblk0p12
    brw------- root     root     179,  13 1970-01-03 03:46 mmcblk0p13
    ...
    brw------- root     root     179,  32 1970-01-03 03:46 mmcblk0rpmb
    brw------- root     root     179,  64 1970-01-03 03:46 mmcblk1
    brw------- root     root     179,  65 1970-01-03 03:46 mmcblk1p1

    可以观察到设备都是以mmcblk为前缀,并且主设备号为179,从设备号则是依次递增。

    • 块设备的sys节点
    root@msm8916_64:/sys/bus/mmc/devices/mmc1:e624/block/mmcblk1 # ls
    alignment_offset    bdi   bkops_check_threshold   capability
    dev    device   discard_alignment   ext_range   force_ro
    holders    inflight   mmcblk1p1   no_pack_for_random   num_wr_reqs_to_start_packing
    power    queue   range   removable   ro
    ...

    1、整体核心流程说明

    mmc_blk层为sd card创建块设备流程是在mmc_blk_probe中实现的。
    复习一下前面的内容,mmc_blk_probe主要做如下事情
    1. 创建struct mmc_blk_data
    2. 初始化mmc_blk_data
    3. 初始化struct mmc_blk_data->mmc_queue
    4. 创建struct mmc_blk_data->mmc_queue->request_queue(创建struct request_queue)
    5. 分配和初始化struct mmc_blk_data->gendisk(创建struct gendisk & 初始化struct gendisk)
    6. 关联struct mmc_blk_data->gendisk 和 mmc_blk_data->mmc_queue->request_queue (关联struct gendisk和struct request_queue)
    7. 注册mmc_blk_data->gendisk到系统中 (注册块设备)

    后面看代码的过程中可以发现整个probe过程都是围绕上述工作进行的。

    2、mmc_blk_probe

    只考虑sd card的情况,分析和sd card相关的代码。

    static int mmc_blk_probe(struct mmc_card *card)
    {
        struct mmc_blk_data *md, *part_md;
        char cap_str[10];
    
        /*
         * Check that the card supports the command class(es) we need.
         */
    /** 判断card是不是一个block设备 **/
        if (!(card->csd.cmdclass & CCC_BLOCK_READ))   
            return -ENODEV;
            // 前面说过了,只要是mmc_card(包括SDIO card)被注册到mmc_bus上,那么所有mmc_bus上的mmc_driver都会被匹配到
            // 而mmc_blk只使用于存储设备(emmc、sd card、mmc card),并不能驱动于SDIO card
            // 因此,这里根据是否支持块读写属性判断card是不是一个存储设备,如果不是的话,说明并不能使用mmc_blk这个mmc_driver来驱动mmc_card.
    
    /** 为mmc_card分配和设置mmc_blk_data **/
    /** 在mmc_blk_alloc中会去分配和设置mmc_queue、request_queue、gendisk **/
        md = mmc_blk_alloc(card);   
          // 负责第1小节中的步骤1-6的工作,所以也是分析“mmc_blk层为sd card创建块设备流程”的核心函数
            // 1. 创建struct mmc_blk_data
            // 2. 初始化mmc_blk_data
            // 3. 初始化struct mmc_blk_data->mmc_queue
            // 4. 创建struct mmc_blk_data->mmc_queue->request_queue(创建struct request_queue)
            // 5. 分配和初始化struct mmc_blk_data->gendisk(创建struct gendisk & 初始化struct gendisk)
            // 6. 关联struct mmc_blk_data->gendisk 和 mmc_blk_data->mmc_queue->request_queue (关联struct gendisk和struct request_queue)
        if (IS_ERR(md))
            return PTR_ERR(md);
    
    /** 一个存储设备上(例如emmc)上可能有多个物理分区,这里用于为这些物理分区(例如rpmb分区)分配和设置mmc_blk_data **/
    /** SD card上只有一个物理分区,所以我们这里不care,后面学习emmc的时候再说明 **/
        if (mmc_blk_alloc_parts(card, md))
            goto out;
    
    /** 关联mmc_card和mmc_blk_data **/
        mmc_set_drvdata(card, md);
        mmc_fixup_device(card, blk_fixups);
    
    /** 将mmc_blk构造的gendisk注册到系统中,生成对应的块设备 **/
        if (mmc_add_disk(md))
            // 负责第1小节中的步骤7的工作
            // 注册mmc_blk_data->gendisk到系统中 (注册块设备)
            goto out;
    
    /** 将其他物理分区的gendisk注册到系统中,生成对应的块设备。 **/
    /** sd card 上只有一个分区,所以这里我们同样不关心 **/
        list_for_each_entry(part_md, &md->part, part) {
            if (mmc_add_disk(part_md))
                goto out;
        }
        return 0;
    }

    通过上述代码,可以知道对于“mmc_blk层为sd card创建块设备流程”来说,有两个核心代码来实现。

    • mmc_blk_alloc
    • mmc_add_disk

    后续主要分析这两个函数的代码。

    3、mmc_blk_alloc

    需要实现如下内容:
    1. 创建struct mmc_blk_data
    2. 初始化mmc_blk_data
    3. 初始化struct mmc_blk_data->mmc_queue
    4. 创建struct mmc_blk_data->mmc_queue->request_queue(创建struct request_queue)
    5. 分配和初始化struct mmc_blk_data->gendisk(创建struct gendisk & 初始化struct gendisk)
    6. 关联struct mmc_blk_data->gendisk 和 mmc_blk_data->mmc_queue->request_queue (关联struct gendisk和struct request_queue)

    代码如下,去掉一些无关内容:

    static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
    {
        sector_t size;
        struct mmc_blk_data *md;
    
    /** 以下先获取card容量,以扇区为单位 **/
        if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
            size = card->ext_csd.sectors;      // 对于emmc设备来说,其容量是从ext_csd寄存器的sectors域获取
        } else {
            size = card->csd.capacity << (card->csd.read_blkbits - 9);
                    // 对于sd card来说,其容量是从csd的capacity域获取的
                    // 计算方法memory capacity = (C_SIZE+1) * 512K byte  
                    // 可以参考SD 3.0协议中的5.3.3节
        }
    
    /** 调用mmc_blk_alloc_req来实现前面所说的工作 **/
        md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
                        MMC_BLK_DATA_AREA_MAIN);
            // 参数说明如下:
            // card:对应的mmc设备,mmc_card
            // card->dev:作为块设备的sys节点的父设备,/sys/bus/mmc/devices/mmc1:e624/block/mmcblk1,这里的mmc1:e624就是card->dev
            // size:块设备的大小,也就是card的容量
            // false:bool default_ro,默认并不作为只读设备
            // NULL:subname,块设备的后缀名,例如mmcblk0rpmb,rpmb就是后缀名
            // MMC_BLK_DATA_AREA_MAIN:分区类型,UDA分区
    
        return md;
    }
    
    /-----------------------------mmc_blk_alloc_req实现------------------------------/
    static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
                              struct device *parent,
                              sector_t size,
                              bool default_ro,
                              const char *subname,
                              int area_type)
    {
        struct mmc_blk_data *md;
        int devidx, ret;
        unsigned int percentage =
            BKOPS_SIZE_PERCENTAGE_TO_QUEUE_DELAYED_WORK;
    
    /** 分配一个mmcblk的从设备号 **/
        devidx = find_first_zero_bit(dev_use, max_devices);
        __set_bit(devidx, dev_use);
    
    /** 以下就是分配mmc_blk_data并进行设置,也是mmc_blk_alloc_req的核心工作 **/
    /* 对应“1. 创建struct mmc_blk_data” */
        md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
    
    /* 以下对应“2. 初始化mmc_blk_data” */
       //---- 设置mmc_blk_data->name_idx
        if (!subname) {
            md->name_idx = find_first_zero_bit(name_use, max_devices);
            __set_bit(md->name_idx, name_use);
        } else
            md->name_idx = ((struct mmc_blk_data *)
                    dev_to_disk(parent)->private_data)->name_idx;
            // 从name_use中分配一个名称的id号,mmcblk+id号会作为完整的块设备的名称
            // 例如mmcblk0的0就是名称的id号,mmcblk1的1就是名称的id号
    
       //---- 设置mmc_blk_data->area_type
        md->area_type = area_type;   // 物理分区类型,SD card只有一个物理分区,对应为MMC_BLK_DATA_AREA_MAIN
    
       //---- 设置mmc_blk_data->read_only 
        md->read_only = mmc_blk_readonly(card);   // 设置只读属性
    
       //---- 分配mmc_blk_data->disk
        md->disk = alloc_disk(perdev_minors);   // 调用alloc_disk分配一个gendisk结构体
    
       //---- 设置mmc_blk_data->lock、part、usage 
        spin_lock_init(&md->lock);   // 给request_queue使用的锁
        INIT_LIST_HEAD(&md->part);   // 挂载其他物理分区的链表
        md->usage = 1;   // 使用计数设置为1
    
       //---- 核心:设置mmc_blk_data->queue,会调用mmc_init_queue来分配和设置mmc_blk_data->queue,在这里面会创建对应的request_queue.
    /* 以下对应“3. 初始化struct mmc_blk_data->mmc_queue” */
    /* 以下对应“4. 创建struct mmc_blk_data->mmc_queue->request_queue(创建struct request_queue)” */
        ret = mmc_init_queue(&md->queue, card, &md->lock, subname);   // 后面说明
        md->queue.issue_fn = mmc_blk_issue_rq;   // 将mmc_queue的IO请求(request)下发方法设置为mmc_blk_issue_rq
        md->queue.data = md;   // 关联mmc_queue和mmc_blk_data
    
       //---- 核心:设置mmc_blk_data->disk
    /* 以下对应“5. 分配和初始化struct mmc_blk_data->gendisk(创建struct gendisk & 初始化struct gendisk)” */
            // 分配gendisk放在前面完成了
        md->disk->major = MMC_BLOCK_MAJOR;   // 设置块设备的主设备号为MMC_BLOCK_MAJOR,179
        md->disk->first_minor = devidx * perdev_minors;   // 设置块设备的从设备号
        md->disk->fops = &mmc_bdops;   // 设置操作集
        md->disk->private_data = md;   // 关联gendisk和mmc_blk_data
    /* 以下对应“6. 关联struct mmc_blk_data->gendisk 和 mmc_blk_data->mmc_queue->request_queue (关联struct gendisk和struct request_queue)” */
        md->disk->queue = md->queue.queue;  // 重要,关联gendisk和request_queue!!!
        md->disk->driverfs_dev = parent;
        set_disk_ro(md->disk, md->read_only || default_ro);   // 设置gendisk的只读属性
        md->disk->flags = GENHD_FL_EXT_DEVT;   // 设置gendisk的一些标识
        if (area_type & MMC_BLK_DATA_AREA_RPMB)
            md->disk->flags |= GENHD_FL_NO_PART_SCAN;
        snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),   // 设置块设备的设备名,例如mmcblk0、mmcblk1,而mmcblk0rpmb中的rpmb则是指subname
             "mmcblk%d%s", md->name_idx, subname ? subname : "");
        // 设置逻辑块大小,这里先不关心
        if (mmc_card_mmc(card))
            blk_queue_logical_block_size(md->queue.queue, card->ext_csd.data_sector_size);
        else
            blk_queue_logical_block_size(md->queue.queue, 512);
        set_capacity(md->disk, size);   // 设置gendisk的容量,size是以扇区为单位。
        return md;
    }
    
    /-----------------------------mmc_init_queue实现------------------------------/
    // 两个重要的功能
    // 1、创建request_queue并封装到mmc_queue中
    // 2、创建IO请求(request)的提取和处理进程
    int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
               spinlock_t *lock, const char *subname)
    {
        struct mmc_host *host = card->host;
        u64 limit = BLK_BOUNCE_HIGH;
        int ret;
        struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
        struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
    
        if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
            limit = *mmc_dev(host)->dma_mask;
    
        mq->card = card;
    /** 创建struct mmc_blk_data->mmc_queue->request_queue(创建struct request_queue) **/
        mq->queue = blk_init_queue(mmc_request_fn, lock);   // request的处理回调函数设置为mmc_request_fn
        if (!mq->queue)
            return -ENOMEM;
    
        if ((host->caps2 & MMC_CAP2_STOP_REQUEST) &&
                host->ops->stop_request &&
                mq->card->ext_csd.hpi_en)
            blk_urgent_request(mq->queue, mmc_urgent_request);
    
        mq->mqrq_cur = mqrq_cur;
        mq->mqrq_prev = mqrq_prev;
        mq->queue->queuedata = mq;
        mq->num_wr_reqs_to_start_packing =
            min_t(int, (int)card->ext_csd.max_packed_writes,
                 DEFAULT_NUM_REQS_TO_START_PACK);
    
        blk_queue_prep_rq(mq->queue, mmc_prep_request);
        queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
        if (mmc_can_erase(card))
            mmc_queue_setup_discard(mq->queue, card);
    
    success:
        sema_init(&mq->thread_sem, 1);
    
    //---- 创建IO请求(request)的提取和处理进程
        mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s", host->index, subname ? subname : "");
    
        if (IS_ERR(mq->thread)) {
            ret = PTR_ERR(mq->thread);
            goto free_bounce_sg;
        }
        return 0;
    }

    另外,重点关注一下以下几个东西。在mmcblk块设备读写流程的时候会用到。

    md->queue.issue_fn = mmc_blk_issue_rq;
    md->disk->queue = md->queue.queue;
    (mmc_blk_data->mmc_queue) mq->queue = blk_init_queue(mmc_request_fn, lock); 
    (mmc_blk_data->mmc_queue) mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s", host->index, subname ? subname : "");

    4、mmc_add_disk

    static int mmc_add_disk(struct mmc_blk_data *md)
    {
        int ret;
        struct mmc_card *card = md->queue.card;
    
    /** 注册mmc_blk_data->gendisk到系统中 (注册块设备) **/
        add_disk(md->disk);    // 直接调用add_disk将mmc_blk_data->gendisk注册到系统中
            // 此处略掉很多关于属性的代码
    
        return ret;
    }

    五、补充说明,软件分区对应块设备的创建

    在存储设备上有软件分区,例如emmc上可能有很多个软件分区,而sd card上只有一个软件分区,他们也有自己的块设备节点,如下:

    brw------- root     root     179,   1 1970-01-03 03:46 mmcblk0p1
    brw------- root     root     179,  10 1970-01-03 03:46 mmcblk0p10
    brw------- root     root     179,  11 1970-01-03 03:46 mmcblk0p11
    brw------- root     root     179,  12 1970-01-03 03:48 mmcblk0p12
    brw------- root     root     179,  13 1970-01-03 03:46 mmcblk0p13
    ...
    brw------- root     root     179,  65 1970-01-03 03:46 mmcblk1p1

    这些都是软件分区对应的块设备节点,但是这些块设备节点是在对mmcblk进行add_disk的时候,从mmcblk上识别出来的,由通用块设备层自己完成,而不需要mmcblk层的关心。
    具体代码在kernel/block/partitions/check.c check_partition()中。
    调用流程可以参考如下调用堆栈:

    [    7.442971] ooonebook check_partition check gendisk mmcblk1
    [    7.442977] CPU: 3 PID: 49 Comm: kworker/u8:3 Tainted: G        W    3.10.49-g5e97c06-dirty #64
    [    7.442988] Workqueue: kmmcd mmc_rescan
    [    7.442989] Call trace:
    [    7.442997] [<ffffffc000206b40>] dump_backtrace+0x0/0x270
    [    7.443002] [<ffffffc000206dc0>] show_stack+0x10/0x1c
    [    7.443007] [<ffffffc000c2ad74>] dump_stack+0x1c/0x28
    [    7.443012] [<ffffffc00043c22c>] check_partition+0x34/0x20c      // 在这里识别软件分区
    [    7.443016] [<ffffffc00043bf6c>] rescan_partitions+0x90/0x270
    [    7.443022] [<ffffffc00032ba84>] __blkdev_get+0x13c/0x320
    [    7.443026] [<ffffffc00032be0c>] blkdev_get+0x1a4/0x2c8
    [    7.443031] [<ffffffc000439bb8>] add_disk+0x3b8/0x3e8   // 在这里把mmc_blk层创建的gendisk注册到系统中
    [    7.443036] [<ffffffc00089b3b8>] mmc_add_disk+0x44/0x228
    [    7.443040] [<ffffffc00089ba1c>] mmc_blk_probe+0x224/0x270
    [    7.443044] [<ffffffc00088c9d4>] mmc_bus_probe+0x14/0x20
    [    7.443050] [<ffffffc0005bc7d8>] driver_probe_device+0x160/0x368
    [    7.443055] [<ffffffc0005bca08>] __device_attach+0x28/0x4c
    [    7.443060] [<ffffffc0005baa28>] bus_for_each_drv+0x70/0x90
    [    7.443064] [<ffffffc0005bc5ec>] device_attach+0x68/0x98
    [    7.443069] [<ffffffc0005bb9fc>] bus_probe_device+0x28/0xa0
    [    7.443073] [<ffffffc0005b9d90>] device_add+0x324/0x600
    [    7.443077] [<ffffffc00088d208>] mmc_add_card+0x2a8/0x36c
    [    7.443082] [<ffffffc000893758>] mmc_attach_sd+0x204/0x25c
    [    7.443086] [<ffffffc00088c8cc>] mmc_rescan+0x25c/0x2ec
    [    7.443091] [<ffffffc0002359e0>] process_one_work+0x258/0x3d0
    [    7.443095] [<ffffffc000236ac4>] worker_thread+0x1f0/0x340
    [    7.443100] [<ffffffc00023bc94>] kthread+0xac/0xb8

    这里就没有进行深入的学习了。

    展开全文
  • 块代码编程---声明和创建块代码

    千次阅读 2011-10-27 15:09:27
    声明一个代码的引用 代码变量用来保存到代码的引用。使用和声明函数指针相识的语法声明代码变量,只是把其中的*换成占位符^。代码类型基本上都是C语言的格式。下面就是一些有效的代码变量声明: ...
    
    

    声明一个块代码的引用

    块代码变量用来保存到块代码的引用。使用和声明函数指针相识的语法声明块代码变量,只是把其中的*换成占位符^。块代码类型基本上都是C语言的格式。下面就是一些有效的块代码变量声明:

    void (^blockReturningVoidWithVoidArgument)(void);
    int (^blockReturningIntWithIntAndCharArguments)(int, char);
    void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);

    块代码同样支持可变参数列表。没有参数的块代码必须在参数列表中使用void。

    块代码在传入参数,返回值等方面使用元数据验证,在设计上达到数据安全的目的。你可以把一个块代码的引用转换成一个任何类型的指针,反之亦然。但是你不能通过一个指针引用来对一个块代码引用进行解引用--因为在编译时期不能计算块代码所占大小。

    你可以创建块代码的类型--只是你在多个地方创建同一种类型的块代码的最好的方式:

    typedef float (^MyBlockType)(float, float);
     
    MyBlockType myFirstBlock = // ... ;
    MyBlockType mySecondBlock = // ... ;

    创建块代码

    使用^标示一个块代码的开始。可能后面跟着使用一对小括号括起来的参数列表。块代码的代码部分是在一对大括号里面。下面是一个块代码定义的示例,并把这个块代码赋值给一个前面声明的块代码变量--和C语言一样,块代码使用一个分号;作为定义的结束:

    int (^oneFrom)(int);
     
    oneFrom = ^(int anInt) {
        return anInt - 1;
    };

    如果你在块代码定义中没有标示返回值类型,返回值类型会被通过块代码的内容推断出来,如果这个返回值是推断出来的,并且参数列表是void,你可以省略这个void。如果多个return语句出现,他们必须返回完全相同类型的值(合适的时候必须使用转换)。

    全局块代码

    在文件作用域内,你可以定义一个块代码具有全局可用的:

    #import <stdio.h>
     
    int GlobalInt = 0;
    int (^getGlobalInt)(void) = ^{ return GlobalInt; };
    展开全文
  • 在张帆老师的教程中,有VC6.0+ObjectArx2002创建对话框的详细指导,但由于项目需要,开发环境...本文将创建一个在对话框中查看定义图标的程序,通过对实现流程和源代码的讲解,详细介绍ObjectArx2010+VC2008开发MFC

    在张帆老师的教程中,有VC6.0+ObjectArx2002创建对话框的详细指导,但由于项目需要,开发环境选择了Win7+VC2008+ObjectArx2010+CAD2010,虽然基本思路和流程依然可以参考张帆老师的教程,但事过境迁,完全照着教程做已经走不通了!本文将创建一个在对话框中查看块定义图标的程序,通过对实现流程和源代码的讲解,详细介绍ObjectArx2010+VC2008开发MFC对话框程序的方法和过程。

    要实现在对话框中显示块定义的缩略图,就需要读者熟悉VC位图编程的基本知识,你也可以参考文章VC打开并缩放显示位图(.BMP)文件的方法以及VC编程实现位图处理类(显示位图、加载位图、获取/设置像素点颜色)。本文还用到了CComboBox控件罗列当前图形中用户定义的所有块表记录,关于CComboBox的更多用法,读者可以参考MFC程序使用CComboBox控件详解。好了,准备工作已经完成,我们正式开始创建属性定义查看对话框。

    Step 1:新建项目

    创建一个ObjectArx项目,点击MFC Support选项卡,在MFC Support的设置选中Extension DLL Using Shared DLL,如图所示:

    新建ObjectArx2010项目


    其余设置保持默认,点击Finish完成项目的创建。

    Step 2:添加对话框资源

    在新建的项目上点击鼠标右键,在添加子菜单选择资源,添加一个Dialog资源到项目中,对话框的最终布局如图:



    ObjectArx2011块属性查看对话框

    各控件的ID设置为:

           对话框:IDD_BLKPREVIEWICON。
           列表框:IDC_BLKDEF_LIST。
           图片框:IDC_BITMAP。
           块参照数量文本框:IDC_NUM_BLKDEF。
           是否包含属性文本框:IDC_HAS_ATT。
           “关闭”按钮:IDCANCEL。

    Step 3: 添加对话框类

    在完成对话框的布局和ID设置以后,就应该开始设计对话框管理的类了,由于本人的ObjectArx2010与VS2008中文版没有很好的兼容,张帆老师所说的“ObjectArx MFC Support”工具栏没有找到,于是我们先创建普通的对话框,最后手动将基类CDialog改成CAcUiDialog即可。在对话框设计区域点右键,选择“添加类”,类名设置成“CPreviewBlkDlg",基类选择CDialog,点完成。

    Step 4: 添加控件关联的变量

    逐个右击窗体中的控件,在弹出菜单中点”添加变量“,最终的结果如下: 

    1
    2
    3
    4
    DDX_Control(pDX, IDC_BLKDEF_LIST, m_lstBlkDef);
    DDX_Control(pDX, IDC_BITMAP, m_bitmap);
    DDX_Text(pDX, IDC_NUM_BLKDEF, m_strBlkRefNum);
    DDX_Text(pDX, IDC_HAS_ATT, m_strHasAttribute);

    变量类型:

    1
    2
    3
    4
    CComboBox m_lstBlkDef;
    CStatic m_bitmap;
    CString m_strBlkRefNum;
    CString m_strHasAttribute;

    Step 5: 添加实现函数

    5.1 重写窗体的初始化虚函数OnInitDialog,实现程序启动时在CComboBox控件中加载当前图形的块定义列表,你可以使用向导或者手动添加的方式完成。OnInitDialog函数的完整代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    BOOL CPreviewBlkDlg::OnInitDialog()
    {
        CAcUiDialog::OnInitDialog();
     
        // 获得当前图形的块表
        AcDbBlockTable *pBlkTbl;
        AcDbBlockTableRecord *pBlkTblRcd;
        acdbHostApplicationServices()
            ->workingDatabase()
            ->getBlockTable(pBlkTbl,AcDb::kForRead);
     
        //遍历块表,获得用户自定义块表记录的名称,将其添加到组合框中
        AcDbBlockTableIterator *pItr;
        pBlkTbl->newIterator(pItr);
        for(pItr->start();!pItr->done();pItr->step()){
            pItr->getRecord(pBlkTblRcd,AcDb::kForRead);
     
            ACHAR* pName;
            pBlkTblRcd->getName(pName);
            CString strName(pName);
            acutDelString(pName);
     
            if(strName.Compare(ACDB_MODEL_SPACE)!=0&&
                strName.Compare(ACDB_PAPER_SPACE)!=0&&
                strName.Compare(_T("*Paper_Space0"))!=0){
                    m_lstBlkDef.AddString(strName);
            }
            pBlkTblRcd->close();
        }
        delete pItr;
        pBlkTbl->close();
     
        return TRUE;
    }

    这段代码展示了获得当前图形块表、遍历块表以及组合框控件CComboBox添加元素的方法。

    5.2 添加CComboBox控件的CBN_SELCHANGE消息响应函数OnCbnSelchangeBlkdefList,处理CComboBox控件选定内容更改事件,OnCbnSelchangeBlkdefList函数的完整代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    void CPreviewBlkDlg::OnCbnSelchangeBlkdefList()
    {
        //显示预览图标
        CPaintDC dc(this);
        AcDbBlockTable *pBlkTbl;
        acdbHostApplicationServices()
            ->workingDatabase()
            ->getBlockTable(pBlkTbl,AcDb::kForRead);
     
        AcDbObjectId blkTblRcdId;
        CString strBlkDefName;
        m_lstBlkDef.GetLBText(m_lstBlkDef.GetCurSel(),strBlkDefName);
        Acad::ErrorStatus es=pBlkTbl->getAt(strBlkDefName,blkTblRcdId);
     
        HBITMAP hBitmap=BlockIconToBMP(blkTblRcdId,dc.GetSafeHdc());
     
        m_bitmap.SetBitmap(hBitmap);
     
        //获得块表记录的指针
        AcDbBlockTableRecord *pBlkTblRcd;
        pBlkTbl->getAt(strBlkDefName,pBlkTblRcd,AcDb::kForRead);
     
        //获得块参照的数量
        AcDbBlockReferenceIdIterator *pItr;
        pBlkTblRcd->newBlockReferenceIdIterator(pItr);
        int number=0;
        for(pItr->start();!pItr->done();pItr->step()){
            number++;
        }
        m_strBlkRefNum.Format(_T("%d"),number);
     
        //获得块参照是否包含属性
        if(pBlkTblRcd->hasAttributeDefinitions()){
            m_strHasAttribute=_T("是");
        }else{
            m_strHasAttribute=_T("否");
        }
     
        pBlkTblRcd->close();
        pBlkTbl->close();
     
        UpdateData(FALSE);
    }

    这段代码展示了在对话框中显示位图的简单方法,也给出了获取当前图形中指定块参照数量的具体实现。在代码中,使用了函数BlockIconToBMP根据ID获取指定块定义的缩略图,下面将给出函数BlockIconToBMP的完整代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    // 获得指定的块表记录的预览图像,输入参数:块表记录的ID、设备描述表句柄
    HBITMAP CPreviewBlkDlg::BlockIconToBMP(const AcDbObjectId& blkId, HDC hdc)
    {
        Acad::ErrorStatus es;
        //块表记录指针
        AcDbBlockTableRecord *pBlkTblRcd=NULL;
        //保存预览图标的数组
        AcArray<Adesk::UInt8> icon;
     
        //获得保存块表记录的预览图标的数组
        try{
            es=acdbOpenObject(pBlkTblRcd,blkId,AcDb::kForRead);
     
            if(es!=Acad::eOk)
                throw 1;
     
            //如果块表定义不包含预览图标
            if(!pBlkTblRcd->hasPreviewIcon()){
                pBlkTblRcd->close();
                return NULL;
            }
     
            es=pBlkTblRcd->getPreviewIcon(icon);
            if(es!=Acad::eOk)
                throw 2;
     
            es=pBlkTblRcd->close();
            if(es!=Acad::eOk)
                throw 3;
        }catch(...){
            pBlkTblRcd->close();
            return NULL;
        }
     
        //由icon数组获得可显示的位图
        BITMAPINFOHEADER ih;    //位图信息头
        memcpy(&ih,icon.asArrayPtr(),sizeof(ih));
        size_t memsize=sizeof(BITMAPINFOHEADER)+((1<<ih.biBitCount)*sizeof(RGBQUAD));
        LPBITMAPINFO bi=(LPBITMAPINFO)malloc(memsize);    //位图信息
        memcpy(bi,icon.asArrayPtr(),memsize);
        HBITMAP hbm=CreateDIBitmap(hdc,&ih,CBM_INIT,icon.asArrayPtr()+memsize,bi,DIB_RGB_COLORS);
        free(bi);
     
        return hbm;
    }

    Step 6: 注册AutoCAD命令

    使用ObjectArx集成的工具栏注册一个新命令ViewBlk,在响应函数中完成模态对话框的调用,最终代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    // - CHAP3._ViewBlk command (do not rename)
    static void CHAP3_ViewBlk(void)
    {
        // 显示 “块定义查看器”对话框
        CAcModuleResourceOverride resOverride;    //防止资源冲突
        CPreviewBlkDlg dlg;
        dlg.DoModal();
    }

    由于使用了类CAcModuleResourceOverride,你需要添加头文件:

    #include "AcExtensionModule.h"

    到此,查看当前图形块定义属性的ObjectArx对话框应用程序便完成了,最终程序的运行效果如图:

    ObjectArx2010开发的块定义属性浏览应用程序

    展开全文
  • ObjectArx创建指定

    千次阅读 2017-03-27 21:09:25
     仿照AutoCad的Block命令,实现简版创建块功能! 二.  开发环境 Win7操作系统,AutoCad2012, VS2008, ObjectArx_SDK_2012 三. 相关函数简介 1) int acedSSGet (const ACHAR *str, const void *pt1,c

    ObjectArx创建自定义块

    一. 目的
          仿照AutoCad的Block命令,实现简版创建块功能!

    二.  开发环境
    Win7操作系统,AutoCad2012, VS2008, ObjectArx_SDK_2012

    三. 相关函数简介
    1int acedSSGet (const ACHAR *str, const void *pt1,const void *pt2, const struct resbuf *filter,ads_name ss);  

    选择集,个人理解:就是让你一次可以选择多个实体,与之相似的acedEntSel()一次只能选择一个实体

    2) int acedSSName (const ads_name ss, AdInt32 i, ads_name entres)

    既然选择了多个实体,我怎么得到其中的每一个实体呢?次函数即可让你得到选择实体中的任一个,参数二相当于索引,参数三就是还回的实体名

    3)Acad::ErrorStatus acdbGetObjectId(AcDbObjectId& objId,const ads_name objName);

    通过实体名获取实体ID,与之相同的另外几个函数(指针,句柄,ads_name之间的转换)
    acdbopenAcDbEntity,objectid,getAcDbObject,acdbGetObjectid,acdbGetAdsName等

    四.代码实现流程
    1) 首先我们得获取实体,保存这些实体的ID,实现代码:

    void MyBlock::GetPickEntity(AcDbObjectIdArray &idObjectArray)
    {
     ads_name adsName;
     AdInt32 ssLen;
     acedSSGet(NULL, NULL, NULL, NULL, adsName);
     acedSSLength(adsName, &ssLen);
     idObjectArray.removeAll();
     for (INT32 i = 0; i < ssLen; i++)
     {
      ads_name entityName;
      acedSSName(adsName, i, entityName);
      AcDbObjectId entityID;
      acdbGetObjectId(entityID, entityName);
      idObjectArray.append(entityID); 
     }
     acedSSFree(adsName);
    }

    2) 从当前块表中查看是否已存在此块名记录,没有则创建新的块表记录,将选择的实体Clone一份,插入到新的
    块表记录中,切记,关闭所有的数据库对象,实现代码(代码有点渣):
    Acad::ErrorStatus MyBlock::CreateBlock(const CString &strBlockName, const AcDbObjectIdArray &idObjectArray)
    {
     AcDbBlockTable *pBlockTable = NULL;
     Acad::ErrorStatus es = acdbHostApplicationServices()->workingDatabase()->getBlockTable(pBlockTable, AcDb::kForRead);
     if (Acad::eOk != es)
     {
      return es;
     }
     if (Adesk::kTrue == pBlockTable->has(strBlockName))
     {
      pBlockTable->close();
      return es;
     }
     AcDbBlockTableRecord *pBlockTableRcd = new AcDbBlockTableRecord();
     es = pBlockTableRcd->setName(strBlockName);
     es = pBlockTableRcd->setOrigin(AcGePoint3d::kOrigin);
     es = pBlockTable->upgradeOpen();
     if (Acad::eOk != es)
     {
      pBlockTableRcd->close();
      pBlockTable->close();
      return es;
     }
     es = pBlockTable->add(pBlockTableRcd);
     if (Acad::eOk != es)
     { 
      pBlockTable->close();
      pBlockTableRcd->close();
      return es;
     }
     pBlockTable->close();
     AcGeMatrix3d mat;
     mat.setToIdentity();
     for (INT32 i = 0; i < idObjectArray.length(); i++)
     {
      AcDbEntity *pEntity = NULL;
      es = acdbOpenAcDbEntity(pEntity, idObjectArray.at(i), AcDb::kForRead);
      if (Acad::eOk != es)
      {
       acDocManager->unlockDocument(curDoc());
       pBlockTableRcd->close();
       return es;
      }

      AcDbEntity *pNewEntity = AcDbEntity::cast(pEntity->clone());
      es = pBlockTableRcd->appendAcDbEntity(pNewEntity);
      if (Acad::eOk != es)
      {
       acDocManager->unlockDocument(curDoc());
       pBlockTableRcd->close();
       pEntity->close();
       pNewEntity->close();
       return es;
      }
      pEntity->close();
      pNewEntity->close();
     }
     pBlockTableRcd->close();
     return es;
    }
    三).创建非模式对话框,继承自CAcUiDialog类!
    具体添加一些控件与消息响应以及还未完成的非模式对话框销毁(DestoryWindow)就不一一叙述了!

    五. 成果截图
    1)执行自定义命令,点击拾取,我这里选择3条边与一个圆,总共4个对象,joke为新块名,然后点击创建

    2)在命令提示符输入指令insert,即可预览刚才创建的新块

    3)点击确定,即可在Cad工作区添加此块,然后可点击颜色,如下:

    4)这里我先则蓝色,即可改变刚才插入新块的颜色:

    六. 源码
    源码地址:http://download.csdn.net/detail/u012158162/9795717

    七 后序:
    A. 由于个人技术原因以及刚接触Cad与ObjectArx几天,其中难免有些胡说八道,请见谅!
    B. 程序中修改块的颜色存在Bug,以及没对MFC的对话框销毁做处理!
    C. 祝大家学习是艰辛的,生活是快乐的!

    展开全文
  • 怎样创建并使用CAD图

    千次阅读 2019-01-30 16:47:00
    CAD制图的时候,我们会重复的绘制一些常用的CAD图形,这个真的很烦。所以小伙伴们在日常学习CAD过程中,用到了CAD图块。这样会更方便很多,该...3.图形绘制完成后选择所绘制的图形,点击工具栏里“创建块”,打开...
  • pycharm创建代码

    千次阅读 2018-05-26 15:45:41
    代码开发过程除了程序内部已经定义好的代码之外,往往我们还需要自己定义一些程序内部没有定义好的,下面就来给大家展示一下如何自定义创建一个代码1.首先在pycharm中打开File,选择Settings 2.点击Editor下的Live...
  • linux使用设备创建swap

    万次阅读 2020-06-18 14:00:16
    执行以下命令,将新建的分区创建为swap。 mkswap /dev/vdb1 执行以下命令,激活swap分区。 swapon /dev/vdb1 执行以下命令,查询已启动的swap。 swapon -s 执行以下命令,查询swap分区UUID。 blkid |grep swap |awk...
  • 创建块存储镜像 客户端映射镜像 删除镜像 步骤 实现此案例需要按照如下步骤进行。 步骤一:创建镜像 1)查看存储池,默认存储池名称为rbd。 [root@node1 ~]# ceph osd lspools 0 rbd, #查看结果显示,共享池的名称为...
  • 默认情况下创建的表空间使用的数据大小是8K,数据库默认的数据大小由db_block_size参数决定。如果我们想要创建的表空间超出这个范围,首先需要调整数据库的参数db_nk_cache_size。以创建数据库大小为16K的表...
  • 创建lvm:(两磁盘创建逻辑卷并挂载) 1.物理磁盘 /dev/sdb1、/dev/sdc1 2.创建物理分区 pv pvcreate /dev/sdb1 /dev/sdc1 3.创建卷组 vg vgcreate vgdisk /dev/sdb1 /dev/sdc1 #vgdisk为卷组名称 自定义 4.创建...
  • 三菱PLC FB创建与使用

    千次阅读 多人点赞 2020-06-17 17:21:42
    三菱PLC FB创建与使用 在PLC编写程序过程中经常遇到一些 重复逻辑控制 的梯形图,比如流水线控制,气缸报警等等,这时候可以使用FB来便捷编程,减少工作量与出错率。 本例创建一个简单的单控气缸异常报警...
  • 怎么创建一个ramdisk设备

    千次阅读 2017-07-13 17:02:24
    2、通过命令modprobe brd rd_nr=4 rd_size=20480 max_part=0可以在/dev目录下创建4个 20M的设备 3、可以对启动的任意一个设备进行分区 4、查看分区 5、分好区就可以对这个分区进行mkfs mkfs.ext4 /dev/...
  • Eclipse下创建try catch代码的快捷键

    千次阅读 2018-09-22 13:02:42
    Eclipse下创建try catch代码的快捷键: 选中要try的代码然后 ctrl + shift + z  
  • Java中在try catch创建对象的问题

    千次阅读 2019-05-21 19:16:09
    在Java中创建对象在try catch中和在try catch中是不一样的 1.在try catch外面创建对象 如果在try catch外面创建的对象,即使是在try catch中实例化的,在try catch外面也是可以使用的 2.在try catch内部...
  • 2.然后选中整个图块,单击“创建块”的按钮,软件会自动弹出创建块的界面,你需要填写所创建的图块的名称,单击“OK”即可完成创建。   3.然后单击“粘贴块”的按钮,就会弹出一个新的对话框,你需要选择你想...
  • 首先应写出想要创建的模板代码,以下是一个文件读写的代码: http://download.csdn.net/download/skl_tz/5337293 然后进入Windows -> Preferences -> Java -> Editor -> Templates -> New ; Name ...
  • Oracle 创建不同数据的表空间

    千次阅读 2010-01-15 21:20:00
    Oracle数据库的数据DB_BLOCK_SIZE大小确定数据库的最小数据的大小,在创建表空间时可以如果不想使用默认的数据大小,可以通过设置自己的数据大小。 具体实例如下: create tablespace test_16k blocksize...
  • Android Studio 中代码自动补齐以及自定义代码
  • 因为对象就是用来封装数据的,对象调用的方法不用到封装的数据,对象的创建便没有意义; 静态方法使用原则: 一、静态方法不能访问非静态成员或者方法 非静态方法可以访问静态成员或静态方法 二、静态方法...
  • 执行顺序:(优先级从高到低)静态代码>mian方法>构造代码>构造方法。 其中静态代码只执行一次。构造代码在每次创建对象是都会执行。
  • Vscode创建.vue文件快捷生成代码

    千次阅读 2020-03-09 14:24:40
    打开VsCode,找到扩展插件搜索框,搜索Vetur 和 VueHelper 两个插件,点击下载 使用快捷键Ctrl+Shift+P或者File-Preferences-User Snippets搜索vue.json 将vue.json替换为以下内容 { ... "...
  • 为了方便学习文件系统的原理和原始数据的分析,需要创建虚拟设备并格式化为ext4文件系统; Linux创建虚拟设备 losetup命令 -a 显示所有已经使用的回环设备状态 -d 卸除回环设备 -f 寻找第一个未使用的...
  • System.out.println("静态代码"); } { System.out.println("非静态代码"); } public static void main(String[] args) { new Father(); } } 结果: 非静态代码 构造方法 静态代码 非静态代码...
  • 一. 前言 由于本人会负责一些公司运维工作,在...结合阿里云工程师给的方法和自己找的一些方法,“另辟蹊径”成功合并了多磁盘到一个分区!以下是以大于2T的磁盘为例。 二. 使用parted方式格式化磁盘并且创建LVM...
  • 每个图从一格AcDbBlockBegin对象开始,紧接着是一个或多个AcDbEntity对象,最后以AcDbBolckEnd对象结束,按其所属关系得层次结构分为三层:第一层为表,是属于数据库管理的根对象;第二层为表记录,是属于表...
  •  熟悉和理解创建信息提供者、信息范围、信息对象目录、信息对象、信息(信息立方体)的步骤。 平面数据抽取       一、创建信息立方体(信息) (1)创建信息范围; (2)创建信息对象目录...
  • win10系统怎么创建虚拟网卡?

    万次阅读 2018-09-13 20:35:13
    由于工作原因,下午需要重新在物理机上创建块虚拟网卡。因为不熟悉这一块,就百度了下,但是到选择“厂商”和“型号”这一步,却找不到“Microsoft”对应的型号,经过一番查询才知道win10里“厂商”对应的“型号”...
  • win10系统创建虚拟网卡

    千次阅读 2019-11-12 15:47:55
    由于工作原因,下午需要重新在物理机上创建块虚拟网卡。因为不熟悉这一块,就百度了下,但是到选择“厂商”和“型号”这一步,却找不到“Microsoft”对应的型号,经过一番查询才知道win10里“厂商”对应的“型号”...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,326,691
精华内容 530,676
关键字:

创建块