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

news2024/12/23 17:44:37

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

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

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

相关文章

永结无间Ⅰ--基于 LLM 的 AGI

在过去几周&#xff0c;传奇人物 Francois Chollet 发起的ARC 挑战引起了不小的轰动。这项挑战让很多 AI 研究人员感到困惑&#xff0c;它表明了所有 AI 系统都存在泛化能力不足的问题。ARC 上上一次 SOTA AI 的准确率约为 34%&#xff0c;而在同一挑战中&#xff0c;Mechanica…

【React】Context机制跨层传递数据详解

文章目录 一、React Context的概念二、创建和使用Context1. 创建Context2. 使用Provider提供数据3. 使用Consumer获取数据 三、使用useContext Hook四、动态更新Context值五、Context的实际应用场景1. 主题切换2. 用户认证状态 六、注意事项 在开发React应用时&#xff0c;我们…

【C语言】深入探讨数组传参

一、数组传参简介 在C语言中&#xff0c;数组传参是一个常见的操作&#xff0c;尤其是在处理大量数据或需要多次访问相同数据集时。理解如何传递数组以及这些方法之间的差异是编写高效和安全代码的关键。在这篇博客中&#xff0c;我们将详细讨论C语言中数组传参的几种常见方法&…

【网络】应用层协议(自定义协议)(序列和反序列化)

应用层协议&#xff08;自定义协议&#xff09;&#xff08;序列和反序列化&#xff09; 一、引言--应用层的使用二、应用层1、网络版本计算器&#xff08;1&#xff09;协议定制和序列反序列化&#xff08;2&#xff09;网络版计算器协议定制i、封装有效载荷&#xff08;默认上…

数据结构——单链表OJ题(上)

目录 一、移除链表元素 1.思路 2.注意 3.解题 二、反转链表 思路1&#xff1a;三指针翻转法 &#xff08;1&#xff09;注意 &#xff08;2&#xff09;解题 思路2&#xff1a;头插法 &#xff08;1&#xff09;注意 &#xff08;2&#xff09;解题 三、链表的中间结…

depcheck 前端依赖检查

介绍 depcheck 是一款用于检测项目中 未使用依赖项 的工具。 depcheck 通过扫描项目文件&#xff0c;帮助你找出未被引用的依赖&#xff0c;从而优化项目。 优势&#xff1a; 简单易用: 仅需几个简单的命令&#xff0c;就能够扫描并列出未使用的依赖项&#xff0c;让你快速了…

The Schematic workflow failed. See above.

在使用 ng new 新建Angular项目的时候会报一个错误&#xff1a;The Schematic workflow failed. See above. 解决办法&#xff1a; 只需要在后面加上 --skip-install 参数&#xff0c;就不会报错了。 ng new myapp --skip-install

打工人电脑里都需要的远程控制软件有哪些?这4款不能错过

不巧前几天台风&#xff0c;实在没办法到公司&#xff0c;但是项目还得继续&#xff0c;这时候远程控制电脑的技巧可谓是帮了我大忙了。不知道平常的你偶尔会不会也需要远程控制电脑的操作&#xff0c;如果有就继续看下去吧。 1.向日魁远程控制 直通车>>https://down.o…

AJAX-Promise 详解

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 前言 一、Promise基本概念 1.1 定义 1.2 状态 1.3 构造函数 二、Promise基本用法 2.1 then() 2.2 ca…

ThinkPHP一对一关联模型的运用(ORM)

一、序言 最近在写ThinkPHP关联模型的时候一些用法总忘&#xff0c;我就想通过写博客的方式复习和整理下一些用法。 具体版本&#xff1a; topthink/framework&#xff1a;6.1.4topthink/think-orm&#xff1a;2.0.61 二、实例应用 1、一对一关联 1.1、我先设计了两张表&#x…

根据题意写出完整的css,html和js代码【购物车模块页面及功能实现】

🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 问题描述 根据题意写出完…

基于微信小程序+SpringBoot+Vue的社区超市管理系统(带1w+文档)

基于微信小程序SpringBootVue的社区超市管理系统(带1w文档) 基于微信小程序SpringBootVue的社区超市管理系统(带1w文档) 为了让商品信息的管理模式进行升级&#xff0c;也为了更好的维护商品信息&#xff0c;社区超市管理系统的开发运用就显得很有必要&#xff0c;因为它不仅可…

C# 植物大战僵尸

Winform 版本开发 高效率、流畅植物大战僵尸 git地址&#xff1a;冯腾飞/植物大战僵尸

go语言day19 使用git上传包文件到github Gin框架入门

git分布式版本控制系统_git切换head指针-CSDN博客 获取请求参数并和struct结构体绑定_哔哩哔哩_bilibili &#xff08;gin框架&#xff09; GO: 引入GIn框架_go 引入 gin-CSDN博客 使用git上传包文件 1&#xff09;创建一个github账户&#xff0c;进入Repositories个人仓…

我在百科荣创企业实践——简易函数信号发生器(6)

对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业的…

buu做题(8)

[安洵杯 2019]easy_web 查看源代码可以发现一长串的base64编码 就是页面上的一张图片 回到原页面,url上面也有一些奇怪的参数 经过两次base64和一次hex 解密后得到 555.png 应该就是包含着页面上的这张图片 然后尝试将index.php 按照这样的方式编码, 看看能不能包含到 TmprMl…

后端解决跨域(Cross-Origin Resource Sharing)(三种方式)

注解CrossOrigin 控制层的类上或者方法上加注解CrossOrigin 实现接口并重写方法 Configuration public class CorsConfig implements WebMvcConfigurer {Overridepublic void addCorsMappings(CorsRegistry registry) {// 设置允许跨域的路径registry.addMapping("/**&qu…

算法通关:006_4二分查找:寻找数组中的峰值

文章目录 描述主要代码全部代码运行结果总结 二分法不一定只能用在有序数组中。 描述 leetcode&#xff1a;162 主要代码 //二分法查找峰值public static int findPeakElement(int[] arr){if (arr.length 1){//randomArray()不会出现arr null的情况return 0;}//先检查 0…

LabVIEW操作系列1

系列文章目录 我的记录&#xff1a; LabVIEW操作系列 文章目录 系列文章目录前言五、特殊用法5.1 取值范围表示5.2 对输入值取值范围进行限定5.3 控制多个While循环停止运行。5.4 获取按钮上的文本5.5 获取按钮上的文本【进阶】 六、使用步骤1.引入库2.读入数据 七、其余功能7.…

二叉树以及堆的实现

树 树的定义及概念 树是⼀种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09; 个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像⼀棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有⼀个特殊的结点&#xff0c;称…