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

平台总线---深入分析

阅读引言:本文会从平台总线的介绍,注册平台设备和驱动, 源码分析, 总结五个部分展开, 源码分析platform放在了最后面。

目录

一、平台总线介绍

二、平台总线如何使用

三、平台总线是如何工作的

四、注册platform设备

五、注册platform驱动

六、编写probe函数

七、平台总线源码分析


一、平台总线介绍

        平台总线模型也叫platform总线模型。平台总线是Linux系统虚拟出来的总线。所谓虚拟出来的总线其实就是使用软件来模拟物理总线的一些性质, linux各种子系统还是别的, 都特别爱玩匹配这套东西, 挺好。

二、平台总线如何使用

        平台总线模型将一个驱动分成了俩个部分,分别是device.c和driver.c,用来描述device.c硬件,driver. c 用来控制硬件。这样做的目的主要是为了提高代码的重复利用性, 降低耦合度。

三、平台总线是如何工作的

        平台总线通过字符串比较,将name相同的device.c和driver.c匹配到一起来控制硬件。平台总线原则:先分离,后搭档。

        note:源码分析这个位置放在后面些

四、注册platform设备

        platform设备驱动(device.c)里面写的是硬件资源。这里的硬件资源指的是寄存器地址,中断号以及其他硬件资源等。在linux内核里面用struct platform_device结构体来描述硬件资源。这个结构体定义在include/linux/platform_device.h文件当中,结构体原型如下:

struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u64		platform_dma_mask;
	struct device_dma_parameters dma_parms;
	u32		num_resources;
	struct resource	*resource;

	const struct platform_device_id	*id_entry;
	/*
	 * Driver name to force a match.  Do not set directly, because core
	 * frees it.  Use driver_set_override() to set or clear it.
	 */
	const char *driver_override;

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

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

        name可以匹配, 也可以在sys/bus/devices下展示。

/*
 * Resources are tree-like, allowing
 * nesting etc..
 */
struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	unsigned long desc;
	struct resource *parent, *sibling, *child;
};

资源的种类:ioport.h文件中

使用struct resource描述硬件资源信息示例:

struct resource my_dev_resource[] = {
    [0] = {.start = 0x1000, .end = 0x1000, .flags = IORESOURCE_MEM},
    [1] = {.start = 13, .end = 13, .flags = IORESOURCE_IRQ}
};

在my_dev_resource这个结构体数字中包含了俩组资源,第一组资源的类型是IORESOURCE_MEM,表示这组资源是一组内存类型的资源。起始地址是0xFDD60000,终止地址是0xFDD60004。第二组资源的类型是IORESOURCE_IRQ,表示这是一组中断资源, 中断很号是13。

platform设备加载和卸载函数

  1. platform设备加载函数: 函数原型:int platform_device_register(struct platform_device *device) 函数作用:加载platform设备。

  2. platform设备卸载函数: 函数原型:void platform_device_unregister(struct platform_device *device) 函数作用:卸载platform设备。

代码结构:

void my_dev_release(struct device *dev)
{
    printk("%s is called",__func__);
}


struct resource my_dev_resource[] = {
    [0] = {.start = 0xFDD60000, .end = 0xFDD60004, .flags = IORESOURCE_MEM},
    [1] = {.start = 13, .end = 13, .flags = IORESOURCE_IRQ}
};

struct platform_device my_dev = {
    .name = "mydev",
    .id = -1,
    .resource = my_dev_resource,
    .num_resources = ARRAY_SIZE(my_dev_resource),
    .dev = {
        .release = my_dev_release
    },
};

int __init my_dev_init(void)
{
    platform_device_register(&my_dev);
}

void __exit my_dev_exit(void)
{
    platform_device_unregister(&my_dev);
}

五、注册platform驱动

        关键数据结构, platform设备驱动(driver.c)里面写的软件驱动。在driver.c文件中首先需要定义一个platform_driver结构体。然后去实现这个结构体中的各个成员变量。当driver.c和device.c匹配成功以后,会执行driver.c里面的probe函数。platform_driver这个结构体定义在include/linux/platform_device.h文件当中,结构体原型如下:

          匹配过程和调用驱动中的probe函数后面会再说。

platform设备加载和卸载函数
1.platform设备加载函数:
        函数原型:intIlatform_driver_registepr(struct platform_driver *driver)
        函数作用:加载platform设备。
2. platform设备卸载函数:
        函数原型:void platform_driver_register(struct platform_driver *driver)
        函数作用:卸载platform设备。

int my_probe(struct platform_device *)
{

}

int my_remove(struct platform_device *)
{
    
}


const struct platform_device_id my_id_table = {
    .name = "my_driver",
};

static struct platform_driver my_driver = {
    .probe = my_probe,
    .remove = my_remove,
    .driver = {
        .name = "my_driver",
        .owner = THIS_MODULE,
    },
    .id_table = &my_id_table,
};

int __init my_input_init(void)
{
    platform_driver_register(&my_driver);
    return ret;
}

void __exit my_input_exit(void)
{
    platform_driver_unregister(&my_driver);
}

        注册平台驱动框架如上。

        三种匹配方式, 名称匹配, id匹配, 设备树匹配。优先级由低到高。

  • 设备与驱动的名称匹配

 

  • 设备与驱动的ID匹配

  • 设备树匹配

        设备树匹配是不需要设备端的代码的, 由系统启动的时候解析设备树, 得到的device_node,满足条件的再转为平台设备。后面可能还会再出设备树, 设备模型相关的文章。

六、编写probe函数

        驱动是要控制硬件的。但是平台总线模型对硬件的描述是在设备(device)中的。所以在驱动(driver)中,我们需要得到设备(device)中的硬件资源。当设备(device)和驱动(driver)匹配成功以后,会执行驱动(driver)中的probe函数,所以我们要在probe函数中拿到设备(device)中的硬件资源。所以,重点是怎么拿到这些硬件资源呢?

函数原型:extern struct resource *platform_get_resource(struct platform_device *,unsigned int,
unsigned int);
函数作用:获取device中的硬件资源。
函数参数:第一个参数platform_device结构体。第二个参数:资源的类型。第三个参数:索引号。资源处在同类资源的哪个位置上。同类资源指的是flags是一样的同类资源。

返回资源数组中的指定的哪一个的地址。

int my_probe(struct platform_device *dev)
{
    if (dev == NULL)
    {
        goto err_probe;
    }

    struct resource dev_source;

    dev_source = platform_get_resource(dev, IORESOURCE_MEM, 0)
    if (dev_source == NULL) {
        printk("platform_get_resource failed\r\n");
        return -2;
    }

    /* use dev_source */

err_probe:
    return -1;
}

        拿到resource结构体之后, 使用gpio, 内存, 中断都是有相应的函数的, 根据不同的功能去选择。

七、平台总线源码分析

        先从注册设备端的源码入手。设备基类数据结构如下

/**
 * struct device - The basic device structure
 * @parent:	The device's "parent" device, the device to which it is attached.
 * 		In most cases, a parent device is some sort of bus or host
 * 		controller. If parent is NULL, the device, is a top-level device,
 * 		which is not usually what you want.
 * @p:		Holds the private data of the driver core portions of the device.
 * 		See the comment of the struct device_private for detail.
 * @kobj:	A top-level, abstract class from which other classes are derived.
 * @init_name:	Initial name of the device.
 * @type:	The type of device.
 * 		This identifies the device type and carries type-specific
 * 		information.
 * @mutex:	Mutex to synchronize calls to its driver.
 * @bus:	Type of bus device is on.
 * @driver:	Which driver has allocated this
 * @platform_data: Platform data specific to the device.
 * 		Example: For devices on custom boards, as typical of embedded
 * 		and SOC based hardware, Linux often uses platform_data to point
 * 		to board-specific structures describing devices and how they
 * 		are wired.  That can include what ports are available, chip
 * 		variants, which GPIO pins act in what additional roles, and so
 * 		on.  This shrinks the "Board Support Packages" (BSPs) and
 * 		minimizes board-specific #ifdefs in drivers.
 * @driver_data: Private pointer for driver specific info.
 * @links:	Links to suppliers and consumers of this device.
 * @power:	For device power management.
 *		See Documentation/driver-api/pm/devices.rst for details.
 * @pm_domain:	Provide callbacks that are executed during system suspend,
 * 		hibernation, system resume and during runtime PM transitions
 * 		along with subsystem-level and driver-level callbacks.
 * @em_pd:	device's energy model performance domain
 * @pins:	For device pin management.
 *		See Documentation/driver-api/pin-control.rst for details.
 * @msi:	MSI related data
 * @numa_node:	NUMA node this device is close to.
 * @dma_ops:    DMA mapping operations for this device.
 * @dma_mask:	Dma mask (if dma'ble device).
 * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
 * 		hardware supports 64-bit addresses for consistent allocations
 * 		such descriptors.
 * @bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller
 *		DMA limit than the device itself supports.
 * @dma_range_map: map for DMA memory ranges relative to that of RAM
 * @dma_parms:	A low level driver may set these to teach IOMMU code about
 * 		segment limitations.
 * @dma_pools:	Dma pools (if dma'ble device).
 * @dma_mem:	Internal for coherent mem override.
 * @cma_area:	Contiguous memory area for dma allocations
 * @dma_io_tlb_mem: Software IO TLB allocator.  Not for driver use.
 * @dma_io_tlb_pools:	List of transient swiotlb memory pools.
 * @dma_io_tlb_lock:	Protects changes to the list of active pools.
 * @dma_uses_io_tlb: %true if device has used the software IO TLB.
 * @archdata:	For arch-specific additions.
 * @of_node:	Associated device tree node.
 * @fwnode:	Associated device node supplied by platform firmware.
 * @devt:	For creating the sysfs "dev".
 * @id:		device instance
 * @devres_lock: Spinlock to protect the resource of the device.
 * @devres_head: The resources list of the device.
 * @knode_class: The node used to add the device to the class list.
 * @class:	The class of the device.
 * @groups:	Optional attribute groups.
 * @release:	Callback to free the device after all references have
 * 		gone away. This should be set by the allocator of the
 * 		device (i.e. the bus driver that discovered the device).
 * @iommu_group: IOMMU group the device belongs to.
 * @iommu:	Per device generic IOMMU runtime data
 * @physical_location: Describes physical location of the device connection
 *		point in the system housing.
 * @removable:  Whether the device can be removed from the system. This
 *              should be set by the subsystem / bus driver that discovered
 *              the device.
 *
 * @offline_disabled: If set, the device is permanently online.
 * @offline:	Set after successful invocation of bus type's .offline().
 * @of_node_reused: Set if the device-tree node is shared with an ancestor
 *              device.
 * @state_synced: The hardware state of this device has been synced to match
 *		  the software state of this device by calling the driver/bus
 *		  sync_state() callback.
 * @can_match:	The device has matched with a driver at least once or it is in
 *		a bus (like AMBA) which can't check for matching drivers until
 *		other devices probe successfully.
 * @dma_coherent: this particular device is dma coherent, even if the
 *		architecture supports non-coherent devices.
 * @dma_ops_bypass: If set to %true then the dma_ops are bypassed for the
 *		streaming DMA operations (->map_* / ->unmap_* / ->sync_*),
 *		and optionall (if the coherent mask is large enough) also
 *		for dma allocations.  This flag is managed by the dma ops
 *		instance from ->dma_supported.
 *
 * At the lowest level, every device in a Linux system is represented by an
 * instance of struct device. The device structure contains the information
 * that the device model core needs to model the system. Most subsystems,
 * however, track additional information about the devices they host. As a
 * result, it is rare for devices to be represented by bare device structures;
 * instead, that structure, like kobject structures, is usually embedded within
 * a higher-level representation of the device.
 */
struct device {
	struct kobject kobj;
	struct device		*parent;

	struct device_private	*p;

	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	const struct bus_type	*bus;	/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set_drvdata/dev_get_drvdata */
	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct dev_links_info	links;
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_ENERGY_MODEL
	struct em_perf_domain	*em_pd;
#endif

#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
#endif
	struct dev_msi_info	msi;
#ifdef CONFIG_DMA_OPS
	const struct dma_map_ops *dma_ops;
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */
	u64		bus_dma_limit;	/* upstream dma constraint */
	const struct bus_dma_region *dma_range_map;

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

#ifdef CONFIG_DMA_DECLARE_COHERENT
	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#endif
#ifdef CONFIG_DMA_CMA
	struct cma *cma_area;		/* contiguous memory area for dma
					   allocations */
#endif
#ifdef CONFIG_SWIOTLB
	struct io_tlb_mem *dma_io_tlb_mem;
#endif
#ifdef CONFIG_SWIOTLB_DYNAMIC
	struct list_head dma_io_tlb_pools;
	spinlock_t dma_io_tlb_lock;
	bool dma_uses_io_tlb;
#endif
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */
	struct fwnode_handle	*fwnode; /* firmware device node */

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	const struct class	*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
	struct iommu_group	*iommu_group;
	struct dev_iommu	*iommu;

	struct device_physical_location *physical_location;

	enum device_removable	removable;

	bool			offline_disabled:1;
	bool			offline:1;
	bool			of_node_reused:1;
	bool			state_synced:1;
	bool			can_match:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
	bool			dma_coherent:1;
#endif
#ifdef CONFIG_DMA_OPS_BYPASS
	bool			dma_ops_bypass : 1;
#endif
};

        注册平台设备调用了三个函数, 按照顺序查看。

        这里涉及到了很多的sys, 也就是设备模型的东西, 后面单独出文章讲讲这个sys, 也随便我自己梳理一下这个sys设备模型这块的东西。重点还是看这个函数platform_device_add, 文件路径drivers\base\platform.c

/**
 * platform_device_add - 将平台设备添加到设备层次结构中
 * @pdev: 要添加的平台设备
 *
 * 这是 platform_device_register() 的第二部分,可以单独调用,但前提是 pdev 是通过 platform_device_alloc() 分配的。
 */
int platform_device_add(struct platform_device *pdev)
{
    u32 i;  // 用于遍历资源的索引
    int ret;  // 返回值

    if (!pdev)
        return -EINVAL;  // 如果 pdev 为空,返回无效参数错误

    if (!pdev->dev.parent)
        pdev->dev.parent = &platform_bus;  // 设置设备的父设备为 platform_bus

    pdev->dev.bus = &platform_bus_type;  // 设置设备的总线类型为 platform_bus_type

    // 根据设备 ID 设置设备名称
    switch (pdev->id) {
    default:
        dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);  // 设置设备名称为 "name.id"
        break;
    case PLATFORM_DEVID_NONE:
        dev_set_name(&pdev->dev, "%s", pdev->name);  // 设置设备名称为 "name"
        break;
    case PLATFORM_DEVID_AUTO:
        /*
         * 自动分配的设备 ID。我们将其标记为自动分配,以便记住它需要被释放,并附加一个后缀
         * 以避免与显式 ID 的命名空间冲突。
         */
        ret = ida_alloc(&platform_devid_ida, GFP_KERNEL);  // 分配一个新的设备 ID
        if (ret < 0)
            goto err_out;  // 如果分配失败,跳转到错误处理
        pdev->id = ret;  // 设置设备 ID
        pdev->id_auto = true;  // 标记为自动分配的 ID
        dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);  // 设置设备名称为 "name.id.auto"
        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;  // 如果是内存资源,父资源为 iomem_resource
            else if (resource_type(r) == IORESOURCE_IO)
                p = &ioport_resource;  // 如果是 I/O 资源,父资源为 ioport_resource
        }

        if (p) {
            ret = insert_resource(p, r);  // 将资源插入到资源树中
            if (ret) {
                dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);  // 如果插入失败,记录错误
                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;  // 如果成功,返回 0

failed:
    if (pdev->id_auto) {
        ida_free(&platform_devid_ida, pdev->id);  // 如果 ID 是自动分配的,释放 ID
        pdev->id = PLATFORM_DEVID_AUTO;  // 重置 ID
    }

    // 释放所有已分配的资源
    while (i--) {
        struct resource *r = &pdev->resource[i];
        if (r->parent)
            release_resource(r);  // 释放资源
    }

err_out:
    return ret;  // 返回错误码
}
EXPORT_SYMBOL_GPL(platform_device_add);  // 导出符号,使其可以被 GPL 模块使用

 

数据结构原型:

/**
 * struct bus_type - The bus type of the device
 *
 * @name:	The name of the bus.
 * @dev_name:	Used for subsystems to enumerate devices like ("foo%u", dev->id).
 * @bus_groups:	Default attributes of the bus.
 * @dev_groups:	Default attributes of the devices on the bus.
 * @drv_groups: Default attributes of the device drivers on the bus.
 * @match:	Called, perhaps multiple times, whenever a new device or driver
 *		is added for this bus. It should return a positive value if the
 *		given device can be handled by the given driver and zero
 *		otherwise. It may also return error code if determining that
 *		the driver supports the device is not possible. In case of
 *		-EPROBE_DEFER it will queue the device for deferred probing.
 * @uevent:	Called when a device is added, removed, or a few other things
 *		that generate uevents to add the environment variables.
 * @probe:	Called when a new device or driver add to this bus, and callback
 *		the specific driver's probe to initial the matched device.
 * @sync_state:	Called to sync device state to software state after all the
 *		state tracking consumers linked to this device (present at
 *		the time of late_initcall) have successfully bound to a
 *		driver. If the device has no consumers, this function will
 *		be called at late_initcall_sync level. If the device has
 *		consumers that are never bound to a driver, this function
 *		will never get called until they do.
 * @remove:	Called when a device removed from this bus.
 * @shutdown:	Called at shut-down time to quiesce the device.
 *
 * @online:	Called to put the device back online (after offlining it).
 * @offline:	Called to put the device offline for hot-removal. May fail.
 *
 * @suspend:	Called when a device on this bus wants to go to sleep mode.
 * @resume:	Called to bring a device on this bus out of sleep mode.
 * @num_vf:	Called to find out how many virtual functions a device on this
 *		bus supports.
 * @dma_configure:	Called to setup DMA configuration on a device on
 *			this bus.
 * @dma_cleanup:	Called to cleanup DMA configuration on a device on
 *			this bus.
 * @pm:		Power management operations of this bus, callback the specific
 *		device driver's pm-ops.
 * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
 *              driver implementations to a bus and allow the driver to do
 *              bus-specific setup
 * @need_parent_lock:	When probing or removing a device on this bus, the
 *			device core should lock the device's parent.
 *
 * A bus is a channel between the processor and one or more devices. For the
 * purposes of the device model, all devices are connected via a bus, even if
 * it is an internal, virtual, "platform" bus. Buses can plug into each other.
 * A USB controller is usually a PCI device, for example. The device model
 * represents the actual connections between buses and the devices they control.
 * A bus is represented by the bus_type structure. It contains the name, the
 * default attributes, the bus' methods, PM operations, and the driver core's
 * private data.
 */
struct bus_type {
	const char		*name;
	const char		*dev_name;
	const struct attribute_group **bus_groups;
	const struct attribute_group **dev_groups;
	const struct attribute_group **drv_groups;

	int (*match)(struct device *dev, struct device_driver *drv);
	int (*uevent)(const struct device *dev, struct kobj_uevent_env *env);
	int (*probe)(struct device *dev);
	void (*sync_state)(struct device *dev);
	void (*remove)(struct device *dev);
	void (*shutdown)(struct device *dev);

	int (*online)(struct device *dev);
	int (*offline)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	int (*num_vf)(struct device *dev);

	int (*dma_configure)(struct device *dev);
	void (*dma_cleanup)(struct device *dev);

	const struct dev_pm_ops *pm;

	const struct iommu_ops *iommu_ops;

	bool need_parent_lock;
};

        后面设备和驱动匹配的时候就是调用了这些当中的一些函数。

调用过程关系

platform_device_register

        platform_device_add

                device_add

                        bus_probe_device

                                device_initial_probe

                                        __device_attach

                                                __device_attach_driver

                                                        driver_probe_device

                                                                __driver_probe_device

                                                                        really_probe

        在really_probe函数中调用找到的匹配上的驱动中的probe函数。这里看了驱动端的platform_driver_register函数的实现, 发现和设备端的实现差不多。直接上实现的原理

原理分析: 

        维护了两条链表,一个是设备的, 一个是驱动的, 当注册设备的时候, 调用总线的匹配函数, 这个匹配函数会根据规则匹配, 用当前注册的设备和驱动链表中的一 一做匹配, 匹配上之后, 调用驱动中的probe函数, 注册驱动也是一样的道理。 

http://www.dtcms.com/a/111345.html

相关文章:

  • transforms-pytorch4
  • 要素的选择与转出
  • 阿里云服务器遭遇DDoS攻击有争议?
  • 在MacOS 10.15上使用MongoDB
  • 洛谷题单3-P4956 [COCI 2017 2018 #6] Davor-python-流程图重构
  • linux signal up/down/down_interruptiable\down_uninterruptiable使用
  • 机器视觉工程师的专业精度决定职业高度,而专注密度决定成长速度。低质量的合群,不如高质量独处
  • linux 命令 awk
  • 洛谷题单3-P1217 [USACO1.5] 回文质数 Prime Palindromes-python-流程图重构
  • eBay新规倒计时:您的店铺配送方案即将被系统默认修改
  • python如何快速删除文件夹中的大量文件
  • 内网(域)渗透测试流程和模拟测试day--5--Windows和Linux的提权
  • 通信数据记录仪-产品概念ID
  • IntelliJ IDEA 2020~2024 创建SpringBoot项目编辑报错: 程序包org.springframework.boot不存在
  • 人工智能时代人才培养的变革路径:模式创新、能力重塑与认证赋能
  • Hello Robot创新突破!Stretch3机器人搭载RUMs模型实现未知环境中“即插即用”
  • 【AI模型核心流程】(一)大语言模型输入处理机制详解与常见误解辨析
  • leetcode75.颜色分类
  • 对备忘录模式的理解
  • vulkanscenegraph显示倾斜模型(5.6)-vsg::RenderGraph的创建
  • Python 助力人工智能与机器学习的深度融合
  • Linux如何设置bash为默认shell
  • AIGC9——​AIGC时代的用户体验革命:智能交互与隐私保护的平衡术
  • Linux线程同步与互斥:【线程互斥】【线程同步】【线程池】
  • JavaScript BOM、事件循环
  • 解锁 C 语言安全新姿势:C11 安全函数全解析
  • 合肥SMT贴片制造工艺全解析
  • React编程高级主题:背压(Backpressure)处理
  • 谢志辉和他的《韵之队诗集》:探寻生活与梦想交织的诗意世界
  • 6.0 使用Qt+ OpenCV+Python加载图片