Linux VLAN相关概念、转发原理及处理流程

news2024/10/6 6:47:01

背景

        二、三层转发是网络工程师经常接触到的一个问题,VLAN配置是二、三层转中一个很重要的概念,在配置VLAN的情况下,内核对报文是如何处理的呢?

概念

        了解VLAN转发,首先必须知道VLAN相关概念;

VLAN作用

        VLAN(Virtual Local Area Network)虚拟局域网,是一组逻辑上的设备和用户,这组设备和用户不受地理位置的限制,相互之间的通信好像在同一网段一样。

        VLAN用于逻辑上划分广播域,VLAN内可以通信,VLAN间不可以通信,从而实现网络隔离,将广播报文限制在一个VLAN内,缩小广播域,减小广播风暴对整个通信网络的影响;此外,由于只允许相同VLAN内通信,对于重要的敏感数据降低了泄漏的风险,增加了安全性。

        为了实现转发控制,在待转发的以太网帧中添加VLAN标签,设定端口对该标签和帧的处理方式;包括添加标签、去掉标签、转发帧、丢弃帧。

VLAN帧格式

       对比看出,打上标签的帧多个4个字节的VLAN标记,又称tag;VLAN Tag具体格式如下:

        TIPD:标签协议标识,2个字节,固定为0x8100,表明这是一个带有802.1Q 标签的以太网帧

        PRI:表示帧优先级,3bit,取值0-7,用于提供有差别的转发服务

        CFI:标准格式指示符,1bit,0 表示经典格式,1 表示非经典格式,如令牌环网,在以太网环境中,通常为0

        VLAN ID :VLAN标识,表示一个帧所属VLAN,12bit,VLAN ID 范围为 0 --4095(2^12-1),但0、4095保留,仅限系统使用,因此VLAN ID的有效范围为1-4094。

        802.1Q帧由交换机设备处理,而非用户主机;

        1)当交换机设备接收到普通以太网帧时,会插入4字节的VLAN tag变成802.1Q帧,称为“打标签”

        2)当交换机转发802.1Q帧时,若帧携带VLAN ID 与端口默认VLAN ID一致,则去除VLAN tag,称为“去标签”

        端口接收时总是希望能“打标签”,发送时总希望去除“标签”

VLAN划分

        VLAN划分即通过某种方法、约定或策略将特定端口、特定报文划分到特定VLAN的方式,目前VLAN划分有如下几种方式:

        1)基于端口划分(最常用)

         根据端口编号划分 VLAN

        2)基于MAC地址划分

        根据端口MAC地址划分VLAN

        3)基于协议划分

        根据数据帧的协议类型(或协议簇类型)、封装格式划分VLAN

        4)基于策略划分

        使用几个条件的组合来划分VLAN

        此外还有基于IP子网的划分等,具体使用何种方式,依据使用场景而定。

VLAN链路

        VLAN技术的出现,使得交换网络中出现了带VLAN标签的以太网帧和不带VLAN标签的以太网帧,因此也对以太网链路作了划分:

        1)接入链路,通常用于连接用户主机与交换设备,接入链路上的以太网帧通常不带标签

        2)干道链路,用于交换机间或交换机与路由器间互连,通过的帧一般为带Tag的帧,也允许不带Tag的帧通过。

转发原理

        如前所述,VLAN技术出现以后,以太网中出现了带Tag的以太网帧和不带Tag的以太网帧,同时也规定了端口对帧和Tag的处理方式,在了解转发原理之前,需要知道几个概念。

缺省VLAN

        每个端口都有一个默认VLAN,称为缺省VLAN,在思科交换机上称为Native VLAN,华为交换机上称为Port VLAN ID(PVID)。

端口模式

       交换机端口分为三种模式:Access模式、Trunk模式、Hybrid模式,因此也对三种不同类型的端口,各模式下端口对tag、以太网帧的处理方式如何?下面分别讨论:

Access口

        Access口又称接入口,只能属于一个VLAN;其缺省VLAN 与端口VLAN相同。

        接收处理方法:接入端口只能接收不带VLAN Tag的以太网帧,对于带Tag的以太网帧直接丢弃。接收后依据PVID打上VLAN 标签在设备内部转发处理。

        发送处理方法:若帧中的VLAN ID和端口的PVID 相同,则剥离Tag发送,否则不转发。

Trunk口

        Access口又称为干道端口,可以同时属于多个VLAN。

       接收处理方法:

        1)对于未打标签的帧,依据PVID打上VLAN Tag。

        2)对于已有Tag的帧,若端口允许通过,直接接收,否则丢弃。

        发送处理方法:

        1)若帧携带打tag与端口PVID相同,剥离tag发送。

        2)否则,直接发送。

Hybrid口

        混合端口,仅华为交换机有。混合端口既可以用于交换机间或交换机与路由器间(同Trunk口),也可用于用户主机与交换机间(同Access口)。

        混合端口可以属于多个VLAN。

        接收处理方法(同trunk):

        1)若帧未携带Tag,依据PVID 打上VLAN标签

        2)对于已携带VLAN tag帧,若端口允许进入,直接接收,否则丢弃。

        发送处理方法:查看VID是否在端口去标签列表中

        1)在,去标签转发

        2)不在,直接转发

        综上可见,在增加VLAN技术后,端口对报文的处理实质就是打标签、剥离标签、转发或丢弃的过程,接下来我们看看内核是如何实现的。

内核实现

        对于VLAN内核中的处理,我们也从接收、发送两方面学习

接收

        内核收包会经过一系列的过程,最终会通过__netif_receive_skb_core接口将报文发送到上层协议栈,这里直接从__netif_receive_skb_core处理分析。具体实现如下:

static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
				    struct packet_type **ppt_prev)
{
	.....

	/*设置网络层头部起始位置*/
	skb_reset_network_header(skb);
	if (!skb_transport_header_was_set(skb))
		/*设置传输层部起始位置*/
		skb_reset_transport_header(skb);
	/* 设置以太网头部长度 */
	skb_reset_mac_len(skb);

	pt_prev = NULL;

another_round:
	skb->skb_iif = skb->dev->ifindex;

	__this_cpu_inc(softnet_data.processed);

	if (static_branch_unlikely(&generic_xdp_needed_key)) {
		int ret2;

		migrate_disable();
		ret2 = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb);
		migrate_enable();

		if (ret2 != XDP_PASS) {
			ret = NET_RX_DROP;
			goto out;
		}
	}

	/* 如果是vlan协议报文802.1Q,提取VLAN信息,去tag */
	if (eth_type_vlan(skb->protocol)) {
		skb = skb_vlan_untag(skb);
		if (unlikely(!skb))
			goto out;
	}

	if (skb_skip_tc_classify(skb))
		goto skip_classify;

	if (pfmemalloc)
		goto skip_taps;

	/* 如果报文属于某类协议报文,上送对应协议处理 */
	list_for_each_entry_rcu(ptype, &ptype_all, list) {
		if (pt_prev)
			ret = deliver_skb(skb, pt_prev, orig_dev);
		pt_prev = ptype;
	}

	list_for_each_entry_rcu(ptype, &skb->dev->ptype_all, list) {
		if (pt_prev)
			ret = deliver_skb(skb, pt_prev, orig_dev);
		pt_prev = ptype;
	}

skip_taps:
#ifdef CONFIG_NET_INGRESS
	if (static_branch_unlikely(&ingress_needed_key)) {
		bool another = false;

		nf_skip_egress(skb, true);
		skb = sch_handle_ingress(skb, &pt_prev, &ret, orig_dev,
					 &another);
		if (another)
			goto another_round;
		if (!skb)
			goto out;

		nf_skip_egress(skb, false);
		if (nf_ingress(skb, &pt_prev, &ret, orig_dev) < 0)
			goto out;
	}
#endif
	skb_reset_redirect(skb);
skip_classify:
	if (pfmemalloc && !skb_pfmemalloc_protocol(skb))
		goto drop;

	if (skb_vlan_tag_present(skb)) {
		if (pt_prev) {
			ret = deliver_skb(skb, pt_prev, orig_dev);
			pt_prev = NULL;
		}
        /* vlan 报文接收处理 */
		if (vlan_do_receive(&skb))
			goto another_round;
		else if (unlikely(!skb))
			goto out;
	}
	
	.....
	return ret;
}

        驱动层接收上来的数据包已经设置好了skb的protocol字段(为8021Q或者8021AD)。依次调用skb_vlan_untag与vlan_do_receive函数进行处理。 

带tag

        skb_vlan_untag主要功能是从数据包中提取vlan信息,保存到skb结构中,然后从数据包中取出vlan相关字段,另外,获取数据包的真实协议类型更新到skb的protocol字段(三层协议类型),替换了之前的ETH_P_8021Q或者ETH_P_8021AD;具体实现如下:

struct sk_buff *skb_vlan_untag(struct sk_buff *skb)
{
	struct vlan_hdr *vhdr;
	u16 vlan_tci;

	if (unlikely(skb_vlan_tag_present(skb))) {
		/* vlan_tci is already set-up so leave this for another time */
		return skb;
	}

	/* 共享拷贝skb */
	skb = skb_share_check(skb, GFP_ATOMIC);
	if (unlikely(!skb))
		goto err_free;
	/* We may access the two bytes after vlan_hdr in vlan_set_encap_proto(). */
	if (unlikely(!pskb_may_pull(skb, VLAN_HLEN + sizeof(unsigned short))))
		goto err_free;

	/* 获取vlan信息 */
	vhdr = (struct vlan_hdr *)skb->data;
	vlan_tci = ntohs(vhdr->h_vlan_TCI);
	/* 更新vlan信息到skb */
	__vlan_hwaccel_put_tag(skb, skb->protocol, vlan_tci);

	skb_pull_rcsum(skb, VLAN_HLEN);
	/* 更新skb->protocol,以太网帧或802.3帧 */
	vlan_set_encap_proto(skb, vhdr);

	/* 通过拷贝覆盖,剥离vlan Tag, 重新设置mac头部指针 */
	skb = skb_reorder_vlan_header(skb);
	if (unlikely(!skb))
		goto err_free;

	skb_reset_network_header(skb);
	if (!skb_transport_header_was_set(skb))
		skb_reset_transport_header(skb);
	skb_reset_mac_len(skb);

	return skb;

err_free:
	kfree_skb(skb);
	return NULL;
}

        提取vlan信息,剥离vlan tag后由vlan_do_receive处理 

不带Tag

       无tag跳过skb_vlan_untag处理,但所有从vlan下的物理接口接收上来的报文都要经过vlan_do_receive处理;具体流程如下:

       1) 系统中必须要有与数据包中vlan id相同的vlan设备,否则结束处理。

       2) 找到此vlan设备后将其赋值给skb的dev,此时就完成了接收设备从物理网卡到vlan设备的转换,skb中的vlan_tci也就没有用处了可以清理。

        3)VLAN设备可能具有与其所在物理设备不同的MAC地址,在此情况下物理设备驱动程序会赋值PACKET_OTHERHOST到skb的pkt_type,需要进一步判断数据包目的MAC是否为vlan的MAC地址,如果是,修改pkt_type为PACKET_HOST,表示为发往本机的数据包;具体实现如下:

bool vlan_do_receive(struct sk_buff **skbp)
{
	struct sk_buff *skb = *skbp;
	__be16 vlan_proto = skb->vlan_proto;
	u16 vlan_id = skb_vlan_tag_get_id(skb);
	struct net_device *vlan_dev;
	struct vlan_pcpu_stats *rx_stats;

	/* 根据vlan协议,vlan id在系统上查找vlan设备 */
	vlan_dev = vlan_find_dev(skb->dev, vlan_proto, vlan_id);
	if (!vlan_dev)
		return false;

	/* 共享拷贝skb */
	skb = *skbp = skb_share_check(skb, GFP_ATOMIC);
	if (unlikely(!skb))
		return false;

	if (unlikely(!(vlan_dev->flags & IFF_UP))) {
		kfree_skb(skb);
		*skbp = NULL;
		return false;
	}

	/* 更新skb->dev为vlan_dev, 将报文上送vlan */
	skb->dev = vlan_dev;
	if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) {
		/* Our lower layer thinks this is not local, let's make sure.
		 * This allows the VLAN to have a different MAC than the
		 * underlying device, and still route correctly. */
		 /* 如果目的mac等于vlan dev的mac,也认为是到本机的 */
		if (ether_addr_equal_64bits(eth_hdr(skb)->h_dest, vlan_dev->dev_addr))
			skb->pkt_type = PACKET_HOST;
	}

	if (!(vlan_dev_priv(vlan_dev)->flags & VLAN_FLAG_REORDER_HDR) &&
	    !netif_is_macvlan_port(vlan_dev) &&
	    !netif_is_bridge_port(vlan_dev)) {
		unsigned int offset = skb->data - skb_mac_header(skb);

		/*
		 * vlan_insert_tag expect skb->data pointing to mac header.
		 * So change skb->data before calling it and change back to
		 * original position later
		 */
		skb_push(skb, offset);
		skb = *skbp = vlan_insert_inner_tag(skb, skb->vlan_proto,
						    skb->vlan_tci, skb->mac_len);
		if (!skb)
			return false;
		skb_pull(skb, offset + VLAN_HLEN);
		skb_reset_mac_len(skb);
	}

	skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci);
	/* 去掉skb vlan标识,同普通报文处理 */
	__vlan_hwaccel_clear_tag(skb);

    /* 获取并更新vlan_dev 统计信息 */
	rx_stats = this_cpu_ptr(vlan_dev_priv(vlan_dev)->vlan_pcpu_stats);
	u64_stats_update_begin(&rx_stats->syncp);
	u64_stats_inc(&rx_stats->rx_packets);
	u64_stats_add(&rx_stats->rx_bytes, skb->len);
	if (skb->pkt_type == PACKET_MULTICAST)
		u64_stats_inc(&rx_stats->rx_multicast);
	u64_stats_update_end(&rx_stats->syncp);

	return true;
}

发送

        内核下通过网络设备发送报文,通常会调用设备的.ndo_start_xmit函数,该接口在设备初始化阶段注册或指定,对于vlan设备具体为:

        1)vlan_dev_init初始化过程

        2)网络设备操作处理函数指定:dev->netdev_ops = &vlan_netdev_ops;vlan_netdev_ops具体定义如下:

static const struct net_device_ops vlan_netdev_ops = {
	......
    /* 报文发送处理函数 */
	.ndo_start_xmit =  vlan_dev_hard_start_xmit,
	......
};

        由此可见,vlan设备的发送处理函数为:vlan_dev_hard_start_xmit。

打tag

        vlan_dev_hard_start_xmit函数的具体实现如下:

static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
					    struct net_device *dev)
{
	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
	unsigned int len;
	int ret;

	/* Handle non-VLAN frames if they are sent to us, for example by DHCP.
	 *
	 * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING
	 * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs...
	 */
	/* 以太网协议类型不等于0x8100,说明还没有打上vlan tag */
	if (vlan->flags & VLAN_FLAG_REORDER_HDR ||
	    veth->h_vlan_proto != vlan->vlan_proto) {
		u16 vlan_tci;
		/* 打tag */
		vlan_tci = vlan->vlan_id;
		vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority);
		__vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);
	}

	/* 将skb->dev设置为vlan下的物理口 */
	skb->dev = vlan->real_dev;
	len = skb->len;
	if (unlikely(netpoll_tx_running(dev)))
		return vlan_netpoll_send_skb(vlan, skb);

	/* 报文发送 */
	ret = dev_queue_xmit(skb);

	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
		struct vlan_pcpu_stats *stats;

		/* vlan接口发送信息更新 */
		stats = this_cpu_ptr(vlan->vlan_pcpu_stats);
		u64_stats_update_begin(&stats->syncp);
		u64_stats_inc(&stats->tx_packets);
		u64_stats_add(&stats->tx_bytes, len);
		u64_stats_update_end(&stats->syncp);
	} else {
		this_cpu_inc(vlan->vlan_pcpu_stats->tx_dropped);
	}

	return ret;
}

不打(剥离)tag

        对于VLAN报文,剥离tag在接收阶段已经处理,若无需打tag,直接通过VLAN下的物理口发送。

参考连接

VLAN技术

VLAN简介

VLAN技术详解

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

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

相关文章

闲聊电脑(4)硬盘分区

夜深人静&#xff0c;万籁俱寂&#xff0c;老郭趴在电脑桌上打盹&#xff0c;桌子上的小黄鸭和桌子旁的冰箱又开始窃窃私语…… 小黄鸭&#xff1a;冰箱大哥&#xff0c;上次你说的那个“分区”和“格式化”是什么意思&#xff1f; 冰箱&#xff1a;分区么&#xff0c;就是分…

Kotlin快速入门系列10

Kotlin的委托 委托模式是常见的设计模式之一。在委托模式中&#xff0c;有两个对象参与处理同一个请求&#xff0c;接受请求的对象将请求委托给另一个对象来处理。与Java一样&#xff0c;Kotlin也支持委托模式&#xff0c;通过关键字by。 类委托 类的委托即一个类中定义的方…

VPP学习-startup.conf配置文件

背景 VPP&#xff08;Vector Packet Processing&#xff0c;矢量报文处理&#xff09;&#xff0c;作为一个开源的高性能数据包处理框架&#xff0c;旨在提供可扩展、灵活且高效的网络数据包处理能力&#xff1b;由于传统Linux 内核协议栈整体网络吞吐性能的局限性&#xff0c;…

MySQL | 一定会走索引却没有走的原因,日志报conversion相关错误

TL;DR&#xff1a;函数作用于表字段时&#xff0c;索引会失效。 具体情况 Indexes如下&#xff1a; SQL如下&#xff1a; explain select *from accounting_status_flow_tab_00000000WHERE ( client_no 6848134000 and loan_no 0119324345936016261000 )order by modif…

MySQL 8.0 引入 innodb_flush_method 等新参数的系统调用分析

本文我们将讨论如何在操作系统级别验证 innodb_flush_method 和 innodb_use_fdatasync 修改为默认值之外的其它值&#xff08;特别是 O_DIRECT 是最常用的&#xff09;后的效果。 介绍 首先&#xff0c;让我们定义该 innodb_flush_method 参数的作用。它规定了 InnoDB 如何管…

加快“数实融合”推进新型工业化 济南个性化体检“把脉”企业数字化转型难点痛点

加快“数实融合”推进新型工业化 济南个性化体检“把脉”企业数字化转型难点痛点 以主流价值为光&#xff0c;解读“济南故事”&#xff0c;解码C位新闻。爱济南客户端推出“追光”栏目&#xff0c;发挥新型主流媒体权威性和创新性&#xff0c;着眼经济社会发展紧要处、关键处、…

Java学习day26:和线程相关的Object类的方法、等待线程和唤醒线程(知识点详解)

声明&#xff1a;该专栏本人重新过一遍java知识点时候的笔记汇总&#xff0c;主要是每天的知识点题解&#xff0c;算是让自己巩固复习&#xff0c;也希望能给初学的朋友们一点帮助&#xff0c;大佬们不喜勿喷(抱拳了老铁&#xff01;) 往期回顾 Java学习day25&#xff1a;守护线…

压力测试工具-Jmeter使用总结

目录 一.前言 二.线程组 三.线程组的组件 四.线程组-HTTP请求 1、JSON提取器 2、XPATH提取器 3、正则表达式提取器 五.线程组-断言 1、响应断言 2、JSON断言 六.创建测试 1.创建线程组 2.配置元件 3.构造HTTP请求 4.添加HTTP请求头 5.添加断言 6.添加查看结果树…

Python之Numpy 和 Pandas

目录 2.1 numpy import numpy as np array np.array([[1,2,3],[2,3,4]]) print(array) print(number of dim:,array.ndim) print(shape:,array.shape) print(size:,array.size)pandas 1,pandas 基本介绍 df2 pd.DataFrame({A:1.,B:pd.Timestamp(20130102),C:pd.Series(1,i…

算法设计与分析实验:最短路径算法

一、网络延迟时间 力扣第743题 本题采用最短路径的思想进行求解 1.1 具体思路 &#xff08;1&#xff09;使用邻接表表示有向图&#xff1a;首先&#xff0c;我们可以使用邻接表来表示有向图。邻接表是一种数据结构&#xff0c;用于表示图中顶点的相邻关系。在这个问题中&am…

【C语言进阶篇】assert宏 使用详解

文章目录 一、assert简介 二、assert使用方法和规则 2.1 头文件 2.2 原型 2.3 功能 2.4 示例 2.5 assert的打开与关闭 三、注意事项 3.1 运行效率问题 3.2 assert只适用于调试版本 3.3 资源释放与清理 3.4 过度依赖 四、总结 个人主页&#xff1a; 倔强的石头的…

Datax3.0+DataX-Web部署分布式可视化ETL系统

一、DataX 简介 DataX 是阿里云 DataWorks 数据集成的开源版本&#xff0c;主要就是用于实现数据间的离线同步。DataX 致力于实现包括关系型数据库&#xff08;MySQL、Oracle 等&#xff09;、HDFS、Hive、ODPS、HBase、FTP 等各种异构数据源&#xff08;即不同的数据库&#x…

(2024,SaFaRI,双三上采样和 DFT,空间特征和频率特征)基于扩散模型的图像空间和频率感知恢复方法

Spatial-and-Frequency-aware Restoration method for Images based on Diffusion Models 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 3. 方法 3.1 修改数据保真度 3.2 …

Python中使用Opencv-python库绘制直线、矩形、圆、文本

Python中使用Opencv-python库绘制直线、矩形、圆、文字 在Python中使用Opencv-python绘制直线、矩形、圆、文本非常简单&#xff0c;分别使用到line、rectangle、circle、putText这几个函数&#xff0c;具体可以参考https://docs.opencv.org/4.9.0/d6/d6e/group__imgproc__dra…

基础小白快速入门c语言----数据类型

数据类型&#xff0c;运算符&#xff0c;表达式 1c语言支持 数据类型 1.基础类型&#xff08;基本类型&#xff09; a数值类型 整型&#xff1a;往往有符号和无符号的区分&#xff0c;&#xff08;signed&#xff09;有符号 &#xff08;unsigned)无符号 基础整型&#xff1…

GSM模块的使用及注意事项

1.如何使用&#xff1f; 最近&#xff0c;我准备使用GSM模块&#xff08;SIM900A&#xff09;发送英文短信到指定号码&#xff0c;翻阅资料如下&#xff1a; 可见&#xff0c;只要给该模块按照如下步骤发送指令&#xff1a; 就可以使得模块正常工作。&#xff08;SIM900A&#…

6-1 A. DS二叉树—二叉树构建与遍历(不含框架)

题目描述 给定一颗二叉树的逻辑结构如下图&#xff0c;&#xff08;先序遍历的结果&#xff0c;空树用字符‘#’表示&#xff0c;例如AB#C##D##&#xff09;&#xff0c;建立该二叉树的二叉链式存储结构&#xff0c;并输出该二叉树的先序遍历、中序遍历和后序遍历结果。 输入 第…

【Java程序设计】【C00231】基于Springboot的景区寄存管理系统(有论文)

基于Springboot的景区寄存管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的景区行李寄存系统 主要功能如下&#xff1a;用户登录模块、用户信息管理模块、角色信息管理模块、部门信息管理模块、行李寄存柜…

十、Qt三维图表

一、Data Visualization模块概述 Data Visualization的三维显示功能主要有三种三维图形来实现&#xff0c;三各类的父类都是QAbstract3DGraph&#xff0c;从QWindow继承而来。这三类分别是&#xff1a;三维柱状图Q3DBar三维空间散点Q3DScatter三维曲面Q3DSurface 1、相关类的…

窥探向量乘矩阵的存内计算原理—基于向量乘矩阵的存内计算

在当今计算领域中&#xff0c;存内计算技术凭借其出色的向量乘矩阵操作效能引起了广泛关注。本文将深入研究基于向量乘矩阵的存内计算原理&#xff0c;并探讨几个引人注目的代表性工作&#xff0c;如DPE、ISAAC、PRIME等&#xff0c;它们在神经网络和图计算应用中表现出色&…