热迁移中VirtIO-PCI设备的配置空间处理

news2024/11/16 7:55:20

文章目录

  • 问题现象
  • 定位过程
    • 日志分析
      • 源端
      • 目的端
    • 原理分析
      • 基本原理
      • 上下文分析
      • 复现分析
      • patch分析
  • 总结
  • 解决方案

问题现象

  • 集群升级虚拟化组件版本,升级前存量运行并挂载了virtio磁盘的虚拟机集群内热迁移到升级后的节点失败,QEMU报错如下:
2023-09-15T04:52:39.221053Z qemu-kvm: get_pci_config_device: Bad config data: i=0x10 read: c1 device: 1 cmask: ff wmask: 80 w1cmask:0
2023-09-15T04:52:39.221140Z qemu-kvm: Failed to load PCIDevice:config
2023-09-15T04:52:39.221148Z qemu-kvm: Failed to load virtio-blk:virtio
2023-09-15T04:52:39.221154Z qemu-kvm: error while loading state for instance 0x0 of device '0000:00:0b.0/virtio-blk'

定位过程

  • 通过“Failed to load”关键字可以确认迁移目的端报错且该日志为目的端虚机启动是Qemu进程日志,通过日志关键字“Bad config data”,搜索集群其它节点是否有相同报错的虚机,搜索到另一个虚机迁移失败有相同报错,问题有机率复现。

日志分析

源端

  • 选取其中一个问题虚机,查看源端虚机启动和热迁移发起的时间:
/* 虚机启动时间 */
2023-09-15 02:14:55.253+0000: starting up libvirt version:xxx
...
/* 虚机启动时pci号最大的一块virtio磁盘 */
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x9,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1,write-cache=on \
...
/* 虚机启动时pci号最大的设备 */
-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0xa \
/* 虚机第一次迁移时间 */
2023-09-15 04:52:30.383+0000: initiating migration
/* 虚机第二次迁移时间 */
2023-09-15 05:46:23.278+0000: initiating migration
  • 从界面事件日志看,虚机在10:14被克隆后启动运行(Qemu日志比前端界面时间早8小时),启动时有一个磁盘,ID为virtio-disk0,pci的bdf号为0:9.0,启动后10:15界面显示立即热添加了一块磁盘。QEMU日志无记录。虚拟机分别在12:52和13:46发起热迁移,都失败,对应QEMU日志:
2023-09-15 04:52:30.383+0000: initiating migration
2023-09-15 05:46:23.278+0000: initiating migration

目的端

  • 目的端虚机第一次启动时间
2023-09-15 04:52:30.070+0000: starting up libvirt version:xxx
...
/* 源端启动时挂载的virtio磁盘*/
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x9,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1,write-cache=on \
...
/* 源端启动后热添加的virtio磁盘 */
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0xb,drive=drive-virtio-disk1,id=virtio-disk1,bootindex=4,write-cache=on \
...
/* 源端启动后热添加的virtio网卡 */
-device virtio-net-pci,mq=on,vectors=10,rx_queue_size=1024,netdev=hostnet1,id=net1,mac=52:54:00:6a:ef:94,bus=pci.0,addr=0xc \
/* 热迁移目的端启动的虚机,命令行中会增加-incoming defer参数 */
/* 表示虚机内存的读取通过启动后的migrate_incoming qmp 命令指定 */
-incoming defer \
-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0xa \
/* 第一次迁移报错 */
2023-09-15T04:52:39.221053Z qemu-kvm: get_pci_config_device: Bad config data: i=0x10 read: c1 device: 1 cmask: ff wmask: 80 w1cmask:0
2023-09-15T04:52:39.221140Z qemu-kvm: Failed to load PCIDevice:config
2023-09-15T04:52:39.221148Z qemu-kvm: Failed to load virtio-blk:virtio
2023-09-15T04:52:39.221154Z qemu-kvm: error while loading state for instance 0x0 of device '0000:00:0b.0/virtio-blk'
...
  • 第一次迁移失败时间点2023-09-15 04:52,目的端报错:
get_pci_config_device: Bad config data: i=0x10 read: c1 device: 1 cmask: ff wmask: 80 w1cmask:0
  • 第二次迁移失败时间点2023-09-15 05:46,目的端报相同错误。另外,目的端QEMU启动命令行比源端多出两个-device,分别是热添加的virtio-disk1磁盘和net1网卡,bdf号分别是00:b.0,00:c.0。

原理分析

  • 通过上面的日志,我们仅知道迁移报错了,报错的关键函数是get_pci_config_device,这个函数在迁移中起什么作用呢?为什么会有pci配置空间的报错?这个报错涉及哪些基本原理呢?下面我们简单分析关于virtio-pci设备的基本原理。

基本原理

  • pci配置空间布局
    pci规范定义pci配置空间长度为256byte,其中通用头部为64byte,也称为预定义空间,通用头部的前16byte格式如下,之后就是bar空间及其它内容,偏移0x5字节定义了status字段,其中有1bit定义为Capabilities List,它是pci规范定义的附加空间标志位,Capabilities List的意义是允许在pci设备配置空间之后加上额外的寄存器,这些寄存器由Capability List组织起来,用来实现特定的功能(virtio-pci基于该特性实现各类设备),附加空间在64字节配置空间之后,该bit为1表示在capabilities pointer偏移处(0x34)存放了附加寄存器组的起始偏移。
    在这里插入图片描述
  • virtio-pci配置空间布局
    virtio-pci通过capabilities list存放规范中定义的数据结构,结构如下:
    在这里插入图片描述
  • list由若干元素连接而成,每个元素的头三个字节有通用的格式:第1 byte为capability ID,表示实现了何种capability,对于virtio-blk,其ID为0x90; 第2 byte为list中下一个元素的偏移,如果list结束,第2 byte为0;第3 byte为元素的长度。以virtio-blk为例,每个元素格式如下:
/* This is the PCI capability header: */
struct virtio_pci_cap {
    __u8 cap_vndr;      /* Generic PCI field: PCI_CAP_ID_VNDR */
    __u8 cap_next;      /* Generic PCI field: next ptr. */
    __u8 cap_len;       /* Generic PCI field: capability length */
    __u8 cfg_type;      /* Identifies the structure. */
    __u8 bar;       /* Where to find it. */
    __u8 id;        /* Multiple capabilities of the same type */
    __u8 padding[2];    /* Pad to full dword. */
    __le32 offset;      /* Offset within bar. */
    __le32 length;      /* Length of the structure, in bytes. */
};
  • 可以看到virtio-pci规范也遵循了pci的规范,从第四个字节开始,为virtio-blk特有内容,其中cfg_type用于标识virtio-pci定义的数据结构类型,定义如下:
/* Common configuration */
#define VIRTIO_PCI_CAP_COMMON_CFG        1
/* Notifications */
#define VIRTIO_PCI_CAP_NOTIFY_CFG        2
/* ISR Status */
#define VIRTIO_PCI_CAP_ISR_CFG           3
/* Device specific configuration */
#define VIRTIO_PCI_CAP_DEVICE_CFG        4
/* PCI configuration access */
#define VIRTIO_PCI_CAP_PCI_CFG           5
/* Shared memory region */
#define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8
/* Vendor-specific data */
#define VIRTIO_PCI_CAP_VENDOR_CFG        9
  • 整个virtio-blk配置空间内容如下:
    在这里插入图片描述
  • 上面是virtio pci设备的简单介绍,完整的分析可以参考:VirtIO实现原理——PCI基础

上下文分析

  • 了解基础的virtio pci设备配置空间后,继续分析迁移报错的出错上下文:
static int get_pci_config_device(QEMUFile *f, void *pv, size_t size,
                                 VMStateField *field)
{
    PCIDevice *s = container_of(pv, PCIDevice, config);
    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(s);
    uint8_t *config;    /* 1byte的指针 */
    int i;

    assert(size == pci_config_size(s));
    config = g_malloc(size);    		/* 分配0x100=256字节内存用来存放pci的配置空间信息 */

    qemu_get_buffer(f, config, size);   /* 从迁移流中读取256字节的pci配置空间内容*/
    for (i = 0; i < size; ++i) {        /* 按字节移动逐一比较配置空间内容 */
        if ((config[i] ^ s->config[i]) &        /* 如果配置空间不相同,报错 */
            s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) {
            error_report("%s: Bad config data: i=0x%x read: %x device: %x "
                         "cmask: %x wmask: %x w1cmask:%x", __func__,
                         i, config[i], s->config[i],
                         s->cmask[i], s->wmask[i], s->w1cmask[i]);
            g_free(config);
            return -EINVAL;
        }
    }
    memcpy(s->config, config, size);
    ......
}
  • get_pci_config_device实现目的端加载pci设备配置空间,从迁移的流中读取源端传来的pci设备配置空间内容。再比较本地QEMU实例化pci设备得到的pci空间内容,如果两个值不同,说明源端pci设备的配置空间内容和目标端初始化的pci设备配置空间内容不同,通常该问题是由于迁移两端的同一个virtio-pci设备有不同的配置导致,比如virtio队列数配置不同会报类似错误:
get_pci_config_device: Bad config data: i=0x9a read: 1 device: 2 cmask: ff wmask: 0 w1cmask:0
  • 报错原因是从源端读取到的virtio队列数为1,但目标端初始化队列数为2。其中i=0x90表示读取内容在pci配置空间(总长256byte)的偏移。分析本次报错:
get_pci_config_device: Bad config data: i=0x10 read: c1 device: 1 cmask: ff wmask: 80 w1cmask:0
  • 目的端在配置空间偏移0x10的地方读取了一字节的数据,源端内容为0xc1,目的端内容为0x1,高4bit的内容不同,源端为0xc,目的端为0x0。pci配置空间0x10及BAR0的内容(参考基本原理一节),因此进一步确定是迁移两端的同一个virtio-pci设备BAR0内容不同导致的报错。
  • BAR0中存放的是什么地址呢?
    BAR0映射了1个virtio-pci规范定义的IO空间,该IO空间用于实现对virtio设备配置空间访问的一个可选方法,IO空间格式如下:
struct virtio_pci_cfg_cap {
        struct virtio_pci_cap cap;
        u8 pci_cfg_data[4]; /* Data for BAR access. */
};
  • 当Guest驱动想访问某个virtio-blk配置空间的某个区域(common configuration, notification, ISR and device-specific configuration)时,首先获取要访问的bar号(cap.bar),长度(cap.length)和偏移(cap.offset),将其设置到cap中(其它字段: cap.cap_vndr:0x9, cap.cap_next:70,cap._cap_len:14,cap.cfg_type: 05,与capabilies list中的VIRTIO_PCI_CAP_PCI_CFG元素相同),实际动作是往BAR0中记录的IO地址写上述内容,QEMU会将对应的信息放到pci_cfg_data中供Guest驱动读取。通过这样的方式,Guest驱动可以实现对virtio-blk配置空间数据的访问。
  • 从上可知,迁移的virtio设备,由于源端上BAR0地址的bit[4,7]内容为0xc,目的端BAR0地址的bit[4,7]内容为0,分析目的端日志:
    2023-09-15T04:52:39.221154Z qemu-kvm: error while loading state for instance 0x0 of device ‘0000:00:0b.0/virtio-blk’
    迁移的设备是1个磁盘设备,其bdf号为00:0b.0,对比源端的virtio-pci设备:
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x9,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1,write-cache=on
-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0xa
  • 第1个磁盘的pci号为00:09.0,随后是virtio-balloon-pci设备,分配到00:0a.0,pci bdf为00:0b.0的设备在QEMU源端日志的启动命令行中并没有出现,因此只可能是热插拔的设备分配到了该bdf号,从界面看,虚机启动后有热添加设备事件,首先是热添加磁盘,之后是热添加网卡。假定pci号按顺序被分配(通常如此),则热添加的virtio磁盘分配到的bdf号为00:0b.0,热添加的virtio网卡分配到的bdf号为00:0c.0。进一步描述迁移失败过程是在旧版本环境中通过克隆创建的虚机,磁盘热添加后,热迁移到升级后版本的节点报错。按照该方法,可以稳定复现该问题。

复现分析

  • 查看复现的虚机PCI设备空间布局
virsh qemu-monitor-command  {vm_uuid} --hmp info pci
...
  Bus  0, device   9, function 0:
    SCSI controller: PCI device 1af4:1001
      IRQ 0.
      BAR0: I/O at 0xd080 [0xd0bf].
      BAR1: 32 bit memory at 0xfea59000 [0xfea59fff].
      BAR4: 64 bit prefetchable memory at 0xfe208000 [0xfe20bfff].
      id "virtio-disk0"
  Bus  0, device  10, function 0:
    Class 0255: PCI device 1af4:1002
      IRQ 10.
      BAR0: I/O at 0xd100 [0xd11f].
      BAR4: 64 bit prefetchable memory at 0xfe20c000 [0xfe20ffff].
      id "balloon0"
  Bus  0, device  11, function 0:
    SCSI controller: PCI device 1af4:1001
      IRQ 0.
      BAR0: I/O at 0xffc0 [0xffff].
      BAR1: 32 bit memory at 0xfebff000 [0xfebfffff].
      BAR4: 64 bit prefetchable memory at 0x4287fffc000 [0x4287fffffff].
      id "virtio-disk1"
...
  • 可以看到第一个磁盘的BAR0空间bit[4,7]内容为0xc,与QEMU日志报错信息匹配:
get_pci_config_device: Bad config data: i=0x10 read: c1 device: 1 cmask: ff wmask: 80 w1cmask:0
  • 源端选取一个相同硬件配置的虚机,查看第二个virtio磁盘的pci信息BAR0地址相同:
  Bus  0, device  11, function 0:
    SCSI controller: PCI device 1af4:1001
      IRQ 0.
      BAR0: I/O at 0xffc0 [0xffff].
      BAR1: 32 bit memory at 0xfebff000 [0xfebfffff].
      BAR4: 64 bit prefetchable memory at 0x4287fffc000 [0x4287fffffff].
      id "virtio-disk1"
  • 冷重启该虚机,为保证QEMU分配的pci号不变,以下面的步骤冷重启该虚机:
virsh dumpxml {vm_uuid} >> {vm_uuid}.xml
virsh destroy {vm_uuid}
virsh undefine {vm_uuid}
virsh define {vm_uuid}.xml
virsh start {vm_uuid}
  • 查看冷重启后虚机的第2块盘的pci信息如下:
  Bus  0, device  11, function 0:
    SCSI controller: PCI device 1af4:1001
      IRQ 0.
      BAR0: I/O at 0xd080 [0xd0ff].
      BAR1: 32 bit memory at 0xfea9a000 [0xfea9afff].
      BAR4: 64 bit prefetchable memory at 0xfe210000 [0xfe213fff].
      id "virtio-disk1"
  • 两个版本设备的IO空间的确发生了变化。新版本BAR0的IO空间边长。
  • 对比存量运行虚机和冷重启后虚机设备的IO空间,冷重启后的虚机磁盘的BAR0 IO空间从3f扩大为7f。

patch分析

  • 分析新版本引入的特性中,与virtio-blk设备相关的patch只有TRIM/UNMAP特性,社区patch如下:
37b06f8d46 virtio-blk: add DISCARD and WRITE_ZEROES features
20764be042 virtio-blk: set config size depending on the features enabled
ba550851f5 virtio-net: make VirtIOFeature usable for other virtio devices
5c81161f80 virtio-blk: add "discard" and "write-zeroes" properties
bbe8bd4d85 virtio-blk: add host_features field in VirtIOBlock
00f639fb8f virtio-blk: add acct_failed param to virtio_blk_handle_rw_error
  • 该commit是vhost-user-blk实现TRIM/UNMAP的核心修改,为什么这个commit会导致virito-blk的BAR0 IO空间变长?因为这个commit需要定义virtio spec要求的discard/write_zeroes相关参数,因此扩展了virtio-blk的配置空间,配置空间原来提供的内容以下数据结构:
struct virtio_blk_config {
    /* The capacity (in 512-byte sectors). */
    uint64_t capacity;
    /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */
    uint32_t size_max;
    /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */
    uint32_t seg_max;
    /* geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */
    struct virtio_blk_geometry {
        uint16_t cylinders;
        uint8_t heads;
        uint8_t sectors;
    } geometry;

    /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
    uint32_t blk_size;

    /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY  */
    /* exponent for physical block per logical block. */
    uint8_t physical_block_exp;
    /* alignment offset in logical blocks. */
    uint8_t alignment_offset;
    /* minimum I/O size without performance penalty in logical blocks. */
    uint16_t min_io_size;
    /* optimal sustained I/O size in logical blocks. */
    uint32_t opt_io_size;

    /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */
    uint8_t wce;
    uint8_t unused;

    /* number of vqs, only available when VIRTIO_BLK_F_MQ is set */
    uint16_t num_queues;
}
  • 该数据结构总长度为36字节,再加上struct virtio_pci_cap的长度16字节,总计52字节。IO空间为64字节,可以满足IO访问要求。当引入discard/write_zeroes之后,数据结构struct virtio_blk_config新增了以下字段:
+
+       /* the next 3 entries are guarded by VIRTIO_BLK_F_DISCARD */
+       /*
+        * The maximum discard sectors (in 512-byte sectors) for
+        * one segment.
+        */
+       uint32_t max_discard_sectors;
+       /*
+        * The maximum number of discard segments in a
+        * discard command.
+        */
+       uint32_t max_discard_seg;
+       /* Discard commands must be aligned to this number of sectors. */
+       uint32_t discard_sector_alignment;
+
+       /* the next 3 entries are guarded by VIRTIO_BLK_F_WRITE_ZEROES */
+       /*
+        * The maximum number of write zeroes sectors (in 512-byte sectors) in
+        * one segment.
+        */
+       uint32_t max_write_zeroes_sectors;
+       /*
+        * The maximum number of segments in a write zeroes
+        * command.
+        */
+       uint32_t max_write_zeroes_seg;
+       /*
+        * Set if a VIRTIO_BLK_T_WRITE_ZEROES request may result in the
+        * deallocation of one or more of the sectors.
+        */
+       uint8_t write_zeroes_may_unmap;
+
+       uint8_t unused1[3];
  • 总计增加了24字节。因此IO空间至少需要52+24 = 76字节才能满足访问要求。QEMU按照64字节对齐,如果新版本按照开源的策略默认打开discard特性,在初始化virtio-blk设备时会将IO空间扩展为128字节,配置空间会变长:
virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config));

总结

  • TRIM/UNMAP特性的引入使高版本QEMU在模拟virtio-blk设备时,PCI配置空间变长。会导致迁移是目的端加载设备状态报错。
  • QEMU virtio设备在支持新特性时,目的端除了检查两端源端的feature是否是目的端的子集,还会检查virtio-pci配置空间内容:
  1. 如果新特性仅仅引入feature bit,前端如果不支持,在前后端协商时,该feature bit不会被置位。因此热迁移时,就算目的端默认开启新特性,也不会在热迁移过程中进行协商,最终目的端feature也不会包含该特性。只有冷重启后才能生效。这种情况不存在热迁移兼容性问题。
  2. 如果新特性不仅引入feature bit,还对virtio-pci规范中定义的配置空间数据结构进行了扩展,从而引起配置空间变化,比如这里的TRIM/UNMAP特性,则会存在热迁移兼容性问题。

解决方案

  • QEMU virtio-pci设备在初始化pci空间时,应该根据feature是否使能来动态计算PCI空间的长度并初始化,只有使能该特性的feature,才能将其涉及的数据结构计算到PCI空间的长度中,否则不应该在配置空间提供该feature相关数据结构。社区在下面的commit实现了该逻辑:
20764be042 virtio-blk: set config size depending on the features enabled
  • 控制面如Libvirt,在涉及到平滑升级的处理时,如果目的端有新增的feature且涉及virtio设备配置空间改变时,应该在热迁移时显式关闭该特性,从而保证热迁移源端和目的端配置空间相同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1067170.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

KdMapper扩展实现之Dell(pcdsrvc_x64.pkms)

1.背景 KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动&#xff0c;本文是利用其它漏洞&#xff08;参考《【转载】利用签名驱动漏洞加载未签名驱动》&#xff09;做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。 2.驱动信息 驱动名称pcds…

数据中台实战(11)-数据中台的数据安全解决方案

0 微盟删库跑路 除了快、准和省&#xff0c;数据中台须安全&#xff0c;避免“微盟删库跑路”。 2020年2月23日19点&#xff0c;国内最大精准营销服务商微盟出现大面积系统故障&#xff0c;旗下300万商户线上业务全停&#xff0c;商铺后台所有数据被清。始作俑者是一位运维&a…

Java常见设计模式

单例模式&#xff1a;程序自始至终只创建一个对象。 应用场景&#xff1a;1.整个程序运行中只允许一个类的实例时 2.需要频繁实例化然后销毁的对象 3.创建对象时耗时过多但又经常用到的对象 4.方便资源相互通信的环境 懒汉式线程不安全问题解决方案&#xff1a; 双重检查加锁机…

HTTPS 加密工作过程

引言 HTTP 协议内容都是按照文本的方式明文传输的&#xff0c;这就导致在传输过程中出现一些被篡改的情况。例如臭名昭著的运营商劫持。显然&#xff0c; 明文传输是比较危险的事情&#xff0c;为此引入 HTTPS &#xff0c;HTTPS 就是在 HTTP 的基础上进行了加密, 进一步的来保…

计算机毕业设计 基于Java的同城宠物帮(宠物领养平台\系统)的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

SpringSecurity源码学习一:过滤器执行原理

目录 1. web过滤器Filter1.1 filter核心类1.2 GenericFilterBean1.3 DelegatingFilterProxy1.3.1 原理1.3.2 DelegatingFilterProxy源码 2. FilterChainProxy源码学习2.1 源码2.1.1 doFilterInternal方法源码2.1.1.1 getFilters()方法源码2.1.1.2 VirtualFilterChain方法源码 3…

C++学习day1

一>整理思维导图 二>提示并输入一个字符串 &#xff0c;统计活该字符串中的大写&#xff0c;小写&#xff0c;数字&#xff0c;空格&#xff0c;以及其他字符的个数要求使用c风格符完成 #include <iostream>int main() {std::string input;std::cout << &qu…

网络和系统操作命令

目录 ping&#xff1a;用于检测网络是否通畅&#xff0c;以及网络时延情况。ipconfig&#xff1a;查看计算机的IP参数配置信息&#xff0c;如IP地址、默认网关、子网掩码等信息。netstat&#xff1a;显示协议统计信息和当前TCP/IP网络连接。tasklist&#xff1a;显示当前运行的…

Go复合类型之数组类型

Go复合类型之数组 文章目录 Go复合类型之数组一、数组(Array)介绍1.1 基本介绍1.2 数组的特点 二、数组的声明与初始化2.1 数组声明2.2 常见的数据类型声明方法2.3 数组的初始化方式一&#xff1a;使用初始值列表初始化数组方法二&#xff1a;根据初始值个数自动推断数组长度方…

温故知新:dfs模板-843. n-皇后问题

n−n−皇后问题是指将 nn 个皇后放在 nnnn 的国际象棋棋盘上&#xff0c;使得皇后不能相互攻击到&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上。 现在给定整数 nn&#xff0c;请你输出所有的满足条件的棋子摆法。 输入格式 共一行&#xff0c;包含整数 n…

通过位运算,实现单字段标识多个状态位

可能经常有如下这种需求: 需要一张表,来记录学员课程的通过与否. 课程数量不确定,往往很多,且会有变动,随时可能新增一门课. 这种情况下,在设计表结构时,一门课对应一个字段,就有些不合适, 因为不知道课程的具体数量,也无法应对后期课程的增加. 考虑只用一个状态标志位,利用位运…

精确到区县级街道乡镇行政边界geojson格式矢量数据的获取拼接实现Echarts数据可视化大屏地理坐标信息地图的解决方案

在Echarts制作地理信息坐标地图时&#xff0c;最麻烦的就是街道乡镇级别的行政geojson的获取&#xff0c; 文件大小 788M 文件格式 .json格式&#xff0c;由于是大文件数据&#xff0c;无法直接使用记事本或者IDE编辑器打开&#xff0c;推荐Dadroit Viewer&#xff08;国外…

【代码实践】HAT代码Window平台下运行实践记录

HAT是CVPR2023上的自然图像超分辨率重建论文《activating More Pixels in Image Super-Resolution Transformer》所提出的模型。本文旨在记录在Window系统下运行该官方代码&#xff08;https://github.com/XPixelGroup/HAT&#xff09;的过程&#xff0c;中间会遇到一些问题&am…

linux系统中常见注册函数的使用方法

大家好&#xff0c;今天给大家分享一下&#xff0c;linux系统中常见的注册函数register_chrdev_region()、register_chrdev()、 alloc_chrdev_region()的使用方法​。 一、函数包含的头文件&#xff1a; 分配设备编号&#xff0c;注册设备与注销设备的函数均在fs.h中申明&…

OMV6 安装Extras 插件失败的解决方法

# Time: 2023/10/07 #Author: Xiaohong # 运行环境: OS: OMV6 # 功能: 安装Extras 插件失败的解决方法 问题描述&#xff1a;OMV6 安装插件omv-extras&#xff0c;只能按如下提示的命令行&#xff0c;但安装过程中&#xff0c;会提示raw.githubusercontent.com 无法访问插…

校招秋招,性格和职业有关系吗?

企业在招聘应届毕业生时不再局限于普通的面试或者笔试&#xff0c;在互联网时代&#xff0c;为了能够更好的匹配需要的优质人才&#xff0c;企业会通过各种测试来提高招聘的准确率以及成功率。也许以前很多人都听说过性格和职业是有一定关系的&#xff0c;但是如何确定自己的性…

win安装ESPHome教程

感谢 Little-Bear-Bear 博主的教程,ESPHome入门 windows环境安装 -01 1.安装python 从Python官方网站下载。 确保选中“将Python添加到PATH”&#xff0c;然后一直通过 安装。 安装后重新启动计算机 winR 。按回车键。cmd python --version输入 查看 是否安装成功 python…

基于虚拟同步发电机控制的双机并联Simulink仿真模型

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

基于LADRC自抗扰控制的VSG三相逆变器预同步并网控制策略(Simulink仿真实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

8.2 JUC - 4.Semaphore

目录 一、是什么&#xff1f;二、简单使用三、semaphore应用四、Semaphore原理 一、是什么&#xff1f; Semaphore&#xff1a;信号量&#xff0c;用来限制能同时访问共享资源的线程上限 二、简单使用 public class TestSemaphore {public static void main(String[] args) …