【Linux 驱动基础】Linux platform平台设备驱动

# 前置知识

总线驱动模型简介:

总线是处理器与一个或者多个设备之间的通道,在设备模型中,所有的设备都是通过总线相连,当然也包括虚拟的 platform 平台总线。

总线驱动模型中有三要素:

1. 总线

/**
 * 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).
 * @dev_root:	Default device to use as the parent.
 * @dev_attrs:	Default attributes of the devices on the bus.
 * @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 nonzero value if the
 *		given device can be handled by the given driver.
 * @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.
 * @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.
 * @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
 * @p:		The private data of the driver core, only the driver core can
 *		touch this.
 * @lock_key:	Lock class key for use by the lock validator
 *
 * 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;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */
	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)(struct device *dev, struct kobj_uevent_env *env);
	int (*probe)(struct device *dev);
	int (*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);

	const struct dev_pm_ops *pm;

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;
	struct lock_class_key lock_key;
};

API: 

extern int __must_check bus_register(struct bus_type *bus);

extern void bus_unregister(struct bus_type *bus);

平台总线定义: 

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

平台总线的注册如下: 

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();

	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	of_platform_register_reconfig_notifier();
	return error;
}

2. 总线设备

struct platform_device {
	const char	*name;             /* 名字 */ 
	int		id;                    /* 用于标识该设备的ID */
	bool		id_auto;           /* 指示在注册设备时,是否自动赋予ID值 */
	struct device	dev;           /* 真正的设备(Platform设备只是一个特殊的设备,因此其核心逻辑还是由底层的模块实现) */
	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;    /* 私有数据 */
};

struct resource {
	resource_size_t start;    /* 起始地址 */
	resource_size_t end;      /* 结束地址 */
	const char *name;         /* 资源名字 */
	unsigned long flags;      /* 资源标识 */
	struct resource *parent, *sibling, *child;
};

 API:

/* 注册平台设备 */
int platform_device_register(struct platform_device *);

/* 注销平台设备 */
void platform_device_unregister(struct platform_device *);

/* 设置platform_device变量中的archdata指针 */
void arch_setup_pdev_archdata(struct platform_device *);

/* 通过资源类型获取platform_device变量中的resource信息 */
struct resource *platform_get_resource(struct platform_device *,
					      unsigned int, unsigned int);

/* 通过资源名字获取platform_device变量中的resource信息 */
struct resource *platform_get_resource_byname(struct platform_device *,
						     unsigned int,
						     const char *);

3. 总线驱动

struct platform_driver {
	int (*probe)(struct platform_device *);        /* 当驱动和硬件信息匹配成功之后,就会调用probe函数,驱动所有的资源的注册和初始化全部放在probe函数中 */
	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;        /* 内核维护的所有的驱动必须包含该成员,通常driver->name用于和设备进行匹配 */
	const struct platform_device_id *id_table;    /* 往往一个驱动可能能同时支持多个硬件,这些硬件的名字都放在该结构体数组中 */
	bool prevent_deferred_probe;
};

struct device_driver {
	const char		*name;
	struct bus_type		*bus;

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */

	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;

	int (*probe) (struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;

	const struct dev_pm_ops *pm;

	struct driver_private *p;
};

API:

/*
 * use a macro to avoid include chaining to get THIS_MODULE
 */
/* 注册设备 */
#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)
extern int __platform_driver_register(struct platform_driver *,
					struct module *);

/* 注销设备 */
extern void platform_driver_unregister(struct platform_driver *);

/* non-hotpluggable platform devices may use this so that probe() and
 * its support may live in __init sections, conserving runtime memory.
 */
#define platform_driver_probe(drv, probe) \
	__platform_driver_probe(drv, probe, THIS_MODULE)
extern int __platform_driver_probe(struct platform_driver *driver,
		int (*probe)(struct platform_device *), struct module *module);

/* 获取设备私有资源 */
static inline void *platform_get_drvdata(const struct platform_device *pdev)
{
	return dev_get_drvdata(&pdev->dev);
}

设备与驱动匹配过程

 来自:https://blog.csdn.net/qq_16504163/article/details/118562670

# 示例代码

platform_device:

#include <linux/init.h>
#include <linux/file.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

#define CCM_CCGR1_ADDR                              0x20C406C
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR       0x20E0068
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR       0x20E02F4
#define GPIO1_BASE_ADDR                             0x209C000     

static struct resource atk_led_resource[] = {
    [0] = {
        .start  = CCM_CCGR1_ADDR,
        .end    = CCM_CCGR1_ADDR + 4,
        .name   = "led_clock",
        .flags  = IORESOURCE_MEM,
    },
    [1] = {
        .start  = IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR,
        .end    = IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR + 4,
        .name   = "led_mux_ctrl",
        .flags  = IORESOURCE_MEM,
    },
    [2] = {
        .start  = IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR,
        .end    = IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR + 4,
        .name   = "led_pad_ctrl",
        .flags  = IORESOURCE_MEM,
    },
    [3] = {
        .start  = GPIO1_BASE_ADDR,
        .end    = GPIO1_BASE_ADDR + 4,
        .name   = "led_gpio_ctrl",
        .flags  = IORESOURCE_MEM,
    }
};

static void	atk_led_release(struct device *dev)
{

}

static struct platform_device atk_led_dev = {
    .name = "atk_led",
    .id = -1,
    .resource = atk_led_resource,
    .num_resources = ARRAY_SIZE(atk_led_resource),
    .dev    = {
        .release = atk_led_release,
    },
};

static int __init led_device_init(void)
{
    int err;

    err = platform_device_register(&atk_led_dev);

    return 0;
}

static void __exit led_device_exit(void)
{
    platform_device_unregister(&atk_led_dev);
}

module_init(led_device_init);
module_exit(led_device_exit);

MODULE_LICENSE("GPL");

platform_driver: 

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/platform_device.h>

typedef struct
{
    volatile uint32_t GPIO_DR;
    volatile uint32_t GPIO_GDIR;
    volatile uint32_t GPIO_PSR;
    volatile uint32_t GPIO_ICR1;
    volatile uint32_t GPIO_ICR2;
    volatile uint32_t GPIO_IMR;
    volatile uint32_t GPIO_ISR;
    volatile uint32_t GPIO_EDGE_SEL;
}GPIO_TypeDef;

static GPIO_TypeDef *GPIO;

static volatile uint32_t *CCM_CCGR;
static volatile uint32_t *IOMUXC_SW_MUX_CTL_PAD;
static volatile uint32_t *IOMUXC_SW_PAD_CTL_PAD;

struct pri_led_TypeDef
{
    char drv_name[50];  /* 驱动名称 */

    int major;      /* 主设备号 */
    int minor;      /* 次设备号 */
    dev_t devt;     /* 设备号 */

    struct device *device;   /* 设备 */
    char device_name[50];    /* 设备名称 */

    struct class *class;    /* 类 */
    char class_name[50];    /* 类名称 */
};

static struct pri_led_TypeDef pri_led = {
    .drv_name = "led_drv",
    .major  = 0,
    .minor  = 0,
    .devt   = 0,
    .device = NULL,
    .device_name = "led_dev",
    .class  = NULL,
    .class_name  = "led_class",
};

static int led_open(struct inode *inode, struct file *file)
{
    uint32_t val;
    /* 使能GPIO1时钟 */
    *CCM_CCGR |= (3 << 26);

    /* 设置IO复用 */
    val = *IOMUXC_SW_MUX_CTL_PAD;
    val &= ~(0x0F);
    val |= 5;
    *IOMUXC_SW_MUX_CTL_PAD = val;

    /* 设置IO属性 */

    /* 设置IO方向,设置GPIO1_IO03为输出 */
    GPIO->GPIO_GDIR |= (1 << 3);

    return 0;
}

static int led_release(struct inode *inode, struct file *file)
{
    return 0;
}

static ssize_t led_write(struct file *file, const char __user *buff, size_t size, loff_t *ppos)
{
    uint8_t status;
    int err;

    err = copy_from_user(&status, buff, 1);

    if(status == 1)
    {
        GPIO->GPIO_DR &= ~(1<<3);
    }
    else
    {
        GPIO->GPIO_DR |= (1<<3);
    }

    return 1;
}

static const struct file_operations led_op = {
    .owner      = THIS_MODULE,
    .open       = led_open,
    .release    = led_release,
    .write      = led_write,
};

static int atk_led_drv_probe(struct platform_device *dev)
{
    struct resource *res;
    int err;

    printk("atk_led_drv_probe\r\n");

    res = platform_get_resource(dev, IORESOURCE_MEM, 0);
    CCM_CCGR = ioremap(res->start, (res->end - res->start));
    
    res = platform_get_resource(dev, IORESOURCE_MEM, 1);
    IOMUXC_SW_MUX_CTL_PAD = ioremap(res->start, (res->end - res->start));

    res = platform_get_resource(dev, IORESOURCE_MEM, 2);
    IOMUXC_SW_PAD_CTL_PAD = ioremap(res->start, (res->end - res->start));

    res = platform_get_resource(dev, IORESOURCE_MEM, 3);
    GPIO = ioremap(res->start, sizeof(GPIO_TypeDef));

    pri_led.major = register_chrdev(0, pri_led.drv_name, &led_op);
    pri_led.devt = MKDEV(pri_led.major, pri_led.minor);

    pri_led.class = class_create(THIS_MODULE, pri_led.class_name);
    if(IS_ERR(pri_led.class))
    {
        printk("class_create error\r\n");
        err = PTR_ERR(pri_led.class);
        goto err_class_create_out;
    }

    pri_led.device = device_create(pri_led.class, NULL, pri_led.devt, NULL, pri_led.device_name);
    if(IS_ERR(pri_led.device))
    {
        printk("device_create error\r\n");
        err = PTR_ERR(pri_led.device);
        goto err_device_create_out;
    }

    return 0;

err_device_create_out:
    class_destroy(pri_led.class);
err_class_create_out:
    unregister_chrdev(pri_led.major, pri_led.drv_name);
    return err;
}

static int atk_led_drv_remove(struct platform_device *dev)
{
    printk("atk_led_drv_remove\r\n");

    iounmap(GPIO);
    iounmap(CCM_CCGR);
    iounmap(IOMUXC_SW_MUX_CTL_PAD);
    iounmap(IOMUXC_SW_PAD_CTL_PAD);

    
    device_destroy(pri_led.class, pri_led.devt);
    class_destroy(pri_led.class);
    unregister_chrdev(pri_led.major, pri_led.drv_name);
    return 0;
}

static struct platform_driver atk_led_driver = {
    .probe = atk_led_drv_probe,
    .remove = atk_led_drv_remove,
    .driver = {
        .name = "atk_led",
    },
};

static int __init led_driver_init(void)
{
    int err;

    err = platform_driver_register(&atk_led_driver);

    return 0;
}

static void __exit led_driver_exit(void)
{
    platform_driver_unregister(&atk_led_driver);
}

module_init(led_driver_init);
module_exit(led_driver_exit);

MODULE_LICENSE("GPL");

# 补充知识

1. platform_match 匹配规程

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}
  • 最先比较:platform_device.driver_override 和 platform_driver.driver.name,可以设置 platform_device 的 driver_override,强制选择某个 platform_driver
  • 然后比较:platform_device. name 和 platform_driver.id_table[i].name,Platform_driver.id_table 是“platform_device_id”指针,表示该 drv 支持若干个 device,它里面列出了各个 device 的{.name, .driver_data},其中的“ name”表示该drv 支持的设备的名字, driver_data 是些提供给该 device 的私有数据
  • 最后比较:platform_device.name 和 platform_driver.driver.name,platform_driver.id_table 可能为空,这时可以根据 platform_driver.driver.name 来寻找同名的 platform_device

2. struct platform_device 中 id 的作用

	switch (pdev->id) {
	default:
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
		break;
	case PLATFORM_DEVID_NONE:    /* -1 */
		dev_set_name(&pdev->dev, "%s", pdev->name);
		break;
	case PLATFORM_DEVID_AUTO:    /* -2 */
		/*
		 * 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;
	}
  • id = -1 时,名称直接为名字
  • id = -2时,名称为自动获取的id加上.auto
  • 其他为名称加上id

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言书籍——B/陷阱之处(2)

文章参考于文献&#xff1a;《C陷阱与缺陷》[美]Andrew Koening &#x1f308;个人主页&#xff1a;慢了半拍 &#x1f525; 创作专栏&#xff1a;《史上最强算法分析》 | 《无味生》 |《史上最强C语言讲解》 | 《史上最强C练习解析》 &#x1f3c6;我的格言&#xff1a;一切只…

Obsidian插件-高亮块(Admonition)

在插件市场里面搜索Admonition并安装插件&#xff0c;就可以使用高亮块了。 添加高亮块 用法稍微有一些不同。按照下面的格式&#xff0c;输入Markdown就可以创建一个高亮块。 内容内容内容输入*ad-*会出现相应的类型可以选择

Dubbo管理控制台

1.将资料中的dubbo-admin-2.6.0.war文件复制到tomcat的webapps目录下 2.启动tomcat,修改WEB-INF下的dubbo.properties文件 #如果Zookeeper是安装在虚拟机上的那么注册中心的地址需要修改为虚拟机的ip地址 dubbo.registry.addresszookeeper://192.168.100.110:2181 dubbo.admin…

对象存储服务MinIO快速入门

对象存储服务MinIO快速入门 MinIO简介开箱使用快速入门封装MinIO为starter1 创建模块heima-file-starter2 配置类3 封装操作minIO类4 对外加入自动配置5 其他微服务使用 MinIO简介 官网文档 开箱使用 docker run -p 9000:9000 --name minio -d --restartalways -e "MINIO…

ocr之opencv配合paddleocr提高识别率

背景1&#xff1a;在这篇文章编写之前使用到的工具并不是opencv&#xff0c;而是java原有的工具BufferedImage。但因为在使用过程中会频繁切图&#xff0c;放大&#xff0c;模糊&#xff0c;所以导致的jvm内存使用量巨大&#xff0c;分秒中都在以百兆的速度累加内存空间。这种情…

WIFI驱动移植实验: openssl库的移植(wpa_supplicant 依赖库)

一. 简介 前面实现了WIFI驱动的移植&#xff0c;而连接某个WIFI热点上就需要用到 wpa_supplicant 工具&#xff0c;所以&#xff0c;本文开始为 移植 wpa_supplicant 工具做准备。 wpa_supplicant 依赖于 openssl库 与 libnl库&#xff0c;因此&#xff0c;需要移植一下open…

鸿蒙hdc使用指导

简介 hdc&#xff08;HarmonyOS Device Connector&#xff09;是HarmonyOS为开发人员提供的用于调试的命令行工具&#xff0c;通过该工具可以在windows/linux/mac系统上与真实设备或者模拟器进行交互。 环境准备 hdc工具通过HarmonyOS SDK获取&#xff0c;存放于SDK的toolch…

蓝桥杯练习题总结(三)线性dp题(摆花、数字三角形加强版)

目录 一、摆花 思路一&#xff1a; 确定状态&#xff1a; 初始化&#xff1a; 思路二&#xff1a; 确定状态&#xff1a; 初始化&#xff1a; 循环遍历&#xff1a; 状态转移方程&#xff1a; 二、数字三角形加强版 一、摆花 题目描述 小明的花店新开张&#xff0c;为了吸…

VBA技术资料MF134:单值匹配查找

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

【消息队列开发】 实现 MqClientTests 类——测试客户端

文章目录 &#x1f343;前言&#x1f333;所需属性&#x1f334;BeforeEach&#x1f332;AfterEach&#x1f38d;API测试⭕总结 &#x1f343;前言 本次开发任务 测试客户端接口 &#x1f333;所需属性 所需要一共三个属性 BrokerServer&#xff1a;服务器 ConnectionFa…

C++进阶之路---C++11新特性 | lambda表达式

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 前言&#xff1a;简介lambda 在C中&#xff0c;lambda表达式是一种匿名函数的方式&#xff0c;它可以用来解决以下问题&a…

免费|Python|【需求响应】一种新的需求响应机制DR-VCG研究

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序对应文章《Contract Design for Energy Demand Response》&#xff0c;电力系统需求响应&#xff08;DR&#xff09;用来调节用户对电能的需求&#xff0c;即在预测的需求高于电能供应时&#xff0c;希…

TTS 文本转语音模型综合简述

本文参考文献&#xff1a; [1] Kaur N, Singh P. Conventional and contemporary approaches used in text ot speech synthesis: A review[J]. Artificial Intelligence Review, 2023, 56(7): 5837-5880. [2] TTS | 一文了解语音合成经典论文/最新语音合成论文篇【20240111更新…

使用certbot为网站启用https

1. 安装certbot客户端 cd /usr/local/bin wget https://dl.eff.org/certbot-auto chmod ax ./certbot-auto 2. 创建目录和配置nginx用于验证域名 mkdir -p /data/www/letsencryptserver {listen 80;server_name ~^(?<subdomain>.).ninvfeng.com;location /.well-known…

大数据之scala

为什么学习scala spark是新一代内存级大数据计算框架&#xff0c;是大数据的重要内容 spark就是使用scala编写的&#xff0c;因此为了更好的学习spark&#xff0c;需要掌握scala这门语言 spark的兴起&#xff0c;带动scala语言的发展 scala发展历史 联邦理工学院的马丁 奥德…

spring boot3自定义注解+拦截器+Redis实现高并发接口限流

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途 目录 写在前面 内容简介 实现思路 实现步骤 1.自定义限流注解 2.编写限流拦截器 3.注册拦截器 4.接口限流测试 写在前…

http和https之间的区别和优势

HTTP&#xff08;超文本传输协议&#xff09;和HTTPS&#xff08;安全超文本传输协议&#xff09;是两种在互联网通信中应用广泛的协议&#xff0c;它们在数据传输、安全性、加密等方面有着明显的区别和优势。下面将详细介绍HTTP和HTTPS之间的区别和各自的优势。 区别和优势 1…

2024年腾讯云最新优惠活动整理汇总

随着云计算技术的不断发展&#xff0c;越来越多的企业和个人开始选择将业务迁移到云端。腾讯云作为国内领先的云计算服务提供商&#xff0c;不仅提供了稳定、安全的云服务&#xff0c;还通过一系列的优惠活动&#xff0c;为用户带来了实实在在的福利。2024年&#xff0c;腾讯云…

用Python机器学习模型预测世界杯结果靠谱吗?

看到kaggle、medium上有不少人用球队的历史数据来进行建模预测&#xff0c;比如用到泊松分布、决策树、逻辑回归等算法&#xff0c;很大程度上能反映强者恒强的现象&#xff0c;比如巴西、英格兰等大概率能进8强&#xff0c;就像高考模拟考试成绩越好&#xff0c;大概率高考也会…

美团0316春招笔试题

下面是美团2024-03-16笔试真题&#xff0c;进行了VP&#xff0c;由于未参与评测&#xff0c;故不保证正确性&#xff0c;仅供参考。 第一题 小美点外卖 求和然后减去满减和红包即可。 #include <bits/stdc.h> using namespace std; using LL long long ; int n, t, x,…
最新文章