platform bus平台总线详解

news2024/12/23 20:38:18

往期内容

  1. 驱动中的device和device_driver结构体-CSDN博客
  2. bus总线的相关结构体和注册逻辑-CSDN博客
  3. bus中设备驱动的probe触发逻辑和device、driver的添加逻辑-CSDN博客

前言

注:以下的代码皆摘自于linux 4.9.88版本的内核源码,不同版本可能有所出入。

之前的章节驱动中的device和device_driver结构体-CSDN博客讲过:Linux驱动开发很少直接使用device和device_driver,因为内核在它们之上又封装了一层,如platform device。内核在设备模型的基础上(device和device_driver),对这些设备进行了更进一步的**封装**,抽象出paltform bus、platform device和platform driver,以便驱动开发人员可以方便的开发这类设备的驱动。

画板

Platform Bus,基于底层bus模块,抽象出一个虚拟的Platform bus,用于挂载Platform设备;

Platform Device,基于底层device模块,抽象出Platform Device,用于表示Platform设备;

Platform Driver,基于底层device_driver模块,抽象出Platform Driver,用于驱动Platform设备。

1. struct platform_device

/*include/linux/platform_device.h*/
struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;
	struct resource	*resource;

	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};
  1. const char *name:
    • 设备的名称。这个名称在设备注册时会被复制到其 struct device 中的 init_name 字段。它用于唯一标识设备,并且在设备和驱动的匹配过程中扮演着关键角色。多个设备可以具有相同的名称,但内核会利用其他属性(如 ID)来区分它们。
  2. **int id:
    • 设备的唯一标识符。它用于区分同一总线上的不同设备。在驱动的 probe 函数中,通过 ID 可以判断设备的具体信息,尤其是在多个同名设备的情况下。
    • 内核允许存在多个名称相同的设备。而设备驱动的probe,依赖于名称,Linux采取的策略是:在bus的设备链表中查找device,和对应的device_driver比对name,如果相同,则查看该设备是否已经绑定了driver(查看其dev->driver指针是否为空),如果已绑定,则不会执行probe动作,如果没有绑定,则以该device的指针为参数,调用driver的probe接口。因此,在driver的probe接口中,通过判断设备的ID,可以知道此次驱动的设备是哪个。
  3. bool id_auto:
    • 指示该设备的 ID 是否由内核自动分配。如果为 true,则在设备注册时无需手动指定 ID,内核会为其分配一个唯一的 ID。这使得开发者在注册设备时更为方便。
  4. struct device dev:
    • 这是一个通用的设备结构,包含了设备的所有基础属性和操作方法。platform_device 是一个特殊的设备,因此它继承了 struct device 的所有功能。这个字段用于与内核的设备模型集成。
  5. u32 num_resources:
    • 设备使用的资源数量。这个字段指明了设备在使用的资源(如中断、内存地址等)的个数。
  6. struct resource *resource:
    • 指向设备使用的资源的指针。资源由 struct resource 描述,它抽象了设备所需的硬件资源,如 I/O 地址、内存地址、IRQ 号等。通过这个结构,内核可以管理这些资源的分配和释放,确保没有两个设备同时占用同一资源。
    • 在Linux中,系统资源包括I/O、Memory、Register、IRQ、DMA、Bus等多种类型。这些资源大多具有独占性,不允许多个设备同时使用,因此Linux内核提供了一些API,用于分配、管理这些资源。当某个设备需要使用某些资源时,只需利用struct resource组织这些资源(如名称、类型、起始、结束地址等),并保存在该设备的resource指针中即可。然后在设备probe时,设备需求会调用资源管理接口,分配、使用这些资源。而内核的资源管理逻辑,可以判断这些资源是否已被使用、是否可被使用等等。
  7. const struct platform_device_id *id_entry:
    • 指向与该平台设备相关的设备 ID 匹配表。该表定义了哪些驱动程序可以与此设备匹配。驱动程序在加载时会检查其 ID 列表,以便找到适合的设备。
  8. char *driver_override:
    • 用于强制设备与特定驱动匹配的驱动名称。如果某个设备需要特定的驱动而不想由内核自动选择,开发者可以在此字段中设置驱动名称。
  9. struct mfd_cell *mfd_cell:
    • 指向多功能设备(MFD)单元的指针。MFD 设备允许多个子设备通过同一个驱动进行管理,适用于复杂的硬件结构。
  10. struct pdev_archdata archdata:
    • 存储架构特定的附加数据。由于不同的硬件平台有不同的需求和实现,该字段允许每种架构定义自己特有的数据结构和成员。虽然这个设计可能显得灵活,但也让代码的可维护性和清晰性受到了挑战。

struct platform_device 结构体是 Linux 内核设备模型中处理平台设备的核心数据结构。它不仅存储了设备的基本信息,如名称和 ID,还管理了设备所需的硬件资源,并提供了与驱动程序匹配的必要信息。通过这个结构,内核能够有效地管理和控制平台设备的生命周期和资源使用。

2. struct platform_driver

struct platform_driver {
	int (*probe)(struct platform_device *);
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};
  1. int (*probe)(struct platform_device *);:
    • 指向一个函数,该函数在驱动程序与设备匹配时被调用。probe 函数的主要作用是初始化设备、分配资源、注册字符设备等。它是设备驱动的入口点,执行设备的设置和配置操作。
  2. int (*remove)(struct platform_device *);:
    • 指向一个函数,该函数在设备被移除时被调用。通常,这个函数负责释放 probe 中分配的资源,注销字符设备等。它确保在设备被卸载时正确清理和释放所有相关资源。
  3. void (*shutdown)(struct platform_device *);:
    • 指向一个函数,在系统关闭或设备驱动被卸载时调用。这个函数通常用于保存设备状态和进行最后的清理,以确保设备可以安全关闭。
  4. int (*suspend)(struct platform_device *, pm_message_t state);:
    • 指向一个函数,在设备进入低功耗状态时被调用。该函数会处理设备的挂起逻辑,如关闭某些功能、保存设备状态等。pm_message_t 参数用于指示挂起的类型(如休眠、待机等)。
  5. **int (*resume)(struct platform_device *);**:
    • 指向一个函数,在设备从挂起状态恢复时调用。该函数负责重新初始化设备并恢复其之前的状态,以便设备可以继续正常操作。
  6. struct device_driver driver;:
    • 这是一个基础的设备驱动结构,包含了设备驱动的通用属性和功能。通过这个字段,platform_driver 继承了设备驱动的基本功能,包括驱动的名称、所属总线等。
  7. const struct platform_device_id *id_table;:
    • 指向与该平台驱动匹配的设备 ID 表。它的功能与 of_match_tableacpi_match_table 类似,为驱动提供其他匹配设备的方式。通过这个表,内核可以根据设备 ID 来判断驱动是否适用于特定设备,而不仅仅依赖于设备名称。
  8. bool prevent_deferred_probe;:
    • 这个字段用于控制是否阻止延迟探测。延迟探测是指在设备驱动无法立即匹配设备时,内核将其推迟到稍后的时间来进行匹配。如果将此字段设置为 true,则驱动在匹配时会立即进行,而不会被延迟处理。

struct platform_driver 提供了一种描述平台设备驱动的机制,包含了多种操作的回调函数,以支持设备的生命周期管理,如初始化、移除、挂起和恢复等。此外,它通过 id_table 提供了更灵活的设备匹配方式,允许驱动在不同的条件下识别和管理多个设备。这使得平台驱动可以更有效地与硬件交互,提供稳定和高效的设备支持。

3. API

3.1 Platform Device

Platform Device主要提供设备的分配、注册等接口,供其它driver使用。在 Linux 内核中,platform_device 代表平台设备,include/linux/platform_device.h 中提供了一系列 API 来注册、管理和使用这些设备。具体包括:

1. 设备注册与注销

int platform_device_register(struct platform_device *);
● 功能: 注册一个平台设备,使其在内核中可用。
● 参数: struct platform_device *pdev,指向要注册的设备结构体。
● 返回值: 返回 0 表示成功,负值表示错误。

void platform_device_unregister(struct platform_device *);
● 功能: 注销一个已注册的平台设备。
● 参数: struct platform_device *pdev,指向要注销的设备结构体。
● 注意: 注销后设备将不再可用。

2. 设备资源和 IRQ 管理

struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
● 功能: 获取与设备关联的资源。
● 参数:struct platform_device *pdev:目标设备。
  ○ unsigned int type:资源类型(如 I/O、内存等)。
  ○ unsigned int num:资源的索引。
● 返回值: 返回指向资源结构的指针,或返回 NULL 如果没有找到。

int platform_get_irq(struct platform_device *, unsigned int);
● 功能: 获取与设备关联的 IRQ。
● 参数:struct platform_device *pdev:目标设备。
  ○ unsigned int num:IRQ 的索引。
● 返回值: 返回 IRQ 号,或负值表示错误。

struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, const char *);
● 功能: 根据资源名称获取资源。
● 参数:struct platform_device *pdev:目标设备。
  ○ unsigned int type:资源类型。
  ○ const char *name:资源名称。
● 返回值: 返回指向资源结构的指针,或返回 NULL 如果没有找到。

int platform_get_irq_byname(struct platform_device *, const char *);
● 功能: 根据名称获取 IRQ。
● 参数:struct platform_device *pdev:目标设备。
  ○ const char *name:IRQ 名称。
● 返回值: 返回 IRQ 号,或负值表示错误。

3. 设备数组管理

int platform_add_devices(struct platform_device **, int);
● 功能: 注册一个平台设备数组。
● 参数:struct platform_device **pdevs:指向设备指针数组。
  ○ int count:数组中的设备数量。
● 返回值: 返回 0 表示成功,负值表示错误。

4. 设备注册的简化接口

struct platform_device *platform_device_register_full(const struct platform_device_info *pdevinfo);
● 功能: 完整注册平台设备,使用 platform_device_info 结构提供的详细信息。
● 参数: const struct platform_device_info *pdevinfo:设备信息结构体。
● 返回值: 返回指向已注册设备的指针,或返回 NULL 如果失败。

struct platform_device *platform_device_register_resndata(struct device *parent, const char *name, int id, const struct resource *res, unsigned int num, const void *data, size_t size);
● 功能: 注册一个带资源和数据的平台设备。
● 参数:struct device *parent:设备父级。
  ○ const char *name:设备名称。
  ○ int id:设备 ID。
  ○ const struct resource *res:资源数组。
  ○ unsigned int num:资源数量。
  ○ const void *data:附加数据。
  ○ size_t size:数据大小。
● 返回值: 返回指向已注册设备的指针,或返回 NULL 如果失败。

struct platform_device *platform_device_register_simple(const char *name, int id, const struct resource *res, unsigned int num);
● 功能: 简单注册一个平台设备,提供名称、ID 和资源。
● 参数:const char *name:设备名称。
  ○ int id:设备 ID。
  ○ const struct resource *res:资源数组。
  ○ unsigned int num:资源数量。
● 返回值: 返回指向已注册设备的指针,或返回 NULL 如果失败。

struct platform_device *platform_device_register_data(struct device *parent, const char *name, int id, const void *data, size_t size);
● 功能: 注册一个带数据的平台设备。
● 参数:struct device *parent:设备父级。
  ○ const char *name:设备名称。
  ○ int id:设备 ID。
  ○ const void *data:附加数据。
  ○ size_t size:数据大小。
● 返回值: 返回指向已注册设备的指针,或返回 NULL 如果失败。

5. 设备内存和资源管理

struct platform_device *platform_device_alloc(const char *name, int id);
● 功能: 分配内存用于一个平台设备,但不注册。
● 参数:const char *name:设备名称。
  ○ int id:设备 ID。
● 返回值: 返回指向分配的设备的指针,或返回 NULL 如果失败。

int platform_device_add_resources(struct platform_device *pdev, const struct resource *res, unsigned int num);
● 功能: 向平台设备添加资源。
● 参数:struct platform_device *pdev:目标设备。
  ○ const struct resource *res:资源数组。
  ○ unsigned int num:资源数量。
● 返回值: 返回 0 表示成功,负值表示错误。

int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size);
● 功能: 向平台设备添加附加数据。
● 参数:struct platform_device *pdev:目标设备。
  ○ const void *data:附加数据。
  ○ size_t size:数据大小。
● 返回值: 返回 0 表示成功,负值表示错误。

int platform_device_add(struct platform_device *pdev);
● 功能: 注册一个已分配的设备。
● 参数: struct platform_device *pdev:目标设备。
● 返回值: 返回 0 表示成功,负值表示错误。

void platform_device_del(struct platform_device *pdev);
● 功能: 删除并注销设备,但不释放内存。
● 参数: struct platform_device *pdev:目标设备。

void platform_device_put(struct platform_device *pdev);
● 功能: 释放对设备的引用。 
● 参数: struct platform_device *pdev:目标设备。

3.2 Platform Driver

在 Linux 内核中,platform_driver 结构体与 platform_device 结构体配合使用,提供了对平台设备驱动的管理和操作。这部分代码主要定义了与平台驱动相关的 API。以下是对这些 API 的详细说明:

1. 驱动注册与注销

int platform_driver_register(struct platform_driver *);
● 功能: 注册一个平台驱动,使其在内核中可用。
● 参数:struct platform_driver *driver:指向要注册的驱动结构体。
● 返回值: 返回 0 表示成功,负值表示错误。成功注册后,内核会在适当的时候调用该驱动的 probe 函数。

void platform_driver_unregister(struct platform_driver *);
● 功能: 注销一个已注册的平台驱动。
● 参数:struct platform_driver *driver:指向要注销的驱动结构体。
● 注意: 注销后驱动将不再可用,内核会停止调用该驱动的相关回调函数。

2. 驱动探测

int platform_driver_probe(struct platform_driver *driver, int (*probe)(struct platform_device *));
● 功能: 手动执行驱动的 probe 函数。
● 参数:struct platform_driver *driver:指向要探测的驱动结构体。
  ○ int (*probe)(struct platform_device *):指向 probe 函数的指针,probe 函数的作用是初始化设备。
● 返回值: 返回 0 表示成功,负值表示错误。

3. 驱动数据管理

static inline void *platform_get_drvdata(const struct platform_device *pdev);
● 功能: 获取与平台设备关联的驱动数据。
● 参数:const struct platform_device *pdev:指向目标设备的指针。
● 返回值: 返回指向设备关联数据的指针。如果没有数据,则返回 NULLstatic inline void platform_set_drvdata(struct platform_device *pdev, void *data);
● 功能: 设置与平台设备关联的驱动数据。
● 参数:struct platform_device *pdev:指向目标设备的指针。
  ○ void *data:要与设备关联的数据指针。
● 注意: 通过 set_drvdata 函数,驱动可以存储与设备相关的上下文数据,以便后续在 probe、remove 或其他回调函数中使用。

这些 API 提供了对平台驱动的管理和操作,包括驱动的注册、注销、探测以及与设备关联的数据的设置和获取。通过这些函数,开发者能够有效地控制平台驱动的生命周期和与之相关的状态信息,增强了内核对硬件设备的支持能力。

4. platform device和platform driver的注册

在 Linux 内核中,platform_deviceplatform_driver 的注册过程是通过 platform_device_addplatform_driver_register 这两个接口实现的。以下是对这两个接口的详细内部动作的解析。

4.1 platform_device_add 的内部动作

Linux-4.9.88\drivers\base\platform.c:
**
 * platform_device_add - add a platform device to device hierarchy
 * @pdev: platform device we're adding
 *
 * This is part 2 of platform_device_register(), though may be called
 * separately _iff_ pdev was allocated by platform_device_alloc().
 */
int platform_device_add(struct platform_device *pdev)
{
	int i, ret;

	if (!pdev)
		return -EINVAL;

	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;

	pdev->dev.bus = &platform_bus_type;

	switch (pdev->id) {
	default:
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
		break;
	case PLATFORM_DEVID_NONE:
		dev_set_name(&pdev->dev, "%s", pdev->name);
		break;
	case PLATFORM_DEVID_AUTO:
		/*
		 * Automatically allocated device ID. We mark it as such so
		 * that we remember it must be freed, and we append a suffix
		 * to avoid namespace collision with explicit IDs.
		 */
		ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
		if (ret < 0)
			goto err_out;
		pdev->id = ret;
		pdev->id_auto = true;
		dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
		break;
	}

	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];

		if (r->name == NULL)
			r->name = dev_name(&pdev->dev);

		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}

		if (p && insert_resource(p, r)) {
			dev_err(&pdev->dev, "failed to claim resource %d\n", i);
			ret = -EBUSY;
			goto failed;
		}
	}

	pr_debug("Registering platform device '%s'. Parent at %s\n",
		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));

	ret = device_add(&pdev->dev);
	if (ret == 0)
		return ret;

 failed:
	if (pdev->id_auto) {
		ida_simple_remove(&platform_devid_ida, pdev->id);
		pdev->id = PLATFORM_DEVID_AUTO;
	}

	while (--i >= 0) {
		struct resource *r = &pdev->resource[i];
		if (r->parent)
			release_resource(r);
	}

 err_out:
	return ret;
}
  1. 父设备设置:
    • 如果设备没有指定父设备,则将其父设备设置为 platform_bus,即 /sys/devices/platform/ 代表的设备。
    • 这意味着设备的 sysfs 目录会被创建为 /sys/devices/platform/xxx_device
  2. 设置设备总线:
    • 将设备的 bus 指针指定为 platform_bus_type
pdev->dev.bus = &platform_bus_type;
  1. 名称和 ID 处理:
    • 根据设备的 ID 修改或设置设备的名称。对于多个同名的设备,通过 ID 区分设备。在这里,将实际名称修改为 name.id 的形式,例如:
spi_device1
  1. 资源管理:
    • 调用 resource 模块的 insert_resource 接口,将该设备需要使用的资源统一管理起来。此时,资源模块会知道该设备所需的资源,这样它就可以进行相应的管理。
  2. 添加设备到内核:
    • 调用 device_add 接口,将内嵌的 struct device 变量添加到内核中,使设备在内核中可用。

4.2 platform_driver_register 的内部动作

Linux-4.9.88\include\linux\platform_device.h
#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)

/**
 * __platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 * @owner: owning module/driver
 */
int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	drv->driver.probe = platform_drv_probe;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}
  1. 设置驱动总线:
    • 将驱动的 bus 指针指定为 platform_bus_type
drv->driver.bus = &platform_bus_type;
  1. 回调函数指针设置:
    • 如果该平台驱动提供了 proberemoveshutdown 等回调函数,则将它内嵌的 struct driver 变量的这些指针设置为平台模块提供的函数,例如:
      • probe 函数指针设置为 platform_drv_probe
      • remove 函数指针设置为 platform_drv_remove
      • shutdown 函数指针设置为 platform_drv_shutdown
    • 通过这种方式,probe 等动作将从 struct driver 变量开始,经过 platform_drv_xxx 等接口的转接,最终调用到平台驱动自身的回调函数中。
  2. 驱动注册到内核:
    • 调用 driver_register 接口,将内嵌的 struct driver 变量添加到内核中,使驱动在内核中可用。

诶?最后不还是调用到了driver_register,而driver_register最后还是会去调用bus_add_driver函数,所以说这玩意其实就是嵌套的。

5. 平台设备的probe

在Bus中讲过设备驱动的probe的触发逻辑,其实在platform中也差不多,注册添加platform_driver时会触发probe,只不过包装了一下而已

bus中设备驱动的probe触发逻辑和device、driver的添加逻辑-CSDN博客

6. 联系

drivers/base/platform.c、dd.c

其实看一个图,兴许就能明白bus、driver/device、platform_deivce/platform_driver之间的关系:

画板

可以很清楚的看出,最后时调用了driver_attach操作,去进行遍历是否有匹配的device(主要依靠下面的_driver_attach,后面会讲到):

如果找到了最后肯定会调用到driver中的probe函数,其probe函数也在__driver_attach中体现:

最后就调用了自己驱动定义的probe函数。

7. 匹配规则

drivers/base/platform.c、dd.c

最后简单讲一下platform_device和platform_driver之间的匹配方式:

下面该函数在上面也讲到了,会去遍历是否有匹配的device,匹配就调用参四__driver_attach函数,里面会去调用bus_probe_device函数去调用驱动程序的probe

dd.c

匹配主要发生在__driver_attach中:

最后通过这张图就可以简单了解到匹配的四种方式

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

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

相关文章

【EXCEL数据处理】保姆级教程 000016案例 EXCEL的vlookup函数。

【EXCEL数据处理】000016案例 vlookup函数。 前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【EXCEL数据处理】保姆级教…

【动态规划】斐波那契模型 dp

动态规划的步骤&#xff1a; 状态表示。所谓状态表示就是 dp 表里的值表示什么含义&#xff0c;那么状态表示怎么找呢&#xff1f; a. 题目要求 b. 经验&#xff08;以某一个位置为结尾 / 起点&#xff09; 题目要求 c. 分析问题的过程中发现重复子问题状态转移方程。dp[ i ] …

ChatGPT Canvas:系统提示词泄漏了~

OpenAI 推出了一款叫做 Canvas 的新工具&#xff0c;用来帮助用户更好地与 ChatGPT 协作写作和编程。详细介绍可以看这篇文章&#xff1a;ChatGPT Canvas&#xff1a;交互式对话编辑器-CSDN博客​编辑 以下是 OpenAI 新功能 “Canvas” 的系统提示内容 你是 ChatGPT&#xff0…

基于连续小波变换(CWT)批量生成一维信号的时频图 最终生成30张时频图。生成的图像可用于后续的深度学习分类或其他处理。附详细的说明文档。

Matlab基于连续小波变换&#xff08;CWT&#xff09;&#xff0c;将一维信号批量生成时频图的源代码。此示例中&#xff0c;原始信号data是30*1280的格式&#xff0c;一共30条信号&#xff0c;信号长度为1280。最终生成30张时频图。生成的图像可用于后续的深度学习分类或其他处…

SpringBoot开发——SpringSecurity安全框架17个业务场景案例(二)

文章目录 一、Spring Security 常用应用场景介绍二、Spring Security场景案例6、CSRF 保护(CSRF Protection)6.1 Spring Security 配置6.2 业务逻辑代码7、密码编码(Password Encoding)7.1 Spring Security 配置7.2 业务逻辑代码7.3 控制器8、方法级安全性(Method Securit…

Vue2电商项目(八) 完结撒花:图片懒加载、路由懒加载、打包的map文件

一、图片懒加载 安装&#xff1a;npm i vue-lazyload1.3 -s &#xff08;弹幕建议按1.3版本&#xff09; 引入 // 引入懒加载的图片 import hlw from /assets/hulu.jpg // 引入插件 import VueLazyload from vue-lazyload // 引入插件 Vue.use(VueLazyload, {// 懒加载默认的图…

Oracle登录报错-ORA-01017: invalid username/password;logon denied

接上文&#xff1a;Oracle创建用户报错-ORA-65096: invalid common user or role name 我以为 按照上文在PDB里创建了用户&#xff0c;我以为就可以用PLSQL远程连接了&#xff0c;远程服务器上也安装了对应版本的Oracle客户端&#xff0c;但是我想多了&#xff0c;客户只是新建…

【爬虫】网站反debugger、内存爆破以及网站限制开发者工具

【爬虫】网站反debugger、内存爆破以及网站直接限制开发者工具 声明&#xff1a;本文中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0…

JWT集成Keycloak

一、直接使用现有域账号、密码获取token方式 1.KeyClack 使用现有配置 Client id : account-console 2.服务配置文件配置 3.API接口配置 4. 获取token 5.调用方式&#xff08;Swagger&#xff09;(代码方式直接在请求头加上token) 5.1 配置在Swagger访问 5.2 访问需要认证的接…

JavaWeb——Vue路由(概述、介绍、使用、解决bug)

目录 概述 介绍 使用 解决bug 概述 员工管理的页面已经制作完成。其他页面的制作方式一致。 项目中准备了部门管理的页面组件 DeptView &#xff0c;这样就有了员工管理和部门管理两个页面组件。 在系统中&#xff0c;要实现点击部门管理菜单展示部门管理组件&#xff0c…

为什么Java不支持多重继承?

不支持多重继承主要是因为会产生“菱形继承”&#xff0c;也称为钻石继承的问题。 那什么是菱形继承呢&#xff1f; 它涉及到一个类继承两个父类&#xff0c;而这两个父类又继承自同一个祖先类。这种结构在没有适当处理的情况下&#xff0c;会导致继承层次中的歧义和冗余。 如…

关于BSV区块链覆盖网络的常见问题解答(下篇)

​​发表时间&#xff1a;2024年9月20日 在BSV区块链上的覆盖网络服务为寻求可扩展、安全、高效交易处理解决方案的开发者和企业家开辟了新的视野。 作为开创性的曼达拉升级的一部分&#xff0c;覆盖网络服务提供了一个强大的框架&#xff0c;用于管理特定类型的交易和数据访问…

【目标检测】室内地板砖铺设缺陷检测数据集2000张VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2002 标注数量(xml文件个数)&#xff1a;2002 标注数量(txt文件个数)&#xff1a;2002 标注…

图论大总结

图论基础 98. 所有可达路径 result [] path [] def dfs(graph,x,n):if x n:result.append(path[:])returnfor i in range(1,n1):if graph[x][i] 1:path.append(i)dfs(graph,i,n)path.pop() def main():n,m map(int,input().split())# 邻接矩阵graph [[0]*(n1) for _ in …

【EXCEL数据处理】000021 案例 保姆级教程,附多个操作案例。EXCEL文档安全性设置。

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【EXCEL数据处理】000021 案例 保姆级教程&#xff0c;附多个操作案例。…

C#高级编程笔记--字符串和正则表达式

本章的主要内容如下&#xff1a; 创建字符串 格式化表达式 正则表达式​​​​​​​ 1.1 System.String类 System.String是一个类&#xff0c;专门用于存储字符串&#xff0c;允许对字符串进行许多操作。由于这种数据类型非常重要&#xff0c;C#提供了它自己…

【C++堆(优先队列)】2233. K 次增加后的最大乘积|1685

本文涉及知识点 C堆(优先队列) 贪心&#xff08;决策包容性) LeetCode 2233. K 次增加后的最大乘积 给你一个非负整数数组 nums 和一个整数 k 。每次操作&#xff0c;你可以选择 nums 中 任一 元素并将它 增加 1 。 请你返回 至多 k 次操作后&#xff0c;能得到的 nums的 最…

用Python实现运筹学——Day 15: 线性规划的项目实战

一、学习内容 在本节中&#xff0c;我们将综合应用之前学习的线性规划知识&#xff0c;解决一个复杂的实际问题。通过这个实战项目&#xff0c;进一步理解线性规划在资源分配问题中的应用。 二、实战案例&#xff1a;公司资源分配问题 2.1 问题描述&#xff1a; 某公司生产两…

Conda答疑

文章目录 优雅的使用Conda管理python环境1. conda info -e 和conda env list区别2.conda创建环境 创建的新环境在哪个文件夹下3. 自定义路径4. anaconda 新建环境 包是来自哪里4.1. 默认 Anaconda 仓库4.2. Conda-Forge4.3. 镜像源4.4. 自定义频道4.5. 总结 5. conda config --…

Spring Boot:打造下一代医院管理系统

3系统分析 3.1可行性分析 通过对本医院管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本医院管理系统采用JAVA作为开发语言&#xff0c;Spring Boot框…