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

PCIe总线-Linux内核PCIe软件框架分析(十一)

1.简介

Linux内核PCIe软件框架如下图所示,按照PCIe的模式,可分为RC和EP软件框架。RC的软件框架分为五层,第一层为RC Controller Driver,和RC Controller硬件直接交互,不同的RC Controller,其驱动实现也不相同;第二层为Core层,该层将Controller进行了抽象,提供了统一的接口和数据结构,将所有的Controller管理起来,同时提供通用PCIe设备驱动注册和匹配接口,完成驱动和设备的绑定,管理所有PCIe设备;第三层为PCIe设备驱动层,包含了Storage、Ethernet、PCI桥等设备驱动;第四层为设备驱动层,根据设备类型,可分为字符设备驱动、网络设备驱动和块设备驱动。第五层为虚拟文件系统层,该层会在用户空间创建设备节点,提供了应用程序访问PCIe设备的路径。EP的软件框架分为六层,第一层为EP Controller Driver,和RC Controller Driver的功能相似;第二层为EP Controller Core层,该层向下将EP Controller进行了抽象,提供了统一的接口和数据结构,将所有的EP Controller管理起来;第三层为EP Function Core,该层统一管理EPF驱动和EPF设备,并提供两者相互匹配的方法;第四层为EP Configfs,在用户空间提供了配置和绑定EPF的接口,用户可以通过这些接口配置EPF,而无需修改驱动;第五层为EP Function Driver,和PCIe设备的具体功能相关;第六层为虚拟文件系统层,和RC的功能相同(EP也有设备驱动层,篇幅所限,图中未画出)。

PCIe软件框架

2.RC软件框架

2.1.RC Controller Driver

RK3588 PCIe RC Controller Driver驱动定义如下所示。

MODULE_DEVICE_TABLE(of, rk_pcie_of_match);
static struct platform_driver rk_plat_pcie_driver = {.driver = {.name	= "rk-pcie",.of_match_table = rk_pcie_of_match,.suppress_bind_attrs = true,.pm = &rockchip_dw_pcie_pm_ops,},.probe = rk_pcie_probe,
};module_platform_driver(rk_plat_pcie_driver);

2.2.Core

2.2.1.Host Bridge

RC Core层使用struct pci_host_bridge数据结构描述Host Bridge。bus描述Root bus,其他bus都在该数据结构的链表中。opschild_ops描述Root bus和其他bus上的设备的配置空间访问方法。windows链表保存bus-range和ranges的资源。dma_ranges链表保存dma-ranges的资源。使用pci_alloc_host_bridgedevm_pci_alloc_host_bridge函数分配struct pci_host_bridge数据结构,使用pci_free_host_bridge释放struct pci_host_bridge数据结构。pci_host_probe枚举Host Bridge下面所有PCIe设备。

[include/linux/pci.h]
struct pci_host_bridge {struct device	dev;struct pci_bus	*bus;		/* Root bus */struct pci_ops	*ops;       /* Low-level architecture-dependent routines */struct pci_ops	*child_ops;void		*sysdata;int		busnr;struct list_head windows;	 /* resource_entry */struct list_head dma_ranges; /* dma ranges resource list */......
};
struct pci_host_bridge *pci_alloc_host_bridge(size_t priv);
struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev,size_t priv);
void pci_free_host_bridge(struct pci_host_bridge *bridge);
int pci_host_probe(struct pci_host_bridge *bridge);

struct pci_ops描述访问PCIe设备配置空间的方法,需要RC Controller Driver实现。常用的是map_busreadwritemap_bus用于映射访问配置空间的region,readwrite用于读写配置空间。

[include/linux/pci.h]
struct pci_ops {int (*add_bus)(struct pci_bus *bus);void (*remove_bus)(struct pci_bus *bus);void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where);int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
};
2.2.2.Bus

RC Core层使用struct pci_bus数据结构描述PCIe bus。所有PCIe bus组成一个PCIe树型结构。parent指向Parent buses,children指向Child buses。devices链表保存该bus上的所有设备。number为该bus的总线编号,primary表示上游总线编号,busn_res保存桥下游总线编号范围,max_bus_speed表示该bus支持的最大速度,cur_bus_speed表示该bus当前的速度。pci_find_bus根据PCIe域和总线编号查找struct pci_buspci_add_new_bus创建一个struct pci_bus并添加到父总线上,注册Host Bridge时会自动创建bus0的数据结构,pci_bus_insert_busn_respci_bus_update_busn_res_end更新PCIe bus编号资源。

[include/linux/pci.h]
struct pci_bus {struct list_head node;		/* Node in list of buses */struct pci_bus	*parent;	/* Parent bus this bridge is on */struct list_head children;	/* List of child buses */struct list_head devices;	/* List of devices on this bus */struct pci_dev	*self;		/* Bridge device as seen by parent */struct list_head slots;		/* List of slots on this bus;protected by pci_slot_mutex */struct resource *resource[PCI_BRIDGE_RESOURCE_NUM];struct list_head resources;	/* Address space routed to this bus */struct resource busn_res;	/* Bus numbers routed to this bus */struct pci_ops	*ops;		/* Configuration access functions */struct msi_controller *msi;	/* MSI controller */void		*sysdata;	/* Hook for sys-specific extension */struct proc_dir_entry *procdir;	/* Directory entry in /proc/bus/pci */unsigned char	number;		/* Bus number */unsigned char	primary;	/* Number of primary bridge */unsigned char	max_bus_speed;	/* enum pci_bus_speed */unsigned char	cur_bus_speed;	/* enum pci_bus_speed */......
};struct pci_bus *pci_find_bus(int domain, int busnr);
struct pci_bus *pci_add_new_bus(struct pci_bus *parent,struct pci_dev *dev, int busnr);
void pci_remove_bus(struct pci_bus *bus);int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int busmax);
int pci_bus_update_busn_res_end(struct pci_bus *b, int busmax);
2.2.3.Device

RC Core层使用struct pci_dev数据结构描述PCIe Devices。devfn表述device和function编号,vendordevice等保存PCIe设备配置空间头信息,driver指向该设备使用的驱动。resource保存设备的资源,如BAR、ROMs等。PCIe bus也是一个PCIe设备。pci_alloc_dev分配struct pci_dev数据结构,pci_dev_put释放struct pci_dev数据结构,pci_device_add向总线上添加PCIe设备。pci_bus_add_devicespci_bus_add_device匹配PCIe设备和PCIe驱动。

[include/linux/pci.h]
/* The pci_dev structure describes PCI devices */
struct pci_dev {struct list_head bus_list;	/* Node in per-bus list */struct pci_bus	*bus;		/* Bus this device is on */struct pci_bus	*subordinate;	/* Bus this device bridges to */void		*sysdata;	/* Hook for sys-specific extension */struct proc_dir_entry *procent;	/* Device entry in /proc/bus/pci */struct pci_slot	*slot;		/* Physical slot this device is in */unsigned int	devfn;		/* Encoded device & function index */unsigned short	vendor;unsigned short	device;unsigned short	subsystem_vendor;unsigned short	subsystem_device;unsigned int	class;		/* 3 bytes: (base,sub,prog-if) */......struct pci_driver *driver;	/* Driver bound to this device */......int		cfg_size;		/* Size of config space *//** Instead of touching interrupt line and base address registers* directly, use the values stored here. They might be different!*/unsigned int	irq;struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */bool		match_driver;		/* Skip attaching driver */......
};struct pci_dev *pci_alloc_dev(struct pci_bus *bus);
void pci_dev_put(struct pci_dev *dev);
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus);
void pci_bus_add_device(struct pci_dev *dev);
void pci_bus_add_devices(const struct pci_bus *bus);
2.2.4.Driver

RC Core层使用struct pci_driver数据结构描述PCIe设备驱动。PCIe设备和驱动匹配的信息保存到id_table中。pci_register_driver注册PCIe设备驱动,pci_unregister_driver注销PCIe设备驱动。

[include/linux/pci.h]
struct pci_driver {struct list_head	node;const char		*name;/* Must be non-NULL for probe to be called */const struct pci_device_id *id_table;/* New device inserted */int  (*probe)(struct pci_dev *dev, const struct pci_device_id *id);/* Device removed (NULL if not a hot-plug capable driver) */void (*remove)(struct pci_dev *dev);/* Device suspended */int  (*suspend)(struct pci_dev *dev, pm_message_t state);/* Device woken up */int  (*resume)(struct pci_dev *dev);void (*shutdown)(struct pci_dev *dev);/* On PF */int  (*sriov_configure)(struct pci_dev *dev, int num_vfs);/*  */const struct pci_error_handlers *err_handler;......
};
/* pci_register_driver() must be a macro so KBUILD_MODNAME can be expanded */
#define pci_register_driver(driver)		\__pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)void pci_unregister_driver(struct pci_driver *dev);

pci_bus_type用于匹配PCIe设备和驱动。

struct bus_type pci_bus_type = {.name		= "pci",.match		= pci_bus_match,.uevent		= pci_uevent,.probe		= pci_device_probe,.remove		= pci_device_remove,.shutdown	= pci_device_shutdown,.dev_groups	= pci_dev_groups,.bus_groups	= pci_bus_groups,.drv_groups	= pci_drv_groups,.pm		= PCI_PM_OPS_PTR,.num_vf		= pci_bus_num_vf,.dma_configure	= pci_dma_configure,
};
2.2.5.设备驱动

不同的PCIe设备,需要不同的PCIe设备驱动。下面列出PCIe桥和NVMe硬盘驱动。

2.2.5.1.桥驱动

如下所示,PCIe桥使用"pcieport"驱动。

[drivers/pci/pcie/portdrv_pci.c]
static const struct pci_device_id port_pci_ids[] = {/* handle any PCI-Express port */{ PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0) },/* subtractive decode PCI-to-PCI bridge, class type is 060401h */{ PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x01), ~0) },/* handle any Root Complex Event Collector */{ PCI_DEVICE_CLASS(((PCI_CLASS_SYSTEM_RCEC << 8) | 0x00), ~0) },{ },
};
static struct pci_driver pcie_portdriver = {.name		= "pcieport",.id_table	= &port_pci_ids[0],.probe		= pcie_portdrv_probe,.remove		= pcie_portdrv_remove,.shutdown	= pcie_portdrv_remove,.err_handler	= &pcie_portdrv_err_handler,.driver.pm	= PCIE_PORTDRV_PM_OPS,
};
static int __init pcie_portdrv_init(void)
{if (pcie_ports_disabled)return -EACCES;pcie_init_services();dmi_check_system(pcie_portdrv_dmi_table);return pci_register_driver(&pcie_portdriver);
}
device_initcall(pcie_portdrv_init);
2.2.5.2.NVMe驱动

M.2 NVMe硬盘使用下面的驱动。

[drivers/nvme/host/pci.c]
static struct pci_driver nvme_driver = {.name		= "nvme",.id_table	= nvme_id_table,.probe		= nvme_probe,.remove		= nvme_remove,.shutdown	= nvme_shutdown,
#ifdef CONFIG_PM_SLEEP.driver		= {.pm	= &nvme_dev_pm_ops,},
#endif.sriov_configure = pci_sriov_configure_simple,.err_handler	= &nvme_err_handler,
};static int __init nvme_init(void)
{BUILD_BUG_ON(sizeof(struct nvme_create_cq) != 64);BUILD_BUG_ON(sizeof(struct nvme_create_sq) != 64);BUILD_BUG_ON(sizeof(struct nvme_delete_queue) != 64);BUILD_BUG_ON(IRQ_AFFINITY_MAX_SETS < 2);return pci_register_driver(&nvme_driver);
}
module_init(nvme_init);
module_exit(nvme_exit);

3.EP软件框架

3.1.EP Controller Driver

RK3399 PCIe EP Controller Driverr驱动定义如下所示。

[drivers/pci/controller/pcie-rockchip-ep.c]
static const struct of_device_id rockchip_pcie_ep_of_match[] = {{ .compatible = "rockchip,rk3399-pcie-ep"},{},
};static struct platform_driver rockchip_pcie_ep_driver = {.driver = {.name = "rockchip-pcie-ep",.of_match_table = rockchip_pcie_ep_of_match,},.probe = rockchip_pcie_ep_probe,
};builtin_platform_driver(rockchip_pcie_ep_driver);

3.2.EP Controller Core

3.2.1.EPC Device

EP Controller Core层使用struct pci_epc描述PCIe Endpoint Controller Device。EPC的所有的functions都挂到pci_epf链表上,ops指向了EPC提供的回调函数集合,用于设置EPC的配置空间、设置region、设置和发送中断等,windows保存了EPC的Outbound的地址段,num_windows表示Outbound的地址段的数量,max_functions保存了functions的最大数量。使用pci_epc_createdevm_pci_epc_create函数创建struct pci_epcdevm_pci_epc_destroypci_epc_destroy销毁struct pci_epc

[include/linux/pci-epc.h]
/* struct pci_epc - represents the PCI EPC device */
struct pci_epc {struct device			dev;struct list_head		pci_epf;const struct pci_epc_ops	*ops;struct pci_epc_mem		**windows;struct pci_epc_mem		*mem;unsigned int			num_windows;u8				max_functions;struct config_group		*group;/* mutex to protect against concurrent access of EP controller */struct mutex			lock;unsigned long			function_num_map;struct atomic_notifier_head	notifier;
};#define pci_epc_create(dev, ops)    \__pci_epc_create((dev), (ops), THIS_MODULE)
#define devm_pci_epc_create(dev, ops)    \__devm_pci_epc_create((dev), (ops), THIS_MODULE)
void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc);
void pci_epc_destroy(struct pci_epc *epc);

struct pci_epc_ops如下图所示,这些回调函数很重要,EP Controller Driver必须实现。EPF驱动会调用这些函数配置EPC。

[include/linux/pci-epc.h]
struct pci_epc_ops {int	(*write_header)(struct pci_epc *epc, u8 func_no,struct pci_epf_header *hdr);int	(*set_bar)(struct pci_epc *epc, u8 func_no,struct pci_epf_bar *epf_bar);void	(*clear_bar)(struct pci_epc *epc, u8 func_no,struct pci_epf_bar *epf_bar);int	(*map_addr)(struct pci_epc *epc, u8 func_no,phys_addr_t addr, u64 pci_addr, size_t size);void	(*unmap_addr)(struct pci_epc *epc, u8 func_no,phys_addr_t addr);int	(*set_msi)(struct pci_epc *epc, u8 func_no, u8 interrupts);int	(*get_msi)(struct pci_epc *epc, u8 func_no);int	(*set_msix)(struct pci_epc *epc, u8 func_no, u16 interrupts,enum pci_barno, u32 offset);int	(*get_msix)(struct pci_epc *epc, u8 func_no);int	(*raise_irq)(struct pci_epc *epc, u8 func_no,enum pci_epc_irq_type type, u16 interrupt_num);int	(*start)(struct pci_epc *epc);void	(*stop)(struct pci_epc *epc);const struct pci_epc_features* (*get_features)(struct pci_epc *epc,u8 func_no);struct module *owner;
};
3.2.2.EPF绑定EPC

每个EP的Function都对应一个struct pci_epf设备,即EPF设备,EPF设备和EPC通过pci_epc_add_epf绑定,通过pci_epc_remove_epf解除绑定。

[include/linux/pci-epc.h]
int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf);
void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf);
3.2.3.EPC API

下面的接口是对struct pci_epc_ops封装,供EPF驱动调用。

[include/linux/pci-epc.h]
int pci_epc_write_header(struct pci_epc *epc, u8 func_no,struct pci_epf_header *hdr);
int pci_epc_set_bar(struct pci_epc *epc, u8 func_no,struct pci_epf_bar *epf_bar);
void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no,struct pci_epf_bar *epf_bar);
int pci_epc_map_addr(struct pci_epc *epc, u8 func_no,phys_addr_t phys_addr,u64 pci_addr, size_t size);
void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no,phys_addr_t phys_addr);
int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts);
int pci_epc_get_msi(struct pci_epc *epc, u8 func_no);
int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts,enum pci_barno, u32 offset);
int pci_epc_get_msix(struct pci_epc *epc, u8 func_no);
int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,enum pci_epc_irq_type type, u16 interrupt_num);
int pci_epc_start(struct pci_epc *epc);
void pci_epc_stop(struct pci_epc *epc);
const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,u8 func_no);

3.3.EP Function Core

EP Function Core层定义了EPF Driver和EPF Device的数据结构,并提供注册、创建及绑定接口。

3.3.1.EPF Driver

EPF Driver的数据结构为struct pci_epf_driver。当EPF Device和EPC Device绑定后,会回调ops函数以通知EPF Driver,id_table定义EPF Driver和EPF Device匹配的信息。pci_epf_register_driver注册EPF Driver,pci_epf_unregister_driver注销EPF Driver。

[include/linux/pci-epf.h]
struct pci_epf_driver {int	(*probe)(struct pci_epf *epf);int	(*remove)(struct pci_epf *epf);struct device_driver	driver;struct pci_epf_ops	*ops;struct module		*owner;struct list_head	epf_group;const struct pci_epf_device_id	*id_table;
};
struct pci_epf_ops {int	(*bind)(struct pci_epf *epf);void	(*unbind)(struct pci_epf *epf);
};#define pci_epf_register_driver(driver)    \__pci_epf_register_driver((driver), THIS_MODULE)
void pci_epf_unregister_driver(struct pci_epf_driver *driver);
3.3.2.EPF Device

每个EP Function都对应一个EPF Device。EPF Device的数据结构为struct pci_epf_driverheader保存了该EP Function配置空间头信息,bar[6]保存了6个BAR映射的物理地址,msi_interruptsmsix_interrupts分别表示EP Function需要的中断数量,func_no表述EP Function的编号。pci_epf_createpci_epf_destroy创建和销毁EPF Device。

[include/linux/pci-epf.h]
struct pci_epf {struct device		dev;const char		*name;struct pci_epf_header	*header;struct pci_epf_bar	bar[6];u8			msi_interrupts;u16			msix_interrupts;u8			func_no;struct pci_epc		*epc;struct pci_epf_driver	*driver;struct list_head	list;struct notifier_block   nb;/* mutex to protect against concurrent access of pci_epf_ops */struct mutex		lock;
};
struct pci_epf *pci_epf_create(const char *name);
void pci_epf_destroy(struct pci_epf *epf);
3.3.3.EPF Device匹配EPF Driver

pci_epf_bus_type用于匹配EPF Device和EPF Driver。

[drivers/pci/endpoint/pci-epf-core.c]
static struct bus_type pci_epf_bus_type = {.name		= "pci-epf",.match		= pci_epf_device_match,.probe		= pci_epf_device_probe,.remove		= pci_epf_device_remove,
};

3.4.EP Configfs

EP Configfs会在/sys目录下创建文件节点,使用者可以在用户空间通过这些文件节点,配置和创建EPP Device,绑定EPP Device、EPP Driver及EPC Device。

3.5.EP Function Driver。

下面是pci_epf_test的EP Function Driver。

[drivers/pci/endpoint/functions/pci-epf-test.c]
static struct pci_epf_ops ops = {.unbind	= pci_epf_test_unbind,.bind	= pci_epf_test_bind,
};static struct pci_epf_driver test_driver = {.driver.name	= "pci_epf_test",.probe		= pci_epf_test_probe,.id_table	= pci_epf_test_ids,.ops		= &ops,.owner		= THIS_MODULE,
};

参考资料

  1. PCIEXPRESS体系结构导读
  2. PCI Express technology 3.0
  3. PCI Express® Base Specification Revision 5.0 Version 1.0
  4. Rockchip RK3588 TRM
  5. Linux kernel 5.10

相关文章:

PCIe总线-Linux内核PCIe软件框架分析(十一)

1.简介 Linux内核PCIe软件框架如下图所示&#xff0c;按照PCIe的模式&#xff0c;可分为RC和EP软件框架。RC的软件框架分为五层&#xff0c;第一层为RC Controller Driver&#xff0c;和RC Controller硬件直接交互&#xff0c;不同的RC Controller&#xff0c;其驱动实现也不相…...

视觉SLAM第二讲

SLAM分为定位和建图两个问题。 定位问题 定位问题是通过传感器观测数据直接或间接求解位置和姿态。 通常可以分为两类&#xff1a;基于已知地图的定位和基于未知地图的定位。 基于已知地图的定位 利用预先构建的地图&#xff0c;结合传感器数据进行全局定位。SLAM中的全局…...

mysql1055报错解决方法

目录 一、mysql版本 二、 问题描述 三、解决方法 1.方法一&#xff08;临时&#xff09; 2.方法二&#xff08;永久&#xff09; 一、mysql版本 mysql版本&#xff1a;5.7.23 二、 问题描述 在查询时使用group by语句&#xff0c;出现错误代码&#xff1a;1055&#xf…...

Java的@DateTimeFormat注解与@JsonFormat注解的使用对比

Java的DateTimeFormat注解与JsonFormat注解的使用对比 在Java开发中&#xff0c;处理日期和时间格式时&#xff0c;我们经常会使用到DateTimeFormat和JsonFormat注解。这两个注解主要用于格式化日期和时间&#xff0c;但在使用场景和功能上有所不同。本文将详细介绍这两个注解…...

德国云手机:企业移动办公解决方案

在现代商业环境中&#xff0c;移动办公已经成为一种趋势。德国云手机作为一种高效的解决方案&#xff0c;为企业提供了强大的支持。本文将探讨德国云手机如何优化企业的移动办公环境。 一、德国云手机的主要优势 高灵活性 德国云手机具有高度的灵活性&#xff0c;能够根据用户需…...

【React】useState:状态管理的基石

文章目录 一、什么是 useState&#xff1f;二、useState 的基本用法三、useState 的工作原理四、高级用法五、最佳实践 在现代前端开发中&#xff0c;React 是一个非常流行的库&#xff0c;而 useState 是 React 中最重要的 Hook 之一。useState 使得函数组件能够拥有自己的状态…...

商品中心关于缓存热key的解决方案

缓存热key一旦被击穿&#xff0c;流量势必会打到数据库&#xff0c;如果数据库崩了&#xff0c;游戏直接结束。 从两点来讨论&#xff1a;如何监控、如何解决。 如何监控 通过业务评估&#xff1a;比如营销活动推出的商品或者热卖的商品。基于LRU的命令&#xff0c;redis-cl…...

【Python系列】Parquet 数据处理与合并:高效数据操作实践

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

大脑自组织神经网络通俗讲解

大脑自组织神经网络的核心概念 大脑自组织神经网络&#xff0c;是指大脑中的神经元通过自组织的方式形成复杂的网络结构&#xff0c;从而实现信息的处理和存储。这一过程涉及到神经元的生长、连接和重塑&#xff0c;是大脑学习和记忆的基础。其核心公式涉及神经网络的权重更新…...

org.springframework.context.annotation.DeferredImportSelector如何使用?

DeferredImportSelector 是 Spring 框架中一个比较高级的功能&#xff0c;主要用于在 Spring 应用上下文的配置阶段延迟导入某些组件或配置。这个功能特别有用&#xff0c;比如在处理依赖于其他自动配置的场景&#xff0c;或者当你想基于某些条件来决定是否导入特定的配置类时。…...

缓慢变化维

缓慢变化维 缓慢变化维&#xff08;Slowly Changing Dimensions&#xff0c;简称SCD&#xff09;是数据仓库中的一个重要概念&#xff0c;用于处理维度表中数据随时间发生的变化。以下是一个具体的例子来描述缓慢变化维&#xff1a; 假设我们有一个销售数据仓库&#xff0c;其…...

Vue常用的指令都有哪些?都有什么作用?什么是自定义指令?

常用指令&#xff1a; 1、v-model 多用于表单元素实现双向数据绑定 (同angular中的ng-model) 2、v-for格式&#xff1a; v-for"字段名in(of)数组json"循环数组或json(同angular中的ng repeat),需要注意从vue2开始取消了$index 3、v-show 4、v-hide 隐藏内容 (同a…...

kettle从入门到精通 第八十一课 ETL之kettle kettle中的json对象字段写入postgresql中的json字段正确姿势

1、上一节可讲解了如何将json数据写入pg数据库表中的json字段&#xff0c;虽然实现了效果&#xff0c;但若客户继续使用表输出步骤则仍然无法解决问题。 正确的的解决方式是设置数据库连接参数stringtypeunspecified 2、stringtypeunspecified 参数的作用&#xff1a; 当设置…...

计算机网络实验-RIP配置与分析

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、相关知识 路由信息协议&#xff08;Routing Information Protocol&#xff0c;RIP&#xff09;是一种基于距离向量&#xff08;Distance-Vector&…...

33.【C语言】实践扫雷游戏

预备知识&#xff1a; 第13篇 一维数组 第13.5篇 二维数组 第28篇 库函数 第29篇 自定义函数 第30篇 函数补充 0x1游戏的运行&#xff1a; 1.随机布置雷 2.排雷 基本规则&#xff1a; 点开一个格子后&#xff0c;显示1&#xff0c;对于9*9&#xff0c;代表以1为中心的去…...

git学习笔记(总结了常见命令与学习中遇到的问题和解决方法)

前言 最近学习完git&#xff0c;学习过程中也遇到了很多问题&#xff0c;这里给大家写一篇总结性的博客&#xff0c;主要大概讲述git命令和部分难点问题&#xff08;简单的知识点这里就不再重复讲解了&#xff09; 一.git概述 1.1什么是git Git是一个分布式的版本控制软件。…...

【计算机网络】TCP协议详解

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 1、引言2、udp和tcp协议的异同3、tcp服务器3.1、接口认识3.2、服务器设计 4、tcp客户端4.1、客户端设计4.2、说明 5、再研Tcp服务端5.1、多进程版5.2、多线程版 5、守护进程化5.1、什么是守护进程5.2…...

2.3 大模型硬件基础:AI芯片(上篇) —— 《带你自学大语言模型》系列

本系列目录 《带你自学大语言模型》系列部分目录及计划&#xff0c;完整版目录见&#xff1a;带你自学大语言模型系列 —— 前言 第一部分 走进大语言模型&#xff08;科普向&#xff09; 第一章 走进大语言模型 1.1 从图灵机到GPT&#xff0c;人工智能经历了什么&#xff1…...

Java | Leetcode Java题解之第279题完全平方数

题目&#xff1a; 题解&#xff1a; class Solution {public int numSquares(int n) {if (isPerfectSquare(n)) {return 1;}if (checkAnswer4(n)) {return 4;}for (int i 1; i * i < n; i) {int j n - i * i;if (isPerfectSquare(j)) {return 2;}}return 3;}// 判断是否为…...

JS逆向高级爬虫

JS逆向高级爬虫 JS逆向的目的是通过运行本地JS的文件或者代码,以实现脱离他的网站和浏览器,并且还能拿到和浏览器加密一样的效果。 10.1、编码算法 【1】摘要算法&#xff1a;一切从MD5开始 MD5是一个非常常见的摘要(hash)逻辑. 其特点就是小巧. 速度快. 极难被破解. 所以,…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...