当前位置: 首页 > news >正文

RDMA驱动学习(二)- command queue

为了实现用户对网卡硬件的配置,查询,或者执行比如create_cq等命令,mellanox网卡提供了command queue + mailbox的机制,本节将以create_cq为例看下这个过程。
command queue(后续简称cmdq)是一个4K对齐的长度为4K的连续物理内存,如图1所示,cmdq中有多个entry,entry的数量和entry的size都是从initialization segment中读到的。
在这里插入图片描述

图 1

entry格式如图2,软件的每一个cmd,会对应cmdq中的一个entry,cmd的input会存到entry中,如果input小于16B,那么就直接存储到command_input_inline_data,如果大于16B,那么剩余的会存到图一中的mailbox里,mailbox是一个链表结构,每个mailbox可以容纳512B的数据,第一个mailbox的指针存储在entry的input mailbox pointer。

在这里插入图片描述

图 2


将输入存到entry和对应的mailbox之后,软件会写cmdq的doorbell,doorbell位于initialization segment,可以理解为一个vector,软件会将这次entry对应的index写入到这个vector对应的bit通知硬件执行。硬件执行cmdq中entry的顺序是不确定的,初始化entry时,entry的ownership会被设置为HW,当硬件执行完成后,ownership会被重新设置为SW,因此软件可以轮询ownership确定硬件是否完成。

然后看下驱动中是怎么做的。

初始化

mlx5_cmd_init主要工作就是创建command queue的buf,然后将这块内存的总线地址告诉网卡硬件。
通过dma_pool_create创建dma内存池,每个内存块大小为sizeof(mlx5_cmd_prot_block),一个内存块表示了一个mailbox,mlx5_cmd_prot_block类型用于表示mailbox。

int mlx5_cmd_init(struct mlx5_core_dev *dev)
{int size = sizeof(struct mlx5_cmd_prot_block);int align = roundup_pow_of_two(size);struct mlx5_cmd *cmd = &dev->cmd;...cmd->pool = dma_pool_create("mlx5_cmd", mlx5_core_dma_dev(dev), size, align, 0);err = alloc_cmd_page(dev, cmd);...
}

然后通过dma_zalloc_coherent分配大小为一页的一致性dma映射,这块内存就是command queue的buf,虚拟地址保存在cmd_alloc_buf,dma地址保存到dma,同时还需要保证这个地址是页对齐的。如果不对齐,还需要重新分配一次大小为两页的dma映射,从而保证页对齐。

static int alloc_cmd_page(struct mlx5_core_dev *dev, struct mlx5_cmd *cmd)
{cmd->cmd_alloc_buf = dma_zalloc_coherent(mlx5_core_dma_dev(dev), MLX5_ADAPTER_PAGE_SIZE, &cmd->alloc_dma, GFP_KERNEL);.../* make sure it is aligned to 4K */if (!((uintptr_t)cmd->cmd_alloc_buf & (MLX5_ADAPTER_PAGE_SIZE - 1))) {cmd->cmd_buf = cmd->cmd_alloc_buf;cmd->dma = cmd->alloc_dma;cmd->alloc_size = MLX5_ADAPTER_PAGE_SIZE;return 0;}    dma_free_coherent(mlx5_core_dma_dev(dev), MLX5_ADAPTER_PAGE_SIZE, cmd->cmd_alloc_buf,cmd->alloc_dma);cmd->cmd_alloc_buf = dma_zalloc_coherent(mlx5_core_dma_dev(dev),2 * MLX5_ADAPTER_PAGE_SIZE - 1, &cmd->alloc_dma, GFP_KERNEL);...cmd->cmd_buf = PTR_ALIGN(cmd->cmd_alloc_buf, MLX5_ADAPTER_PAGE_SIZE);cmd->dma = ALIGN(cmd->alloc_dma, MLX5_ADAPTER_PAGE_SIZE);cmd->alloc_size = 2 * MLX5_ADAPTER_PAGE_SIZE - 1;return 0;
}

然后从Initialization Segment(后续简称iseg)读出cmdq_addr_l_sz,然后解析出低位的log_sz和log_stride,log_sz以log形式表示cmdq一共有多少个entry,log_stride以log形式表示cmdq的一个entry大小。然后将cmdq的dma地址写到iseg,这样硬件就知道cmdq的地址了。最后设置mode为CMD_MODE_POLLING,创建一个单线程的workqueue。到这里初始化就完成了。

int mlx5_cmd_init(struct mlx5_core_dev *dev)
{...cmd_l = ioread32be(&dev->iseg->cmdq_addr_l_sz) & 0xff;cmd->log_sz = cmd_l >> 4 & 0xf;cmd->log_stride = cmd_l & 0xf;cmd->max_reg_cmds = (1 << cmd->log_sz) - 1;cmd->bitmask = (1UL << cmd->max_reg_cmds) - 1;cmd_h = (u32)((u64)(cmd->dma) >> 32);cmd_l = (u32)(cmd->dma);iowrite32be(cmd_h, &dev->iseg->cmdq_addr_h);iowrite32be(cmd_l, &dev->iseg->cmdq_addr_l_sz);/* Make sure firmware sees the complete address before we proceed */wmb();...cmd->mode = CMD_MODE_POLLING;cmd->allowed_opcode = CMD_ALLOWED_OPCODE_ALL;cmd->wq = create_singlethread_workqueue(cmd->wq_name)...
}

cmd的下发与执行

输入输出

接下来以create_cq为例看下如何下发一个cmd,create_cq的输入由结构体mlx5_ifc_create_cq_in_bits解释,这个结构体不表示实际内存,比如opcode[0x10],通过u8类型表示仅仅是为了编程的方便,实际只占了16bit。输出由结构体mlx5_ifc_create_cq_out_bits表示。

struct mlx5_ifc_create_cq_in_bits {u8         opcode[0x10];u8         uid[0x10];u8         reserved_at_20[0x10];u8         op_mod[0x10];u8         reserved_at_40[0x40];struct mlx5_ifc_cqc_bits cq_context;u8         reserved_at_280[0x60];u8         cq_umem_valid[0x1];u8         reserved_at_2e1[0x59f];u8         pas[][0x40];
};

首先看下如何存储输入输出,mlx5_cmd_msg 表示一个msg,用于管理cmdq的entry和对应的mailbox,其中first用于存储开始的16B,如果输入大于16B,将会存储到next对应的mailbox链表里。mlx5_cmd_mailbox表示一个mailbox,其中buf为mailbox对应的虚拟地址,dma为这块buf的dma地址。next为软件侧的链,是一个虚拟地址,软件通过next遍历mailbox链表,硬件遍历mailbox的链表是通过buf里的next,是一个dma地址。

struct mlx5_cmd_msg {struct list_head        list;struct cmd_msg_cache           *parent;u32             len;struct mlx5_cmd_first       first;struct mlx5_cmd_mailbox        *next;
};struct mlx5_cmd_first {__be32      data[4];
};struct mlx5_cmd_mailbox {void           *buf;dma_addr_t  dma;struct mlx5_cmd_mailbox *next;
};

然后开始看执行cmd的过程,入口函数为mlx5_cmd_do,实际直接执行cmd_exec,其中callback和context为NULL,force_poling为false。

static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,int out_size, mlx5_cmd_cbk_t callback, void *context,bool force_polling)
{struct mlx5_cmd_msg *inb;struct mlx5_cmd_msg *outb;opcode = MLX5_GET(mbox_in, in, opcode);pages_queue = is_manage_pages(in);gfp = callback ? GFP_ATOMIC : GFP_KERNEL;inb = alloc_msg(dev, in_size, gfp);...
}

分配msg

cmd_exec首先通过alloc_msg分配input msg,初始化的时候已经分配了若干个mlx5_cmd_msg作为cache。如果in的长度小于16,就是可以放到一个cmdq entry,那么直接分配,不复用cache,否则会先看cache中有没有满足需要大小的msg,如果有则使用cache中的msg,假设没有命中cache,会通过mlx5_alloc_cmd_msg进行分配。

static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size,gfp_t gfp)
{struct mlx5_cmd_msg *msg = ERR_PTR(-ENOMEM);struct mlx5_cmd *cmd = &dev->cmd;if (in_size <= 16)goto cache_miss;...
cache_miss:msg = mlx5_alloc_cmd_msg(dev, gfp, in_size, 0);return msg;
}

先分配msg,然后通过mlx5_calc_cmd_blocks计算需要几个mailbox entry,计算方法为cmdq entry可以存16B,然后剩下的看需要几个mailbox存储,一个mailbox可以存储512B。然后通过alloc_cmd_box分配mailbox,这里就是通过dma_pool_zalloc从cmd->pool中获取一个dma内存块,对于每一个新分配的mailbox,通过mailbox的next字段链接到msg的next链表,软件通过这里的next就可以遍历链表。然后开始初始化mailbox的buf,设置block_num,设置token为0,然后设置buf的next为下一个mailbox的dma地址,硬件通过buf里的next就可以遍历链表了。

static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev,gfp_t flags, int size,u8 token)
{struct mlx5_cmd_mailbox *tmp, *head = NULL;struct mlx5_cmd_prot_block *block;struct mlx5_cmd_msg *msg;int err; int n;int i;msg = kzalloc(sizeof(*msg), flags);if (!msg)return ERR_PTR(-ENOMEM);msg->len = size;n = mlx5_calc_cmd_blocks(msg);for (i = 0; i < n; i++) {tmp = alloc_cmd_box(dev, flags);if (IS_ERR(tmp)) {mlx5_core_warn(dev, "failed allocating block\n");err = PTR_ERR(tmp);goto err_alloc;}    block = tmp->buf;tmp->next = head;block->next = cpu_to_be64(tmp->next ? tmp->next->dma : 0);block->block_num = cpu_to_be32(n - i - 1);block->token = token;head = tmp; }    msg->next = head;return msg; err_alloc:while (head) {tmp = head->next;free_cmd_box(dev, head);head = tmp; }    kfree(msg);return ERR_PTR(err);
}
static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,int out_size, mlx5_cmd_cbk_t callback, void *context,bool force_polling)
{...token = alloc_token(&dev->cmd);err = mlx5_copy_to_msg(inb, in, in_size, token);outb = mlx5_alloc_cmd_msg(dev, gfp, out_size, token);
}

然后开始分配token,token就是一个自增的uint8,标识一次cmd,一次cmd的cmdq entry和对应的所有mailbox的token都需要是一致的,然后通过mlx5_copy_to_msg将数据从in中拷贝到msg inb中,就是将连续的输入分散拷贝到cmd entry和mailbox中。然后分配output msg,和分配input msg一样。

执行

mlx5_cmd_invoke通过cmd_alloc_ent分配一个mlx5_cmd_work_ent ent,用于记录上下文信息,比如输入输出等。初始化ent中的work_struct work,对应的执行函数为cmd_work_handler,然后通过queue_work将ent->work提交到cmd->wq中执行,这里的wq就是初始化中创建的。wait_func通过wait_for_completion等待cmd->wq完成ent->work的执行。

static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,struct mlx5_cmd_msg *out, void *uout, int uout_size,mlx5_cmd_cbk_t callback,void *context, int page_queue, u8 *status,u8 token, bool force_polling)
{struct mlx5_cmd *cmd = &dev->cmd;struct mlx5_cmd_work_ent *ent;ent = cmd_alloc_ent(cmd, in, out, uout, uout_size,callback, context, page_queue);ent->token = token;ent->polling = force_polling;init_completion(&ent->handling);if (!callback)init_completion(&ent->done);INIT_DELAYED_WORK(&ent->cb_timeout_work, cb_timeout_handler);INIT_WORK(&ent->work, cmd_work_handler);if (page_queue) {cmd_work_handler(&ent->work);} else if (!queue_work(cmd->wq, &ent->work)) {mlx5_core_warn(dev, "failed to queue work\n");err = -ENOMEM;goto out_free;}if (callback)goto out; /* mlx5_cmd_comp_handler() will put(ent) */err = wait_func(dev, ent);...
}

然后看下work_queue是如何执行的,即cmd_work_handler。
首先执行,cmd->bitmask表示了当前cdmq的buff有哪些entry可用,cmd_alloc_index就是通过find_first_bit找到bitmask中第一个置为1的位ret,如果找到了那么将这位置清0,表示占用了,将ret记录到ent->idx。

static int cmd_alloc_index(struct mlx5_cmd *cmd)
{       unsigned long flags;int ret;                spin_lock_irqsave(&cmd->alloc_lock, flags);ret = find_first_bit(&cmd->bitmask, cmd->max_reg_cmds);if (ret < cmd->max_reg_cmds)clear_bit(ret, &cmd->bitmask);spin_unlock_irqrestore(&cmd->alloc_lock, flags);return ret < cmd->max_reg_cmds ? ret : -ENOMEM;
}           
static void cmd_work_handler(struct work_struct *work)
{struct mlx5_cmd_work_ent *ent = container_of(work, struct mlx5_cmd_work_ent, work);struct mlx5_cmd *cmd = ent->cmd;bool poll_cmd = ent->polling;struct mlx5_cmd_layout *lay;int alloc_ret;int cmd_mode;dev = container_of(cmd, struct mlx5_core_dev, cmd);cb_timeout = msecs_to_jiffies(mlx5_tout_ms(dev, CMD));complete(&ent->handling);sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem;down(sem);if (!ent->page_queue) {alloc_ret = cmd_alloc_index(cmd);if (alloc_ret < 0) { ...up(sem);return;}    ent->idx = alloc_ret;} else {...}    cmd->ent_arr[ent->idx] = ent; lay = get_inst(cmd, ent->idx);...
}

然后执行get_inst获取cmdq的第idx个entry,即lay,前边有说log_stride是以log表示的cmdq entry大小,因此就是(idx << cmd->log_stride)。

static struct mlx5_cmd_layout *get_inst(struct mlx5_cmd *cmd, int idx)
{       return cmd->cmd_buf + (idx << cmd->log_stride);
}

然后开始设置lay,首先拷贝input msg的前16B到lay->in,如果input msg有next,表示输入长度超过了16B,因此将第一个mailbox的dma地址设置到lay->in_ptr,接着设置inlen,同理设置out和outlen,然后设置token,signature,设置完成后将ownership bit修改为HW。

static void cmd_work_handler(struct work_struct *work) {...ent->lay = lay;memset(lay, 0, sizeof(*lay));memcpy(lay->in, ent->in->first.data, sizeof(lay->in));ent->op = be32_to_cpu(lay->in[0]) >> 16;if (ent->in->next)lay->in_ptr = cpu_to_be64(ent->in->next->dma);lay->inlen = cpu_to_be32(ent->in->len);if (ent->out->next)lay->out_ptr = cpu_to_be64(ent->out->next->dma);lay->outlen = cpu_to_be32(ent->out->len);lay->type = MLX5_PCI_CMD_XPORT;lay->token = ent->token;lay->status_own = CMD_OWNER_HW;set_signature(ent, !cmd->checksum_disabled);...
}

对lay的设置完成之后,然后开始写cmd queue的doorbell,doorbell是一个vector,软件通过写对应的bit表示新的cmd是cmd_buf的哪个位置。

static void cmd_work_handler(struct work_struct *work)
{...wmb();iowrite32be(1 << ent->idx, &dev->iseg->cmd_dbell);/* if not in polling don't use ent after this point */if (cmd_mode == CMD_MODE_POLLING || poll_cmd) {poll_timeout(ent);/* make sure we read the descriptor after ownership is SW */rmb();mlx5_cmd_comp_handler(dev, 1ULL << ent->idx, ent->ret == -ETIMEDOUT ?MLX5_CMD_COMP_TYPE_FORCED : MLX5_CMD_COMP_TYPE_POLLING);}
}

然后通过poll_timeout等待网卡执行,实现上就是通过轮询cmq entry的ownership字段,当ownership变成SW之后表示硬件已经完成执行。

static void poll_timeout(struct mlx5_cmd_work_ent *ent)
{struct mlx5_core_dev *dev = container_of(ent->cmd, struct mlx5_core_dev, cmd);u64 cmd_to_ms = mlx5_tout_ms(dev, CMD);unsigned long poll_end;u8 own;poll_end = jiffies + msecs_to_jiffies(cmd_to_ms + 1000);do {own = READ_ONCE(ent->lay->status_own);if (!(own & CMD_OWNER_HW)) {ent->ret = 0;return;}cond_resched();} while (time_before(jiffies, poll_end));ent->ret = -ETIMEDOUT;
}

然后执行mlx5_cmd_comp_handler将硬件输出的前16B拷贝到output msg,后边的输出可以通过output找到output mailbox获取,通过complete通知执行的完成。

static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, enum mlx5_comp_t comp_type)
{struct mlx5_cmd *cmd = &dev->cmd;struct mlx5_cmd_work_ent *ent;...memcpy(ent->out->first.data, ent->lay->out, sizeof(ent->lay->out));complete(&ent->done);...
}

最后回到cmd_exec会将output msg拷贝回out,对于create_cq来说就是mlx5_ifc_create_cq_in_bits。

相关文章:

RDMA驱动学习(二)- command queue

为了实现用户对网卡硬件的配置&#xff0c;查询&#xff0c;或者执行比如create_cq等命令&#xff0c;mellanox网卡提供了command queue mailbox的机制&#xff0c;本节将以create_cq为例看下这个过程。 command queue&#xff08;后续简称cmdq&#xff09;是一个4K对齐的长度…...

H2 Database IDEA 源码 DEBUG 环境搭建

H2 Database IDEA 源码 DEBUG 环境搭建 基于最新的 version-2.3.230 拉取分支。 git remote add h2 https://github.com/h2database/h2database.git git fetch h2 git checkout -b version-2.3.230 version-2.3.230使用 # 启动 java -jar h2*.jar# H2 shell 方式使用 java …...

nginx系列--(三)--http

本文主要介绍http模块accept read流程&#xff0c;&#xff01;&#xff01;&#xff01;请求对应的响应直接在read流程里就会返回给用户&#xff0c;而不需要通过write事件&#xff0c;和redis一样&#xff0c;基本都不通过eventloop write事件来发送响应给客户端&#xff0c;…...

通过Wireshark抓包分析,体验HTTP请求的一次完整交互过程

目录 一、关于Wireshark 1.1、 什么是Wireshark 1.2、下载及安装 二、HTTP介绍 2.1、HTTP请求过程介绍 2.2 、TCP协议基础知识 2.2.1、概念介绍 2.2.2、TCP协议的工作原理 2.2.3、三次握手建立连接 2.3.4、四次挥手断开连接 2.3、Wireshark抓包分析过程 2.3.1、三次握…...

Requestium:Python中的Web自动化新贵

文章目录 Requestium&#xff1a;Python中的Web自动化新贵背景&#xff1a;为何选择Requestium&#xff1f;Requestium是什么&#xff1f;如何安装Requestium&#xff1f;简单的库函数使用方法场景应用常见Bug及解决方案总结 Requestium&#xff1a;Python中的Web自动化新贵 背…...

2024版红娘金媒10.3婚恋相亲系统源码小程序(亲测)

1. 红娘服务 红娘服务模块是该系统的一大特色。专业红娘会通过分析用户的个人资料和偏好&#xff0c; 为用户提供精准的配对建议和个性化服务。用户可以预约红娘服务&#xff0c;通过红娘的介入&#xff0c;提升配对成功率。 2. 相亲活动 相亲活动模块用于组织和管理线下或线…...

k8s-实战——ES集群部署

文章目录 yaml文件es-pvc.yamles-svc.yamles-cluster-sts.yaml创建elasticsearch集群yaml文件 es-pvc.yaml 通过nfs服务进行新增pv并通过labels关联pvc前置准备需要提前准备pv的服务器以及挂在路径--- apiVersion: v1 kind: PersistentVolume metadata:name: nfs-es-pv-data-...

无人机的就业前景怎么样?

无人机的就业前景在当前及未来一段时间内都非常广阔。随着低空经济的蓬勃发展&#xff0c;无人机在农业、公安、测绘、交通、应急救援、影视拍摄等多个领域得到了广泛应用&#xff0c;对无人机操控员和相关专业人才的需求也随之急剧增加。 一、无人机操控员的就业前景 1. 高需…...

【学习】软件测试中V模型、W模型、螺旋模型三者介绍

在软件工程的星辰大海之中&#xff0c;存在着三种独特的航路图&#xff1a;V模型、W模型以及螺旋模型。它们分别以各自的方式描绘了软件开发与测试的不同旅程。 首先映入眼帘的是V模型——一个以垂直线条贯穿始终的简洁图形。这个模型如同一座倒立的“V”字形山峰&#xff0c;…...

Kafka存储机制大揭秘:从日志结构到清理策略的全面解析

文章目录 一、前言二、日志存储结构1.日志文件结构2.topic3.partition4.segment索引文件5.message结构6.message查找过程 三、存储策略1.顺序写2.页缓存3.零拷贝4.缓存机制 四、日志格式演变1.V0 版本2.V1 版本3.V0/V1消息集合4.V2 版本消息格式5.V2版本消息集合 五、偏移量维护…...

显卡服务器和普通服务器之间的区别有哪些?

显卡服务器也被称之为GPU服务器&#xff0c;显卡服务器与普通的服务器之间有着很明显的区别&#xff0c;下面就让我们共同来了解一下吧&#xff01; 普通服务器的主要处理器通常都是配备的中央处理器&#xff0c;可以用于执行大部分通用计算任务和操作系统的管理&#xff1b;而…...

国产科技里程碑:自主算力走向世界,“表格编程”横空出世

近日&#xff0c;中国高科技领域迎来里程碑式的进展。 据安徽省量子计算工程研究中心官方消息&#xff0c;本源量子计算科技&#xff08;合肥&#xff09;股份有限公司&#xff08;简称“本源量子”&#xff09;成功向海外销售了其第三代自主超导量子计算机“本源悟空”的机时。…...

人工智能如何改变未来生活:从医疗到日常的全面升级

人工智能如何改变未来生活&#xff1a;从医疗到日常的全面升级 随着人工智能&#xff08;AI&#xff09;技术的进步&#xff0c;我们正逐渐看到它为各行各业带来的巨大变革。从医疗、企业到日常生活&#xff0c;AI通过简化流程、提高效率&#xff0c;甚至改善生活质量&#xf…...

第112届全国糖酒会(3月成都)正式官宣!

作为食品饮料行业内备受瞩目的年度盛事&#xff0c;全国糖酒商品交易会&#xff08;简称“糖酒会”&#xff09;一直是各大厂商与经销商展现企业风采、寻觅合作伙伴及签署订单的关键舞台。2024年10月31日&#xff0c;第111届全国糖酒商品交易会&#xff08;秋糖&#xff09;在深…...

NFT Insider #154:The Sandbox Alpha 4 第四周开启,NBA Topshot NFT 销量激增至新高

市场数据 加密艺术及收藏品新闻 NBA 赛季开幕推动 Topshot NFT 销量激增至新高 随着波士顿凯尔特人队和纽约尼克斯队在 10 月 22 日开启 2024-2025 NBA 赛季的序幕&#xff0c;NBA Topshot 的 NFT 销售量达到了自上赛季季后赛以来的最高水平。截止到 10 月 27 日的这一周&…...

【Canal 中间件】Canal 实现 MySQL 增量数据的异步缓存更新

文章目录 一、安装 MySQL1.1 启动 mysql 服务器1.2 开启 Binlog 写入功能1.2.1创建 binlog 配置文件1.2.2 修改配置文件权限1.2.3 挂载配置文件1.2.4 检测 binlog 配置是否成功 1.3 创建账户并授权 二、安装 RocketMQ2.1 创建容器共享网络2.2 启动 NameServer2.3 启动 Broker2.…...

独立开发的个人品牌打造:个人IP与独立开发的结合

引言 个人品牌程序员也需要打造。在当今的创意经济中&#xff0c;个人IP与独立开发的结合成为了一种趋势&#xff0c;为个体带来了前所未有的机会和可能性。本文将探讨如何通过打造个人IP来增强独立开发的影响力&#xff0c;并探索这种结合为个人带来的潜在价值。 个人IP的重…...

每天一题:洛谷P2002 消息扩散

题目背景 本场比赛第一题&#xff0c;给个简单的吧&#xff0c;这 100 分先拿着。 题目描述 有 n 个城市&#xff0c;中间有单向道路连接&#xff0c;消息会沿着道路扩散&#xff0c;现在给出 n 个城市及其之间的道路&#xff0c;问至少需要在几个城市发布消息才能让这所有 …...

【深度学习】用LSTM写诗,生成式的方式写诗系列之一

Epoch 4: 100%|███████████████████████████████████████████████████████████| 63/63 [00:07<00:00, 8.85batch/s, acc18.5, loss5.8] [5] loss: 5.828, accuracy: 18.389 , lr:0.001000 Epoch 5: 100%|███…...

HomeAssistant自定义组件学习-【二】

#要说的话# 前面把中盛科技的控制器组件写完了。稍稍熟悉了一些HA&#xff0c;现在准备写窗帘控制组件&#xff0c;构想的东西会比较多&#xff0c;估计有些难度&#xff0c;过程会比较长&#xff0c;边写边记录吧&#xff01; #设备和场景环境# 使用的是Novo的电机&#xf…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器

一、原理介绍 传统滑模观测器采用如下结构&#xff1a; 传统SMO中LPF会带来相位延迟和幅值衰减&#xff0c;并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF)&#xff0c;可以去除高次谐波&#xff0c;并且不用相位补偿就可以获得一个误差较小的转子位…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...