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

RDMA ibverbs_API功能说明

设备管理

获取当前活动网卡

返回当前rdma设备列表

struct ibv_device **ibv_get_device_list(int *num_devices);//使用
struct ibv_device **dev_list = ibv_get_device_list(NULL);

获取网卡名

返回网卡名字字符串:如"mlx5_0",一般通过网卡名字确定将要使用的网卡

const char *ibv_get_device_name(struct ibv_device *device);

打开网卡

初始化一个网卡,返回该网卡的ibv_context上下文,后续对于该网卡的操作都基于这个上下文。

//初始化使用网卡
struct ibv_context *ibv_open_device(struct ibv_device *device);//释放网卡
int ibv_close_device(struct ibv_context *context);

资源初始化

所有基于网卡的配置,都是以该网卡的ibv_context上下文进行操作

申请PD保护域

PD保护域用来注册内存区域(MR)、创建队列等

struct ibv_pd *ibv_alloc_pd(struct ibv_context *context);

注册内存区域

注册可以本地/远程读写的内存区域

这个内存区域归属于PD保护域

struct ibv_mr *ibv_reg_mr(struct ibv_pd *pd, void *addr, size_t length, int access);enum ibv_access_flags {IBV_ACCESS_LOCAL_WRITE		= 1,IBV_ACCESS_REMOTE_WRITE		= (1<<1),IBV_ACCESS_REMOTE_READ		= (1<<2),IBV_ACCESS_REMOTE_ATOMIC	= (1<<3),IBV_ACCESS_MW_BIND		= (1<<4),IBV_ACCESS_ZERO_BASED		= (1<<5),IBV_ACCESS_ON_DEMAND		= (1<<6),IBV_ACCESS_HUGETLB		= (1<<7),IBV_ACCESS_RELAXED_ORDERING	= IBV_ACCESS_OPTIONAL_FIRST,
};

参数addr, 本地申请好的内存地址, length空间大小, access内存读写flag

创建完成通道

完成通道用于存放完成队列,I/O可由event事件驱动

struct ibv_comp_channel *ibv_create_comp_channel(struct ibv_context *context);

创建完成队列

完成队列绑定到完成通道,

参数cqe是队列大小

/*** ibv_create_cq - Create a completion queue* @context - Context CQ will be attached to* @cqe - Minimum number of entries required for CQ* @cq_context - Consumer-supplied context returned for completion events* @channel - Completion channel where completion events will be queued.*     May be NULL if completion events will not be used.* @comp_vector - Completion vector used to signal completion events.*     Must be >= 0 and < context->num_comp_vectors.*/
struct ibv_cq *ibv_create_cq(struct ibv_context *context, int cqe,void *cq_context,struct ibv_comp_channel *channel,int comp_vector);// 一般用法
struct ibv_cq *cq = ibv_create_cq(context, num, NULL, channel, 0);//还有ibv_create_cq_ex扩展完成队列,具备更多功能 To Do.

创建QueuePair

根据qp_init_attr参数设置QP,返回ibv_qp指针。

struct ibv_qp *ibv_create_qp(struct ibv_pd *pd,struct ibv_qp_init_attr *qp_init_attr);// 查询QP属性,创建后可用其查询确认相关属性
int ibv_query_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr,int attr_mask,struct ibv_qp_init_attr *init_attr);

qp_init_attr属性

其中发送/接收完成队列提前设置为已创建的完成队列。

struct ibv_qp_init_attr {void		       *qp_context;  struct ibv_cq	       *send_cq;  // 发送完成队列struct ibv_cq	       *recv_cq;  // 接收完成队列struct ibv_srq	       *srq;      // 共享接收队列, 多个的QP共享一个队列struct ibv_qp_cap	cap;		  // QP容量属性enum ibv_qp_type	qp_type;      // QP类型,可靠RC,不可靠UC,UD等int			sq_sig_all;
};struct ibv_qp_cap {uint32_t		max_send_wr;   //最大发送工作请求数量uint32_t		max_recv_wr;   //最大接收工作请求数量uint32_t		max_send_sge;  //一次发送最大的数据块数量uint32_t		max_recv_sge;  //一次接收最大的数据块数量uint32_t		max_inline_data; //内联数据大小,小数据和控制信令使用
};

收发API

建链

CM建链,使用CM VERBS,也是基于ibverbs 特殊的控制QP实现。

Socket建链,使用TCP/IP Socket建立链接

建链的本质是交换彼此的 LID, QPN,PSN,GID。

这个可以单独开一篇文档描述(To Do).

修改QueuePair

修改QP的相关状态属性

int ibv_modify_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr,int attr_mask);

在创建QP以后,通过该方法初始化QP

qp_state设置为INIT, pkey_index,access_flags设为0

struct ibv_qp_attr attr = {.qp_state        = IBV_QPS_INIT,.pkey_index      = 0,.port_num        = 1,.qp_access_flags = 0
};if (ibv_modify_qp(ctx->qp, &attr,IBV_QP_STATE              |IBV_QP_PKEY_INDEX         |IBV_QP_PORT               |IBV_QP_ACCESS_FLAGS)) 

在建链交互之后,获取到对端QPN, PSN,LID, GID,通过该函数设置这些参数,建立QP连接。

第一步先设置远端参数,进入RTR状态

RTR状态以后可以接收数据

	struct ibv_qp_attr attr = {.qp_state		= IBV_QPS_RTR,.path_mtu		= mtu,.dest_qp_num		= dest_qpn,.rq_psn			= dest_psn,.max_dest_rd_atomic	= 1,.min_rnr_timer		= 12,.ah_attr		= {.is_global	= 0,.dlid		= dest_lid,.sl		= sl,.src_path_bits	= 0,.port_num	= port}};if (dest->gid.global.interface_id) {attr.ah_attr.is_global = 1;attr.ah_attr.grh.hop_limit = 1;attr.ah_attr.grh.dgid = dest->gid;attr.ah_attr.grh.sgid_index = sgid_idx;}if (ibv_modify_qp(ctx->qp, &attr,IBV_QP_STATE              |IBV_QP_AV                 |IBV_QP_PATH_MTU           |IBV_QP_DEST_QPN           |IBV_QP_RQ_PSN             |IBV_QP_MAX_DEST_RD_ATOMIC |IBV_QP_MIN_RNR_TIMER)) {fprintf(stderr, "Failed to modify QP to RTR\n");return 1;}

第二步设置RTS状态

RTS状态后可以发送数据

	attr.qp_state	    = IBV_QPS_RTS;attr.timeout	    = 14;attr.retry_cnt	    = 7;attr.rnr_retry	    = 7;attr.sq_psn	    = my_psn;attr.max_rd_atomic  = 1;if (ibv_modify_qp(ctx->qp, &attr,IBV_QP_STATE              |IBV_QP_TIMEOUT            |IBV_QP_RETRY_CNT          |IBV_QP_RNR_RETRY          |IBV_QP_SQ_PSN             |IBV_QP_MAX_QP_RD_ATOMIC)) {fprintf(stderr, "Failed to modify QP to RTS\n");return 1;}

设置接收工作请求

将工作请求放入接收队列,用于接收数据

参数 wr是工作请求链表的头,每个工作请求包含接收内存区域,数据长度等

参数bad_wr用于返回发生错误的工作请求

/*** ibv_post_recv - Post a list of work requests to a receive queue.*/
static inline int ibv_post_recv(struct ibv_qp *qp, struct ibv_recv_wr *wr,struct ibv_recv_wr **bad_wr)
{return qp->context->ops.post_recv(qp, wr, bad_wr);
}

用法示例:

// 数据块列表
struct ibv_sge list = {.addr	= buf,      // 接收数据内存地址.length = size,     // 数据大小.lkey	= mr->lkey  // 内存注册区域的lkey
};
// 接收工作请求实例
struct ibv_recv_wr wr = {.wr_id	    = 1,     //工作请求id.sg_list    = &list, //使用数据块列表.num_sge    = 1,     //列表中数据块个数
};
struct ibv_recv_wr *bad_wr; // 接收错误请求// 一般可以重复执行ibv_post_recv,从而塞入一列工作请求
ibv_post_recv(qp, &wr, &bad_wr);

设置发送工作请求

设置一组工作请求wr,工作请求中包含传输数据块

/*** ibv_post_send - Post a list of work requests to a send queue.** If IBV_SEND_INLINE flag is set, the data buffers can be reused* immediately after the call returns.*/
static inline int ibv_post_send(struct ibv_qp *qp, struct ibv_send_wr *wr,struct ibv_send_wr **bad_wr)
{return qp->context->ops.post_send(qp, wr, bad_wr);
}

发送示例

struct ibv_sge list = {.addr	= buf,.length = size,.lkey	= lkey
};
struct ibv_send_wr wr = {.wr_id	    = 2,.sg_list    = &list,.num_sge    = 1,.opcode     = IBV_WR_SEND,.send_flags = ctx->send_flags,
};
struct ibv_send_wr *bad_wr;
ibv_post_send(ctx->qp, &wr, &bad_wr);

设置CQ通知

设置完成队列请求通知,当有完成消息到达完成队列,会触发event事件。

/*** ibv_req_notify_cq - Request completion notification on a CQ.  An*   event will be added to the completion channel associated with the*   CQ when an entry is added to the CQ.* @cq: The completion queue to request notification for.* @solicited_only: If non-zero, an event will be generated only for*   the next solicited CQ entry.  If zero, any CQ entry, solicited or*   not, will generate an event.*/
static inline int ibv_req_notify_cq(struct ibv_cq *cq, int solicited_only)
{return cq->context->ops.req_notify_cq(cq, solicited_only);
}

获取CQ事件

获取完成通道内的完成事件,后2参数返回CQ和CQ上下文

该函数是阻塞的,直到有完成事件才能调用成功

/*** ibv_get_cq_event - Read next CQ event* @channel: Channel to get next event from.* @cq: Used to return pointer to CQ.* @cq_context: Used to return consumer-supplied CQ context.** All completion events returned by ibv_get_cq_event() must* eventually be acknowledged with ibv_ack_cq_events().*/
int ibv_get_cq_event(struct ibv_comp_channel *channel,struct ibv_cq **cq, void **cq_context);

获取接口属性

获取接口属性,判断接口工作状态

/*** ibv_query_port - Get port properties*/
int ibv_query_port(struct ibv_context *context, uint8_t port_num,struct _compat_ibv_port_attr *port_attr);//获取的属性
struct ibv_port_attr {enum ibv_port_state	state;enum ibv_mtu		max_mtu;enum ibv_mtu		active_mtu;int			gid_tbl_len;uint32_t		port_cap_flags;uint32_t		max_msg_sz;uint32_t		bad_pkey_cntr;uint32_t		qkey_viol_cntr;uint16_t		pkey_tbl_len;uint16_t		lid;uint16_t		sm_lid;uint8_t			lmc;uint8_t			max_vl_num;uint8_t			sm_sl;uint8_t			subnet_timeout;uint8_t			init_type_reply;uint8_t			active_width;uint8_t			active_speed;uint8_t			phys_state;uint8_t			link_layer;uint8_t			flags;uint16_t		port_cap_flags2;
};

获取接口GID

获取的是接口的gid表项,其中index可以为0,1,2

对于CX5网卡

index 0 时, GID为 本地IPv6地址

index 1 时, GID为本地IPv4地址构成

具体可因网卡有所不同,如Soft-Roce网卡,index 2 对应的是IPv4地址构成的GID

/*** ibv_query_gid - Get a GID table entry*/
int ibv_query_gid(struct ibv_context *context, uint8_t port_num,int index, union ibv_gid *gid);

获取CQ消息

在获得CQ事件通知之后,执行该函数获取完成消息

/*** ibv_poll_cq - Poll a CQ for work completions* @cq:the CQ being polled* @num_entries:maximum number of completions to return* @wc:array of at least @num_entries of &struct ibv_wc where completions*   will be returned** Poll a CQ for (possibly multiple) completions.  If the return value* is < 0, an error occurred.  If the return value is >= 0, it is the* number of completions returned.  If the return value is* non-negative and strictly less than num_entries, then the CQ was* emptied.*/
static inline int ibv_poll_cq(struct ibv_cq *cq, int num_entries, struct ibv_wc *wc)
{return cq->context->ops.poll_cq(cq, num_entries, wc);
}

根据返回的wc中的信息可以根据wr_id关联到设置发送/接收的工作请求


struct ibv_wc {uint64_t		wr_id;enum ibv_wc_status	status;enum ibv_wc_opcode	opcode;uint32_t		vendor_err;uint32_t		byte_len;/* When (wc_flags & IBV_WC_WITH_IMM): Immediate data in network byte order.* When (wc_flags & IBV_WC_WITH_INV): Stores the invalidated rkey.*/union {__be32		imm_data;uint32_t	invalidated_rkey;};uint32_t		qp_num;uint32_t		src_qp;unsigned int		wc_flags;uint16_t		pkey_index;uint16_t		slid;uint8_t			sl;uint8_t			dlid_path_bits;
};

概念与关系

一个接口可以申请一个ibv_context

一个ibv_context上有多个PD保护域

一个ibv_context上有多个完成通道

一个完成通道只能绑定一个完成队列

一个PD保护域上可以有多个MR(memory region)内存区域

一个PD保护域上可以有多个QP(Queue Pair)

使用方法

  1. 打开网卡获取ibv_context上下文
  2. 申请PD保护域
  3. 注册内存区域
  4. 创建完成通道
  5. 设置完成队列(可添加完成事件通知)
  6. 建链(cm建链和Socket建链)
  7. 申请QP
  8. 设置发送工作请求(发数据)
  9. 设置接收工作请求 (收数据)
  10. 获取CQ消息 (发送数据的结果 和 接收到的数据)

发送数据

内存注册区域是可以用来设置发送和接收数据的缓冲区,内存区域(MR)有一个lkey字段指明该区域。

在设置发送工作请求时,使用的ibv_sge通过.lkey指定MR, 其addr字段指向MR内的地址空间。

待发送的数据都是先放入MR中的一段内存,ibv_sge.addr指向其中的内存地址,将ibv_sge封装进工作请求ibv_send_wr,塞入发送队列。

接收数据

在设置接收工作请求时,使用的ibv_sge通过.lkey指定MR, 其addr字段指向MR内的地址空间,该空间用于接收数据。

准备接收数据时,讲ibv_sge封装进工作请求ibv_recv_wr, 塞入接收队列

获取CQ消息,接收到数据后,可以根据返回的ibv_wc中的wr_id找到具体的ibv_recv_wr,进而获取ibv_sge指向地址空间,获得该数据。

参考代码

https://github.com/linux-rdma/rdma-core/blob/master/libibverbs/examples/rc_pingpong.c

— 此文写于2023年中

相关文章:

RDMA ibverbs_API功能说明

设备管理 获取当前活动网卡 返回当前rdma设备列表 struct ibv_device **ibv_get_device_list(int *num_devices);//使用 struct ibv_device **dev_list ibv_get_device_list(NULL);获取网卡名 返回网卡名字字符串&#xff1a;如"mlx5_0"&#xff0c;一般通过网卡…...

【C++语言】string 类

一、为什么要学习 string 类 C语言中&#xff0c;字符串是以 “\0” 结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些 str 系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合 OOP 的思想&#xff0c;而且底层空间需…...

快速上手gdb/cgdb

Linux调试器-gdb使用 1.背景2.调试原理、技巧命令2.1指令2.2 本质2.3 技巧 1.背景 程序的发布方式有两种&#xff0c;debug模式和release模式 Linux gcc/g出来的二进制程序&#xff0c;默认是release模式 要使用gdb调试&#xff0c;必须在源代码生成二进制程序的时候, 加上 -g…...

《养生》(二)

一、基础生活调整 1‌.作息规律‌ 固定每天7-8小时睡眠&#xff0c;尽量22:30前入睡&#xff0c;晨起后拉开窗帘晒太阳5分钟&#xff0c;调节生物钟‌ ‌2.饮食优化‌ 三餐定时&#xff0c;每餐细嚼慢咽20次以上&#xff0c;优先吃蔬菜和蛋白质&#xff08;如鸡蛋、豆腐&#x…...

JAVA:集成 Drools 业务规则引擎的技术指南

1、简述 Drools 是一个强大的业务规则引擎&#xff0c;适用于需要动态决策或规则管理的场景。它允许开发人员将业务逻辑与应用代码分离&#xff0c;使得业务人员可以通过规则文件维护和更新规则&#xff0c;而无需修改应用代码。本文将介绍 Drools 的基本概念、配置方式&#…...

GeoHD - 一种用于智慧城市热点探测的Python工具箱

GeoHD - 一种用于智慧城市热点探测的Python工具箱 详细原理请参考&#xff1a;Yan, Y., Quan, W., Wang, H., 2024. A data‐driven adaptive geospatial hotspot detection approach in smart cities. Trans. GIS tgis.13137. 代码下载&#xff1a;下载 1. 简介 在城市数据…...

记一次Ngnix配置

记一次Ngnix配置 配置Ngnix配置防火墙 假设一个服务器中有一个公网IP、一个内网IP&#xff0c;另外已经部署好后台服务的接口地址为http://内网ip:8088。 配置Ngnix 找到Ngnix的配置文件&#xff0c;通过在Ngnix的安装路径下的 \conf\nginx.conf 文件。 worker_processes 1;…...

2024年国赛高教杯数学建模C题农作物的种植策略解题全过程文档及程序

2024年国赛高教杯数学建模 C题 农作物的种植策略 原题再现 根据乡村的实际情况&#xff0c;充分利用有限的耕地资源&#xff0c;因地制宜&#xff0c;发展有机种植产业&#xff0c;对乡村经济的可持续发展具有重要的现实意义。选择适宜的农作物&#xff0c;优化种植策略&…...

java基础语知识(8)

类之间的关系 在类之间&#xff0c;最常见的关系有&#xff1a; 依赖&#xff08;“uses-a”&#xff09;;聚合&#xff08;“has-a”&#xff09;;继承&#xff08;“is-a”&#xff09;。 依赖&#xff1a;一种使用关系&#xff0c;即一个类的实现需要另一个类的协助&#x…...

室内定位精度方案对比

室内定位精度方案对比&#xff1a;成本、开发难度与精度的权衡 索引 引言 Wi-Fi 定位方案 定位原理 成本分析 开发难度 定位精度 蓝牙定位方案 定位原理 成本分析 开发难度 定位精度 超宽带&#xff08;UWB&#xff09;定位方案 定位原理 成本分析 开发难度 定…...

Pytorch深度学习教程_5_编写第一个神经网络

欢迎来到《pytorch深度学习教程》系列的第五篇&#xff01;在前面的四篇中&#xff0c;我们已经介绍了Python、numpy及pytorch的基本使用&#xff0c;并在上一个教程中介绍了梯度。今天&#xff0c;我们将探索神经网络&#xff0c;对于神经网络进行概述并进行简单的实践学习 欢…...

ImportError: cannot import name ‘FixtureDef‘ from ‘pytest‘

错误信息表明 pytest 在尝试导入 FixtureDef 时出现了问题。通常是由于 pytest 版本不兼容 或 插件版本冲突 引起的。以下是详细的排查步骤和解决方案&#xff1a; 1. 检查 pytest 版本 首先&#xff0c;确认当前安装的 pytest 版本。某些插件可能需要特定版本的 pytest 才能…...

改BUG:Mock测试的时候,when失效

问题再现&#xff1a; 这里我写了一测试用户注册接口的测试类&#xff0c;并通过when模拟下层的服务&#xff0c;但实际上when并没有奏效&#xff0c;还是走了真实的service层的逻辑。 package cn.ac.evo.review.test;import cn.ac.evo.review.user.UserMainApplication; imp…...

【自动化脚本工具】AutoHotkey (Windows)

目录 1. 介绍AutoHotkey2. 功能脚本集锦2.1 桌面键盘显示 1. 介绍AutoHotkey 支持Windows安装使用&#xff0c;下载地址为&#xff1a;https://www.autohotkey.com/ 2. 功能脚本集锦 2.1 桌面键盘显示 便于练习键盘盲打 脚本地址&#xff1a;https://blog.csdn.net/weixin_6…...

专题--Linux体系

Linux体系结构相关| ProcessOn免费在线作图,在线流程图,在线思维导图 ProcessOn是一个在线协作绘图平台&#xff0c;为用户提供强大、易用的作图工具&#xff01;支持在线创作流程图、思维导图、组织结构图、网络拓扑图、BPMN、UML图、UI界面原型设计、iOS界面原型设计等。同时…...

【DeepSeek】Mac m1电脑部署DeepSeek

一、电脑配置 个人电脑配置 二、安装ollama 简介&#xff1a;Ollama 是一个强大的开源框架&#xff0c;是一个为本地运行大型语言模型而设计的工具&#xff0c;它帮助用户快速在本地运行大模型&#xff0c;通过简单的安装指令&#xff0c;可以让用户执行一条命令就在本地运…...

Spring AI + Ollama 实现调用DeepSeek-R1模型API

一、前言 随着人工智能技术的飞速发展&#xff0c;大语言模型&#xff08;LLM&#xff09;在各个领域的应用越来越广泛。DeepSeek 作为一款备受瞩目的国产大语言模型&#xff0c;凭借其强大的自然语言处理能力和丰富的知识储备&#xff0c;迅速成为业界关注的焦点。无论是文本生…...

如何在本地和服务器新建Redis用户和密码

文章目录 一. Redis安装二. 新建Redis用户&#xff0c;测试连接2.1 本地数据库2.2 线上数据库2.2.1 安装和配置2.2.2 测试连接 三. 配置四. 分布式 一. Redis安装 Redis安装 可以设置开机自动启动&#xff0c;也可以在去查看系统服务&#xff0c;按[win R]&#xff0c;输入命…...

jmeter接口测试(一)

一、什么是接口测试&#xff1f;为什么要做接口测试&#xff1f; 接口测试&#xff1a;就是测试项目和项目之间&#xff0c;模块和模块之间&#xff0c;组件和组件之间的数据交互和权限鉴定&#xff08;鉴权&#xff09;。 前后端分离&#xff1a;前后端联调。mock模拟&#x…...

Java-11

淘天集团2025届春季校园招聘在线笔试-研发 1。设有一个顺序共享栈storageArray[70]&#xff0c;其中栈X的栈顶指针top1的初值为-1&#xff0c;栈Y的栈顶指针top2的初值为70&#xff0c;通过不断进行入栈操作&#xff0c;直到storageArray数组已满&#xff0c;此时top1 top2 …...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...