multipath 内核接口及框架介绍

文章目录

      • 1 云主机使用网络存储 io 流程
      • 2 multipath 介绍

1 云主机使用网络存储 io 流程

对于一个云服务环境,大致会有网络节点,存储节点,计算节点,控制节点,其中虚拟云主机在计算节点工作,而虚拟云主机(qemu 虚机)使用的存储一般通过 ceph,drbd,mulitpch,iscsi 使其在存储节点存储。

(1)计算节点云主机访问本地 /dev/vda,/dev/vdb … 块设备,实际访问的是 dm-mulitpath 管理的计算节点的 /dev/dm-0…1,2,3 设备。

/dev/vda-> virtio_blk-> virtio_pci-> 虚拟主机(qemu vhost)->  本地节点 tcm_vhost-> target_core_mod-> target_core_iblock/target_core_pscsi-> 计算节点 /dev/dm-0

(2)/dev/dm-0 实际由 iscsi 客户端管理的 /dev/sda 和 /dev/sdb 聚合而成,可实现故障路径切换,io 负载均衡等能力,实际 io 数据会通过 iscsi_tcp(如果是 fc 服务,则是走光纤通道)到达存储节点的 /dev/drbd0。如果主路径故障,通路不同则会由 multipath 切换到备用路径(这里是/dev/sdb),等待恢复后切换回主路径(回到/dev/sda)。这里假设走的主路径:

/dev/dm-0-> /dev/sda-> iscsi_tcp/fc-> 到达存储节点 iscsi_target_mod/tcm_fc-> target_core_mod-> target_core_iblock/target_core_pscsi-> /dev/drbd0 (drbd 模块)

(3)存储节点的/dev/drbd0由 drbd 内核模块配置,实现块数据拷贝同步到后端存储设备。后端存储设备为/dev/alcache0,该设备实际是一块高速 SSD 设备(如 nvme),实现数据的高速缓存。
(4)/dev/alcache0由 bcache 配置管理,实际对应后端存储为/dev/rbd0。bcache 实现将 nvme 作为高速缓存将数据同步到 /dev/rbd0 中。
(5)/dev/rbd0 设备由 ceph rbd 管理,该模块提供了分布式存储管理能力,如 io 调度等,通过 rbd,数据 io 会实际到达 HHD 等大容量存储设备。

/dev/rbd0-> rbd 模块(ceph 网络协议转发数据)-> ceph rbd 管理(分布式存储管理实际物理磁盘)

2 multipath 介绍

可以看到上述路径中在计算节点使用了 mutlipath 聚合路径来实现负载均衡和路径切换等。

用户态工具相关:
启动脚本

/usr/lib/systemd/system/multipathd.service

udev 命令规则

/usr/lib/udev/rules.d/11-dm-mpath.rules
/usr/lib/udev/rules.d/62-multipath.rules

用户工具

/usr/sbin/multipathd        守护进程,监听系统中路径状态的变化,并做相应的处理。
/usr/sbin/mpathpersist      SCSI PR命令工具,主要用于隔离。
/usr/sbin/mpathconf         修改多路径配置
/usr/sbin/kpartx            DeviceMapper虚拟设备创建工具
/usr/sbin/multipath         多路径命令工具

查看 device mapper 映射表

[wings-client@fedora sections]$ sudo dmsetup table
mpatha: 0 41940992 multipath 1 queue_if_no_path 1 alua 1 1 service-time 0 2 2 8:0 1 1 8:16 1 1[wings-client@fedora sections]$ ll /dev/sd*
brw-rw----. 1 root disk 8,  0 101615:29 /dev/sda
brw-rw----. 1 root disk 8, 16 101615:29 /dev/sdb

查看被映射的多路径设备

[wings-client@fedora sections]$ ll /dev/dm-*
brw-rw----. 1 root disk 253, 0 101615:29 /dev/dm-0
[wings-client@fedora sections]$ 

多路径创建的 dm 设备的主设备号都是 253。
查看设备的 wwid

[wings-client@fedora sections]$ lsscsi -i
[0:0:0:0]    cd/dvd  QEMU     QEMU DVD-ROM     2.5+  /dev/sr0   -
[6:0:0:0]    disk    LIO-ORG  block1           4.0   /dev/sdb   3600140554012e51273044299221f2903
[7:0:0:0]    disk    LIO-ORG  block1           4.0   /dev/sda   3600140554012e51273044299221f2903
[wings-client@fedora sections]$

同一个设备的两条路径,以及被映射的新设备的 wwid 相同
查看当前多路径信息

[wings-client@fedora sections]$ sudo multipath -ll
mpatha (3600140554012e51273044299221f2903) dm-0 LIO-ORG,block1
size=20G features='1 queue_if_no_path' hwhandler='1 alua' wp=rw
`-+- policy='service-time 0' prio=50 status=active|- 7:0:0:0 sda 8:0  active ready running`- 6:0:0:0 sdb 8:16 active ready running
[wings-client@fedora sections]$ 

多路径软件读取/etc/multipath.conf文件,并根据 wwid 将相同设备聚合成一个虚拟设备。

内核中 dm-multipath 源码实现主要在linux/drivers/md/dm-*中。
dm-multipath 模块属于 md 模块的一个后端部分。
md 是多个设备的内核 RAID 实现,其中 dm 是设备映射器实现,允许将一个设备映射到另一个设备(一个或者多个)上,这可用作创建在/dev/mapper 目录中访问的虚拟设备(映射设备)。该设备的所有 IO 都将映射到其他设备。

RAID 控制器
RAID控制器是一种硬件设备或软件程序,用于管理计算机或存储阵列中的硬盘驱动器(HDD)/固态硬盘(SSD),以便它们能如逻辑部件一样工作,各施其职。RAID控制器的功能既可以由硬件也可以由软件来实现。硬件RAID一般用于处理大量数据的RAID模式。随着处理器的能力的不断增强,软件RAID功能已经成为可能,不过当处理大量数据时CPU仍然会显得力不从心。一般的中高档服务器多使用硬件RAID控制器,但是由于硬件RAID控制器的价格昂贵,导致系统成本大大增加。而随着处理器的性能快速发展,使得软件RAID的解决方法得到人们的重视。在这里,我们使用的是软件RAID。

在Linux系统中目前以MD (Multiple Devices)虚拟块设备的方式实现软件RAID,利用多个底层的块设备虚拟出一个新的虚拟块设备,并且利用条带化(stripping)技术将数据块均匀分布到多个磁盘上来提高虚拟设备的读写性能,利用不同的数据冗余算法来保护用户数据不会因为某个块设备的故障而完全丢失,而且还能在设备被替换后将丢失的数据恢复到新的设备上。

MD (Multiple Devices)框架
Multiple Devices虚拟块设备(利用底层多个块设备虚拟出一个新的虚拟块设备)。目前MD支持linear, multipath, raid0 (stripping), raid1 (mirror), raid4, raid5, raid6, raid10等不同的冗余级别和组成方式,当然也能支持多个RAID阵列的层叠组成raid1+0, raid5+1等类型的阵列。
dm 整体框架大致如下:
在这里插入图片描述
这里主要关心 multipath 部分。
内核选中 DM_MULTIPATH 即可启用 multipath 相关内核支持。
DM_MULTIPATH_QL 和 DM_MULTIPATH_ST 为两种可选的路径选择算法。

dm-mpath.c是多路径驱动的核心,负责初始化相关数据结构,以及向 mapping/target interface 注册 dm 的 target type 回调。
dm-selector.c是负责管理路径选择算法的苦函数。
dm-round-robin.c是必备的路径选择算法:在一条路径上完成指定的 IO 次数后就切换到下一条路径,不断循环。
另外两种是可选算法,见上述 Kconfig:
1)dm-service-time.c算法:根据路径的吞吐量以及完成的字节数选择负载较轻的路径。
2)dm-queue-length.c算法:根据正在处理的 IO 个数较少的路径。

dm-mapth.c核心:

/*-----------------------------------------------------------------* Module setup*---------------------------------------------------------------*/
static struct target_type multipath_target = {.name = "multipath",.version = {1, 13, 0},.features = DM_TARGET_SINGLETON | DM_TARGET_IMMUTABLE |DM_TARGET_PASSES_INTEGRITY,.module = THIS_MODULE,.ctr = multipath_ctr,.dtr = multipath_dtr,.clone_and_map_rq = multipath_clone_and_map,.release_clone_rq = multipath_release_clone,.rq_end_io = multipath_end_io,.map = multipath_map_bio,.end_io = multipath_end_io_bio,.presuspend = multipath_presuspend,.postsuspend = multipath_postsuspend,.resume = multipath_resume,.status = multipath_status,.message = multipath_message,.prepare_ioctl = multipath_prepare_ioctl,.iterate_devices = multipath_iterate_devices,.busy = multipath_busy,
};static int __init dm_multipath_init(void)
{int r;kmultipathd = alloc_workqueue("kmpathd", WQ_MEM_RECLAIM, 0);if (!kmultipathd) {DMERR("failed to create workqueue kmpathd");r = -ENOMEM;goto bad_alloc_kmultipathd;}/** A separate workqueue is used to handle the device handlers* to avoid overloading existing workqueue. Overloading the* old workqueue would also create a bottleneck in the* path of the storage hardware device activation.*/kmpath_handlerd = alloc_ordered_workqueue("kmpath_handlerd",WQ_MEM_RECLAIM);if (!kmpath_handlerd) {DMERR("failed to create workqueue kmpath_handlerd");r = -ENOMEM;goto bad_alloc_kmpath_handlerd;}r = dm_register_target(&multipath_target);if (r < 0) {DMERR("request-based register failed %d", r);r = -EINVAL;goto bad_register_target;}return 0;bad_register_target:destroy_workqueue(kmpath_handlerd);
bad_alloc_kmpath_handlerd:destroy_workqueue(kmultipathd);
bad_alloc_kmultipathd:return r;
}

在启动阶段dm_multipath_init向 dm 的 mapping/target interface 注册 target type,并且申请两个工作队列,用于异步执行耗时的 IO 操作。
使用的数据结构struct target_type:

struct target_type {uint64_t features;const char *name;struct module *module;unsigned version[3];dm_ctr_fn ctr;dm_dtr_fn dtr;dm_map_fn map;dm_clone_and_map_request_fn clone_and_map_rq;dm_release_clone_request_fn release_clone_rq;dm_endio_fn end_io;dm_request_endio_fn rq_end_io;dm_presuspend_fn presuspend;dm_presuspend_undo_fn presuspend_undo;dm_postsuspend_fn postsuspend;dm_preresume_fn preresume;dm_resume_fn resume;dm_status_fn status;dm_message_fn message;dm_prepare_ioctl_fn prepare_ioctl;
#if 1 /* CONFIG_BLK_DEV_ZONED */dm_report_zones_fn report_zones;
#endifdm_busy_fn busy;dm_iterate_devices_fn iterate_devices;dm_io_hints_fn io_hints;dm_dax_direct_access_fn direct_access;dm_dax_copy_iter_fn dax_copy_from_iter;dm_dax_copy_iter_fn dax_copy_to_iter;/* For internal device-mapper use. */struct list_head list;
};ctr = multipath_ctr
当用户程序加载 DeviceMapper 映射表时,如果其中的映射类型为 multipath,则会根据映射表中的参数调用此函数,
用于构建一个 Target Device:申请该实例内存,并进行简单初始化;解析参数,并根据参数创建优先级组,解析每个优先级组的路径算法;设置一些基本的 DeviceMapper 属性;
一个设备的多路径可以使用不同类型,划分成多个优先组,每个组需要包含一个路径选择器(路径选择算法)以及至少一条路径。参数组成部分:特性参数 硬件参数 优先级组以及路径参数参数解析实例:mpatha: 0 41940992 multipath 1 queue_if_no_path 1 alua 1 1 service-time 0 2 2 8:0 1 1 8:16 1 1其中 "1 queue_if_no_path 1 alua 1 1 service-time 0 2 2 8:0 1 1 8:16 1 1" 为传递给驱动的参数。1:表示特性参数,后跟特性参数,反之为 0,不跟参数1: 表示硬件参数,后跟硬件参数(alua),反之为 0,不跟参数1: 表示优先级组个数1: 下一个优先级组序号,主要用于解析优先级组时使用service-time:当前的优先组使用 service-time 算法0: 算法的参数个数为 02: 该优先组包含两条路径2: 每条路径的参数有两个8:0  1 1: 第一条路径的设备号为 8:0,每进行 1 次 IO 就切换路径,路径的吞吐量权重为 18:16 1 1: 第一条路径的设备号为 8:16,每进行 1 次 IO 就切换路径,路径的吞吐量权重为 1
最终用户态程序看到的结果就是:
mpatha (3600140554012e51273044299221f2903) dm-0 LIO-ORG,block1
size=20G features='1 queue_if_no_path' hwhandler='1 alua' wp=rw
`-+- policy='service-time 0' prio=50 status=active|- 7:0:0:0 sda 8:0  active ready running`- 6:0:0:0 sdb 8:16 active ready runningclone_and_map_rq = multipath_clone_and_map(IO 映射)
判断是否需要切换路径,并获取当前可用路径,把路径信息保存在请求的私有数据中
根据路径信息重定向请求,设置其目的队列
调用路径算法的 start_io 函数。
如果成功则返回 DM_MAPIO_REMAPPED,表明映射成功,通知 dm 框架重新投递请求。rq_end_io = multipath_end_io
映射完成后,进行 IO 结束时会调用 multipath_end_io 函数进行资源回收,
如果 IO 错误且 pgpath 不为空则使用 fail_path 函数通过 uevent 通知用户层此路径已经失效。
如果设置了 queue_if_no_path 为 0,则会返回 DM_ENDIO_REQUEUE,dm 会把这个请求重新加入队列,
否则直接返回 EIO 错误,最后清除在开始映射时创建的上下文信息,并调用路径选择算法的 end_io 函数。message = multipath_message
多路径驱动除了根据在给定的算法和配置下进行 IO 错误时以外,并不主动探测路径的状态,增加减少路径,
以及状态改变,这些都是由用户态的 multipathd 和 multipath 来控制完成,然后通过 dm 框架提供的接口,
调用 target type 驱动的 message 函数来完成。这里实现了切换优先组,失效以及使能路径等功能。

dm-path-selector.c路径选择算法管理库
使用链表对路径算法进行管理,注册,取消注册,获取,释放等。

/* Register a path selector */
int dm_register_path_selector(struct path_selector_type *type);/* Unregister a path selector */
int dm_unregister_path_selector(struct path_selector_type *type);/* Returns a registered path selector type */
struct path_selector_type *dm_get_path_selector(const char *name);/* Releases a path selector  */
void dm_put_path_selector(struct path_selector_type *pst);每个路径算法实现下面的结构回调:
/* Information about a path selector type */
struct path_selector_type {char *name;struct module *module;unsigned int table_args;unsigned int info_args;/** Constructs a path selector object, takes custom arguments*/int (*create) (struct path_selector *ps, unsigned argc, char **argv);void (*destroy) (struct path_selector *ps);/** Add an opaque path object, along with some selector specific* path args (eg, path priority).*/int (*add_path) (struct path_selector *ps, struct dm_path *path,int argc, char **argv, char **error);/** Chooses a path for this io, if no paths are available then* NULL will be returned.*/struct dm_path *(*select_path) (struct path_selector *ps,size_t nr_bytes);/** Notify the selector that a path has failed.*/void (*fail_path) (struct path_selector *ps, struct dm_path *p);/** Ask selector to reinstate a path.*/int (*reinstate_path) (struct path_selector *ps, struct dm_path *p);/** Table content based on parameters added in ps_add_path_fn* or path selector status*/int (*status) (struct path_selector *ps, struct dm_path *path,status_type_t type, char *result, unsigned int maxlen);int (*start_io) (struct path_selector *ps, struct dm_path *path,size_t nr_bytes);int (*end_io) (struct path_selector *ps, struct dm_path *path,size_t nr_bytes);
};create:实例化一个选择器
destroy:销毁一个选择器add_path:向该选择器增加一条路径
select_path:选择进行 IO 的路径fail_path:告诉选择器该路径失效
reinstate_path:告诉选择器该路径可用status:返回选择器的状态start_io:使用者进行 IO 前必须调用该函数(如路径算法记录 IO 次数)
end_io:使用者完成 IO 后必须调用该函数

round-robin路径选择器
每条路径只有一个参数,就是每条路径执行多少次 IO 后切换路径。
添加一条路径:

static int rr_add_path(struct path_selector *ps, struct dm_path *path,int argc, char **argv, char **error)
{struct selector *s = ps->context;struct path_info *pi;unsigned repeat_count = RR_MIN_IO;char dummy;unsigned long flags;if (argc > 1) {*error = "round-robin ps: incorrect number of arguments";return -EINVAL;}/* First path argument is number of I/Os before switching path */if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {*error = "round-robin ps: invalid repeat count";return -EINVAL;}if (repeat_count > 1) {DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead");repeat_count = 1;}/* allocate the path */pi = kmalloc(sizeof(*pi), GFP_KERNEL);if (!pi) {*error = "round-robin ps: Error allocating path context";return -ENOMEM;}pi->path = path;pi->repeat_count = repeat_count;path->pscontext = pi;spin_lock_irqsave(&s->lock, flags);list_add_tail(&pi->list, &s->valid_paths);spin_unlock_irqrestore(&s->lock, flags);return 0;
}

路径选择策略:
在每条路径进行repeat_count次 IO 后就切换到下一条路径,不断循环。(目前已经废弃,总是 1 次后就切换)

static struct dm_path *rr_select_path(struct path_selector *ps, size_t nr_bytes)
{unsigned long flags;struct selector *s = ps->context;struct path_info *pi = NULL;spin_lock_irqsave(&s->lock, flags);if (!list_empty(&s->valid_paths)) {pi = list_entry(s->valid_paths.next, struct path_info, list);list_move_tail(&pi->list, &s->valid_paths);}spin_unlock_irqrestore(&s->lock, flags);return pi ? pi->path : NULL;
}

service-time 路径选择器
添加一条路径:

static int st_add_path(struct path_selector *ps, struct dm_path *path,int argc, char **argv, char **error)
{struct selector *s = ps->context;struct path_info *pi;unsigned repeat_count = ST_MIN_IO;unsigned relative_throughput = 1;char dummy;unsigned long flags;
...
...

每条路径需要指定它的重复次数repeat_count,以及吞吐量权重值relative_throughput。

路径选择策略:
在 IO 开始与结束时,分别增加和减少该路径正在处理的 IO 字节数:

static int st_start_io(struct path_selector *ps, struct dm_path *path,size_t nr_bytes)
{struct path_info *pi = path->pscontext;atomic_add(nr_bytes, &pi->in_flight_size);return 0;
}static int st_end_io(struct path_selector *ps, struct dm_path *path,size_t nr_bytes)
{struct path_info *pi = path->pscontext;atomic_sub(nr_bytes, &pi->in_flight_size);return 0;
}在多个路径中,选择正在处理的数据量与吞吐量比值最小的那条路径
static int st_compare_load(struct path_info *pi1, struct path_info *pi2,size_t incoming)
{size_t sz1, sz2, st1, st2;sz1 = atomic_read(&pi1->in_flight_size);sz2 = atomic_read(&pi2->in_flight_size);/** Case 1: Both have same throughput value. Choose less loaded path.*/if (pi1->relative_throughput == pi2->relative_throughput)return sz1 - sz2;
...
...static struct dm_path *st_select_path(struct path_selector *ps, size_t nr_bytes)
{struct selector *s = ps->context;struct path_info *pi = NULL, *best = NULL;struct dm_path *ret = NULL;unsigned long flags;spin_lock_irqsave(&s->lock, flags);if (list_empty(&s->valid_paths))goto out;list_for_each_entry(pi, &s->valid_paths, list)if (!best || (st_compare_load(pi, best, nr_bytes) < 0))best = pi;if (!best)goto out;/* Move most recently used to least preferred to evenly balance. */list_move_tail(&best->list, &s->valid_paths);ret = best->path;
out:spin_unlock_irqrestore(&s->lock, flags);return ret;
}

queue-length 路径选择器:
添加一条路径:
只需要指定repeat_count即可

static int ql_add_path(struct path_selector *ps, struct dm_path *path,int argc, char **argv, char **error)
{struct selector *s = ps->context;struct path_info *pi;unsigned repeat_count = QL_MIN_IO;char dummy;unsigned long flags;
...
...

路径选择策略:
在 IO 开始和结束时,分别增加和减少正在处理的 IO 个数:

static int ql_start_io(struct path_selector *ps, struct dm_path *path,size_t nr_bytes)
{struct path_info *pi = path->pscontext;atomic_inc(&pi->qlen);return 0;
}static int ql_end_io(struct path_selector *ps, struct dm_path *path,size_t nr_bytes)
{struct path_info *pi = path->pscontext;atomic_dec(&pi->qlen);return 0;
}选择正在处理的 IO 个数最少的那条路径
static struct dm_path *ql_select_path(struct path_selector *ps, size_t nr_bytes)
{struct selector *s = ps->context;struct path_info *pi = NULL, *best = NULL;struct dm_path *ret = NULL;unsigned long flags;spin_lock_irqsave(&s->lock, flags);if (list_empty(&s->valid_paths))goto out;list_for_each_entry(pi, &s->valid_paths, list) {if (!best ||(atomic_read(&pi->qlen) < atomic_read(&best->qlen)))best = pi;if (!atomic_read(&best->qlen))break;}if (!best)goto out;/* Move most recently used to least preferred to evenly balance. */list_move_tail(&best->list, &s->valid_paths);ret = best->path;
out:spin_unlock_irqrestore(&s->lock, flags);return ret;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

市场复盘总结 20240103

仅用于记录当天的市场情况,用于统计交易策略的适用情况,以便程序回测 短线核心:不参与任何级别的调整 昨日回顾: 方法一:指标选股 select * from dbo.ResultAll where 入选类型 like %指标选股% and 入选日期=20240103;方法二:趋势选股法 1、最低价持续3日上涨 2、均价…

玩转贝启科技BQ3588C开源鸿蒙系统开发板 —— DevEco Studio下载与安装

一、下载DevEco Studio IDE开发工具 1. 登录鸿蒙官网 网址为&#xff1a; ​​​​​​​华为HarmonyOS智能终端操作系统官网 | 应用设备分布式开发者生态 页面如下&#xff1a; 2. 搜索“DevEco Studio IDE” 点击右上角的“请输入关键词”&#xff0c;在其中搜索“DevEc…

前端发开的性能优化 请求级:请求前(资源预加载和预读取)

预加载 预加载&#xff1a;是优化网页性能的重要技术&#xff0c;其目的就是在页面加载过程中先提前请求和获取相关的资源信息&#xff0c;减少用户的等待时间&#xff0c;提高用户的体验性。预加载的操作可以尝试去解决一些类似于减少首次内容渲染的时间&#xff0c;提升关键资…

智慧工厂:科技与制造融合创新之路

随着科技的迅猛发展&#xff0c;智慧工厂成为制造业领域的热门话题。智慧工厂利用先进的技术和智能化系统&#xff0c;以提高生产效率、降低成本、增强产品质量和灵活性为目标&#xff0c;正在引领着未来制造业的发展。 智慧工厂的核心是数字化和自动化生产&#xff0c;相较于传…

odoo 客制化审批流

以BPM、OA为代表的应用平台&#xff0c;低代码处理为前提的审批流功能定制化 功能介绍&#xff1a; 业务对象&#xff1a;针对侵入式注册BPM业务场景&#xff1a;设置审批场景&#xff1a;如&#xff1a;请假大于三天的场景、金额大于1000的场景节点条件&#xff1a; 当符合某…

Nacos设置账号密码

1、控制台设置 # 开启账号密码验证 nacos.core.auth.enabledtrue# 设置账号密码 nacos.core.auth.usernamenacos nacos.core.auth.passwordnacos1232、数据库设置 密码为&#xff1a;nacos&#xff0c;对应加密信息是&#xff1a; $2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2k…

CMU15-445-Spring-2023-Project #0 - C++ Primer

前置任务。 Task #1 - Copy-On-Write Trie Copy-on-write (COW) Trie 在进行修改时&#xff0c;不会立即复制整个数据结构。相反&#xff0c;它会在需要修改的节点被多个引用的时候才进行复制。当要对某个节点进行写操作&#xff08;添加子节点或者继续向下insert&#xff09…

Vue中全局事件总线的配置和原理

实现任意组件之间的通信 任意组件通信的原理&#xff1a; 1、实现任意组件之间的通信,需要一个傀儡。这个傀儡既能被vm访问到,也能被VueComponent访问。 2、VueComponent.prototype.proto Vue.prototype为图上1.0黄色的线路。是Vue让组件实例对象VueComponent可以访问到Vue原…

普中STM32-PZ6806L开发板(资料收集...)

简介 逐渐收集一些开发过程中使用到的文档资料数据手册 DS18B20 数据手册 DS18B20 Datasheet 开发文档 STM32F1各种文档 https://www.st.com/en/embedded-software/stm32cubef1.html#documentation HAL库文档开发文档 你使用的HAL文档, 在STM32CubeMX生成过程的最下面有…

LeetCode994腐烂的橘子(相关话题:矩阵dfs和bfs)

题目描述 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b;值 1 代表新鲜橘子&#xff1b;值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回 直到单…

GPU的硬件架构

SM: streaming Multiprocessor 流多处理器 sm里面有多个(sp)cuda core 32个线程称为一个warp&#xff0c;一个warp是一个基本执行单元 抽象概念&#xff1a;grid 网格 block 块 thread 线程 块中的线程大小是有讲究的&#xff0c;关乎到资源的调度&#xff0c;一般是128&#x…

How to understand HCS in Huawei Cloud

How to understand HCS in Huawei Cloud 概述 概述 参考文献 华为云产品首页 / 华为云Stack深度用云展望2025白皮书矿山智能化暨矿山大模型最佳实践白皮书城市数据空间CDS白皮书政务数据之道白皮书政务数据安全建设指南从理念到实践 商业银行混沌工程研究和实践白皮书现代化金融…

【Pytorch】学习记录分享11——PyTorch GAN对抗生成网络

PyTorch GAN对抗生成网络 0. 工程实现1. GAN对抗生成网络结构2. GAN 构造损失函数&#xff08;LOSS&#xff09;3. GAN对抗生成网络LOSS损失函数说明 0. 工程实现 1. GAN对抗生成网络结构 2. GAN 构造损失函数&#xff08;LOSS&#xff09; LOSS公式与含义&#xff1a; LOSS…

Nginx正向/反向代理与负载均衡策略

Nginx正向/反向代理与负载均衡策略 1、Nginx1.1、什么是Nginx1.2、Nignx的应用场景1.3、Nginx安装-Windows1.4、Nginx安装-Linux 2、Nginx基础使用2.1、目录结构2.2、基本运行原理2.3、Nginx配置文件2.4、服务配置2.5、泛域名解析 3、代理3.1、具体配置3.2、基于反向代理的负载…

Android App从备案到上架全过程

不知道大家注意没有,最近几年来,新的移动App想要上架是会非常困难的,并且对于个人开发者和小企业几乎是难如登天,各种备案和审核。但是到底有多难,或许只有上架过的才会有所体会。 首先是目前各大应用市场陆续推出新的声明,各种备案截止日期到12月就要到最后期限责令整改…

IDEA生成jar包

一、打开项目结构管理界面 英文版可以使用Ctrl Alt Shift S 打开 Project Structure 窗口 如下图 汉化idea 在设置中 tips&#xff1a;idea汉化包如果不能下载的话&#xff0c;可以手动下载安装 1、先确认自己安装的idea版本 2、来这里Chinese (Simplified) Language Pack…

【Web】vulhub-httpd apache解析漏洞复现(1)

目录 ①CVE-2017-15715 ②apache_parsing_vulnerability ①CVE-2017-15715 贴出源码&#xff1a; <?php if(isset($_FILES[file])) {$name basename($_POST[name]);$ext pathinfo($name,PATHINFO_EXTENSION);if(in_array($ext, [php, php3, php4, php5, phtml, pht]))…

Unity | 渡鸦避难所-5 | 角色和摄像机之间的遮挡物半透明

1 前言 角色在地图上移动到岩石后面时&#xff0c;完全被岩石遮挡&#xff0c;玩家只能看到岩石。这逻辑看起来没问题&#xff0c;但并不是玩家想要看到的画面&#xff0c;玩家更希望关注角色的状态 为了避免角色被遮挡&#xff0c;可以使用 Cinemachine Collider 功能&#x…

window将Mongodb加入环境变量

首先 你需要安装 Mongodb 如果没有下载安装 可以先查看我的文章 window下载安装Mongodb数据库 右击 此电脑/此计算机/我的电脑 选择属性 在新弹出的窗口中搜索 环境变量 新弹出的窗口中 选择环境变量 系统变量中找到 path 选择编辑 点击新建 然后将安装 Mongodb 的目录下的…

wps将姓名处理格式为:姓**

1.打开wps&#xff0c;在要处理数据右侧一个单元格 输入公式&#xff1a;LEFT(A1,1)&"**"&#xff0c;然后回车 2.按住ctrl和处理好的数据的右下角小方框&#xff0c;往下拖动即可生成格式为&#xff1a;姓** 格式的数据 3.复制生成的数据&#xff0c;右键选择 “…