platform 总线

news2024/11/15 13:50:32

驱动的分离与分层思想

分离:硬件信息分离;
在编写硬件驱动的时候,需要操作许多硬件寄存器。比如gpio 驱动,你需要知道gpio控制器 寄存器的地址,你想要哪个gpio输出?或是输入? 这些操作最终都是靠设置寄存器完成,如果你想要操作的gpio变了、方向变了,这些硬件信息的都要修改驱动。

或者说同一个系列的soc 它们的寄存器地址不同、soc 更新换代寄存器地址改变等等,但是寄存器的设置方法相同,驱动无法兼容多个硬件。
这样,明明可以只用一份代码通,却因为硬件不同要写无数份,代码重用性很低,内核中也会多许多这种垃圾代码。
这种要不断修改、重用性低的驱动是不理想的,有没有可能做出一份通用且不需要经常修改代码的驱动呢?

可以,硬件信息分离。把控制器的寄存器地址、要输出的引脚、方向等信息从驱动分离出来另外保存,原本的驱动就变成一些纯软件代码与操作寄存器的代码,这样就不需要因为硬件的改变而不断修改代码。

分离的思想就是把硬件信息从驱动中分离出来,做出一套通用的驱动。

Linux 驱动如何实现分离:内核3.x 以前使用 .c(结构体) 文件描写硬件信息,有设备树后用设备描写硬件信息。

分层:代码分层。
使用过内核通用的gpio驱动可以知道,内核gpio驱动分为两部分:

  1. gpio核心层 (与硬件无关的软件层,负责管理下层的各种驱动,调用它们提供的硬件操作函数(下层一般会把操作函数设成回调函数)进行封装,实现各个厂家的驱动统一的操作方法)
  2. chip_xxx_gpio.c 某个soc 厂家为自己芯片编写的gpio硬件驱动。(实现gpio功能,各个厂家的操作方式可能不同,所以操作函数也不同)

为什么要分层呢?
因为linux内核是要兼容多个平台的,不同的平台可能寄存器的设计不同导致方法不同,那又会衍生出很多驱动。
所以内核提出了分层思想,由与硬件无关的软件层作为上层(核心层)来管理下层驱动,而各个厂家则根据自己的硬件编写驱动代码作为下层(硬件驱动层)(不管你硬件怎么变,暴露给上层的接口必须一致,按照上层的管理方法来编写)。

加上硬件信息,一个驱动一共就是三部分:通用的驱动框架、硬件驱动和硬件信息(硬件信息+硬件驱动=硬件驱动层)。
在这里插入图片描述

Linux 驱动-总线-设备 模型

分离是把硬件信息和驱动代码分离开来,那么在Linux内核中如何实现分离。
Linux 内核建立了设备-总线-驱动 这样的模型,用struct device 来表示设备,用struct device_driver 来表示驱动。(它们都定义于include\linux\device.h)
(struct device 和struct device_driver 只是两个基类,根据不同的总线会衍生出很多派生类,比如platform_device(它可以描述设备信息)、platform_driver (可以描述驱动))

struct device {
	struct device		*parent;				//父设备

	struct device_private	*p;					

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */	//名字
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	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	//platform 数据
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with	//驱动数据,一般可以在驱动代码中设置为比较常用的结构体变量
					   dev_set/get_drvdata */
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
#endif

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#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. */
	unsigned long	dma_pfn_offset;

	struct device_dma_parameters *dma_parms;

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

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#ifdef CONFIG_DMA_CMA
	struct cma *cma_area;		/* contiguous memory area for dma
					   allocations */
#endif
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */		//设备树节点
	struct fwnode_handle	*fwnode; /* firmware device node */

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */		//设备号
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;											//device 所属的类
	const struct attribute_group **groups;	/* optional groups */		//属性文件,组

	void	(*release)(struct device *dev);								//用于释放device 的回调函数
	struct iommu_group	*iommu_group;

	bool			offline_disabled:1;
	bool			offline:1;
};
struct device_driver {
	const char		*name;				//驱动名字
	struct bus_type		*bus;			//总线类型

	struct module		*owner;			//一般设置为THIS_MODULES
	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;		//用于设备树匹配的id	
	const struct acpi_device_id	*acpi_match_table;

	int (*probe) (struct device *dev);		//当匹配成功后会调用probe(在probe中我们可以自由发挥,做任何想做的事,比如硬件的设置)
	int (*remove) (struct device *dev);					//一方想要卸载时会调用remove(匹配成功的状态下)
	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;
};

现在有了设备信息、驱动代码,但是要把它们合在一起使用才能算是完整可用的驱动代码,内核里有那么多个驱动,就会产生无数个device 和device_driver,如何把它们匹配在一起?
这时候总线就出来了,它能把相互对应的device 和device_driver 匹配起来形成完整的驱动。(此总线并非硬件总线,而是Linux内核中虚拟出来的,用来匹配设备信息和驱动代码)
总线用一个struct bus_type 类型的数据结构来描述:

include\linux\device.h
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;
};

在这里插入图片描述
查看device 与 device_driver 匹配过程:
原理分析:
注册device 时会将它添加到bus->p->klist_devices链表,然后获取bus->p->klist_drivers 链表,将device 与链表中的所有device_driver 一一比较,如果匹配成功就会调用device_driver->probe。
同样注册注册device_driver 时也会将它添加到bus->p->klist_drivers链表,然后获取bus->p->klist_devices 链表,将device_driver 与链表中的所有device 一一比较,如果匹配成功就会调用device_driver->probe。

struct device 使用device_register() 向内核注册,device_driver 使用driver_register() 向内核注册。

device_register

在这里插入图片描述
device_add
->bus_add_device
bus_add_device 中将dev->p->knode_bus(struct klist_node) 添加到bus->p->klist_devices(struct klist) 链表中 (bus根据设备所属的总线来定,可能是platform、i2c、spi… 任何总线)。
在这里插入图片描述
device_add
->bus_probe_device
->device_attach (attach 表示附加的意思,此函数将device 附加到某个device_driver 上)
device_attach 中调用bus_for_each_drv 遍历bus 下的所有device_driver。
在这里插入图片描述
klist_iter_init_node 初始化了一个klist_iter (i)
i->i_klist = k; //被设置为了bus->p->klist_drivers(它就是device_driver 的klist 链表)
i->i_cur = n; //i_cur 是struct klist_node 类型的(传入的start为NULL,所以i_cur == NULL)
循环的调用next_driver(&i) 来遍历bus->p->klist_drivers 所有节点,获取与节点对应的device_driver
(依靠刚刚构建的klist_iter 来找到下一个链表节点,由于klist_iter->i_cur 为空,所以可能是从头开始获取节点之类的)

拿到了device_driver,就可以与新注册device 进行比对了,调用fn 也就是传入的_device_attach 进行比较,如果匹配上就会绑定device 和device_driver(device->driver = driver),并且调用device_driver->probe。
在这里插入图片描述
在这里插入图片描述
device_add
->bus_probe_device
->device_attach
->bus_for_each_drv
->__device_attach //判断device 与device_driver 是否匹配
在这里插入图片描述
比较的方法由具体的总线提供,直接调用device_driver->bus->match 回调函数。
在这里插入图片描述
假如总线是platform 的话,那么bus->match 就是platform_match。
在这里插入图片描述
platform_match 中有三种匹配方式,在有设备树的时我们一般使用设备树的匹配方式(比较compatible 的值),没有设备树则使用方法3 和4。
在这里插入图片描述
of_driver_match_device 是通过platform_driver->driver->of_match_table->compatible 与platform_device->device->of_node 中读取compatible 属性值来比较。
of_driver_match_device
->of_match_device
在这里插入图片描述
platform_match_id 是通过比较platform_device 的名字和platform_driver->id_table->name 来判断是否匹配。
在这里插入图片描述

如果匹配则调用 driver_probe_device 绑定device 与device_driver 的关系,并且调用bus->probe 或device_driver->probe
driver_probe_device
->really_probe
在这里插入图片描述
优先调用device->bus->probe,如果没提供则调用deivce_driver->probe
在这里插入图片描述

driver_register

driver_register 调用bus_add_driver 向总线添加device_driver
在这里插入图片描述
bus_add_driver 将device_driver->p->knode_bus (klist_node)节点添加到bus->p->klist_drivers (klist)链表。
然后调用driver_attach,将新注册的device_driver 与bus 下的所有device 进行比对。
在这里插入图片描述
driver_attach 调用bus_for_each_dev 遍历总线下的所有device,调用_driver_attach 将新注册的device_driver 与device 一一比对。
在这里插入图片描述
bus_for_each_dev 循环遍历bus 下的所有device,调用__driver_attach 进行device 与device_driver比较
在这里插入图片描述
__driver_attach 调用driver_match_device 来判断device 和device_driver 是否匹配(这一步和__device_attach 中是一样的调用bus->match 来对比)
如果两者匹配的话调用driver_probe_device->really_probe 绑定device 和device_driver的关系,然后调用bus->probe 或device_driver->probe (这一步和__device_attach 中也是一模一样的)
在这里插入图片描述

根据不同外设的特点,内核定义了多条常用的总线,比如platform总线、i2c 总线、spi总线… 等等。
定义如下:
//drivers/base/platform.c
在这里插入图片描述
//drivers/i2c/i2c-core.c
在这里插入图片描述
//driver/spi/spi.c
在这里插入图片描述
可以在命令行查看内核具体定义了哪些总线,如:
在这里插入图片描述
为什么要定义这么多总线类型,以i2c 和platform 来举例:
i2c 总线:
挂在i2c总线(硬件)下的从设备,比如加密芯片、rtc芯片、触摸屏芯片等等,这些从设备它们也需要驱动,它们自然也要按照分离思想来设计。
那么内核中的i2c 总线就是用来帮助i2c从设备的设备信息 和驱动,互相匹配的。

platform 总线:
像i2c、spi这样硬件有实体总线的,从设备驱动可以用总线来管理。
那么没有总线的硬件外设怎么办,比如gpio、uart、i2c控制器、spi 控制器…等等,这些通通用platform 总线来管理。

如何注册一个总线(常用的总线内核中已经注册好了,通常情况下无需自己创建一条总线):
可以用bus_register 函数注册一条总线:
int bus_register(struct bus_type *bus)
bus_unregister 注销一条总线:
void bus_unregister(struct bus_type *bus)
如platform 总线的注册代码如下:
//drivers\base\platform.c
在这里插入图片描述

platform 总线

根据platform 总线和外设的特点,为了更好的描述设备信息和驱动代码,在devicedevice_driver 基础上衍生了platform_deviceplatform_driver 两个派生类。

//include/linux/platform_device.h
struct platform_device {
	const char	*name;					//设备名
	int		id;
	bool		id_auto;
	struct device	dev;				//派生类仍然包含基础的device,因为里面有不可或缺的重要信息
	u32		num_resources;				//资源数量
	struct resource	*resource;			//描述硬件设备的资源,资源包括寄存器地址范围、irq 等等。

	const struct platform_device_id	*id_entry;		//用于匹配的id
	char *driver_override; /* Driver name to force a match */

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

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

在kernel v3.x 版本以上,内核引入设备树,所以硬件信息往往在设备树中描述。内核代码会解析设备树,把它转化为一个platform_device。
例如这是一个描述gpio控制器的设备树节点:
reg 第一个字段表示gpio控制器寄存器的起始地址,第二个字段表示长度。(物理地址)
interrupts 表示中断号。(共享中断 中断号 中断触发类型)
在这里插入图片描述

//include/linux/platform_device.h
struct platform_driver {
	//当platform_driver与platform_device 匹配时就会调用probe(在probe函数中可以自由发挥,写任何东西,比如驱动代码)
	int (*probe)(struct platform_device *);	
	//当platform_driver 或是platform_device 一方卸载时就会调用remove (匹配完成的情况下)
	//(在remove中通常做与probe 相反的操作,比如probe 中申请了内存,那么remove 就要释放内存)
	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;				//派生类任然包含基类,这是很重要的(device_driver 中包含决定双方是否匹配的重要信息)
	const struct platform_device_id *id_table;	//用于匹配的id
	bool prevent_deferred_probe;
};

在platform_device 中有一个resource 成员,它对于描述硬件信息是非常重要的。
它用来描述一个资源,资源包括寄存器地址、中断等等。(可描述的资源很多,参考宏定义)

//include/linux/ioport.h
/*
如果所描述的资源是寄存器地址(IORESOURCE_MEM 类型),那么:
start 保存着reg 的起始地址,end 保存着reg的末尾地址。start-end 就描述出了一段地址范围。
如果描述的是irq,那么:start 保存着irq 的值。
*/
struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;			//资源的名字
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};

//include/linux/ioport.h
#define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */
#define IORESOURCE_IO		0x00000100	/* PCI/ISA I/O ports */		//io内存地址资源类型
#define IORESOURCE_MEM		0x00000200								//mem内存地址资源类型
#define IORESOURCE_REG		0x00000300	/* Register offsets */
#define IORESOURCE_IRQ		0x00000400								//irq 资源类型
#define IORESOURCE_DMA		0x00000800
#define IORESOURCE_BUS		0x00001000
......

那么platform_deviceplatform_driver 这两个结构体究竟该如何使用它呢,以imx6ull 的gpio驱动为例:
首先硬件信息会用设备树节点描述,由内核解析成一个platform_device 注册到内核中。
(没有设备树的版本,驱动需要自己创建一个platform_device 结构体,设置里面的内容,然后向内核注册。使用platform_device_register 函数注册)
int platform_device_register(struct platform_device *);
在这里插入图片描述
在驱动中,需要构建一个platform_driver,初始化内容,并调用platform_driver_register 向内核注册。
在这里插入图片描述
细看platform_driver 中设置的内容:
device_driver->name :是驱动的名字,它也可以用来判断platform_device 与platform_driver 是否匹配。(非设备树情况下)
device_driver->of_match_table :设备树特有的匹配方式。在dtb节点 和of_match_table 中都有一个compatible 属性,它用来判断驱动与设备是否兼容,如果compatible的值是相同的那么就就判断他两是匹配的。data 是与硬件平台相关的数据。
在这里插入图片描述
在这里插入图片描述
id_table:不使用设备树情况下的一种匹配方式,通过对比id_table->name 与platform_device->name 来判断两者是否兼容。
在这里插入图片描述
probe:当匹配完成之后,就会自动调用probe 函数了,在probe函数中我们可以做任何事情,比如硬件的设置等等。
参考mxc_gpio_probe 来看看它做了什么:
(传入的参数就是由设备树节点转化而来的platform_device)
在这里插入图片描述
除了这些,在probe 中还可以做任何事:注册irq、设置driver_data 等等。
这个函数可以设置platform_device->dev.drver_data,可以把它设置成任何驱动中常用的变量地址。
在这里插入图片描述
platform_device->dev.of_node 指向的就是与驱动所匹配的设备树节点,可以用它来获取设备树节点。

platform_driver 与platform_device 注册流程

总结前面的内容:为了实现硬件信息的分离,Linux 内核用一个platform_device 来描述硬件信息,用platform_driver 来描述驱动代码;由platform 总线来管理,帮助它们相互匹配。
使用platform总线管理的设备都要调用platform_device_register 注册platform_device,驱动要调用platform_driver_regster 注册platform_driver。每当platform_device 和platform_driver 匹配就会调用platform_driver.probe 来执行驱动代码。
#define platform_driver_register(drv) \ __platform_driver_register(drv, THIS_MODULE) extern int __platform_driver_register(struct platform_driver *, struct module *);

int platform_device_register(struct platform_device *);

那么双方究竟是如何完成匹配的,又是如何调用到probe 函数的?这就要看看platform_device_registerplatform_driver_regster 是如何注册的。

代码解析
调用platform_device_register 注册platform_device

platform_device_register
	->platform_device_add

将platform_device->dev 的父设备设为platform_bus (在注册platform 总线时创建的)
platform_device->dev.bus 设为platform_bus_type。
在这里插入图片描述
调用device_add 添加platform_device->dev (struct device)。
在这里插入图片描述
(从device_add 开始就是device 的注册过程,参考前面的device与device_driver 匹配过程)

调用platform_driver_register 注册platform_driver

#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)

__platform_driver_register 中将platform_driver->driver.probe 设置为platform_drv_probe 那么在device 和device_driver 匹配成功后就会调用platform_drv_probe 。
调用driver_register 注册platform_driver->driver (参考前面的的device_driver 注册过程,device 与device_driver 匹配过程)
在这里插入图片描述
在device 与device_driver 匹配成功后,最终将调用device_driver->probe 对于platfrom 总线来说,它就是platform_drv_probe。
在platform_drv_probe 中会调用platform_driver->probe(也就是各自驱动中实现的真正的驱动代码了)
在这里插入图片描述

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

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

相关文章

19 pandas 分层索引与计算

文章目录分层设置与查询数据index 为有序index 为无序(中文)查看数据示例多层索引的创建方式(行)1、from_arrays 方法2、from_tuples 方法3、from_product 方法多层索引的创建方式(列)分层索引计算MultiIndex 参数表分…

Mybatis笔记整理

1. 相关文档地址 中文文档 https://mybatis.org/mybatis-3/zh/index.htmlMybatis可以配置成适应多种环境&#xff0c;不过每个SqlSessionFactory实例只能选择一种环境。Mybatis默认事务管理器是JDBC&#xff0c;连接池&#xff1a;POOLEDMaven仓库:下载地址<dependency>…

KVM-1、Linux 操作系统及虚拟化

1. 前言 一台计算机是由一堆硬件设备组合而成,在硬件之上是操作系统,操作系统与计算机硬件密不可分,操作系统用来管理所有的硬件资源提供服务,各个硬件设备是通过 总线 进行连接起来的: 在操作系统之上,需要一个人机交互接口,我们才能使用计算机对其发送指令,这个人机…

C语言【动态内存管理 后篇】

动态内存管理 后篇&#x1fac5;经典例题&#x1f926;‍♂️题目1&#x1f926;‍♂️题目2&#x1f926;‍♂️题目3&#x1f926;‍♂️题目4&#x1fac5;C/C程序的内存开辟前面的一篇文章动态内存管理 前篇&#xff0c;我们已经了解过了动态内存管理的相关信息&#xff0c…

数据库管理-第五十七期 多灾多难(20230218)

数据库管理 2023-02-18第五十七期 多灾多难1 网络震荡2 挂一大片3 恢复虚拟机总结第五十七期 多灾多难 2月第三周&#xff0c;怎么说呢&#xff0c;多灾多难的一周&#xff0c;一周两次严重故障&#xff0c;而且事情还都发生在24小时之内&#xff0c; 1 网络震荡 本周四一大…

不要让GPT成为你通向“学业作弊”的捷径——使用GPT检测工具来帮助你保持正确的方向

不要让GPT成为你通向“学业作弊”的捷径——使用GPT检测工具来帮助你保持正确的方向 最近&#xff0c;多所美国高校以及香港大学等都明确禁止在校使用ChatGPT等智能文本生成工具。GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一种自然语言处理技术&#x…

04 C++提高编程

文件基本上是黑马程序员的文档&#xff0c;部分添加自己需要的内容&#xff0c;仅用于自己学习&#xff01;链接&#xff1a;黑马程序视频课程GitHub:源代码 C提高编程 本阶段主要针对C泛型编程和STL技术做详细讲解&#xff0c;探讨C更深层的使用 1 模板 1.1 模板的概念 模…

spring的注解

Spring的常用注解常用注解EnableWebMvcConfigurationBeanSpringBootApplication && MapperScanControllerResponseBodyRestControllerRequestMapping("robot")ResourceRequestMappingService常用注解 EnableWebMvc 在配置类中开启Web MVC的配置支持。 Con…

力扣62.不同路径

文章目录力扣62.不同路径题目描述方法1&#xff1a;暴力深搜(超时未通过)方法2&#xff1a;动态规划力扣62.不同路径 题目描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器…

[安装] Dell电脑安装系统时看不到固态硬盘的解决方案

前言如图&#xff0c;配备NVME固态硬盘的机器在重新安装时候没有看到固态硬盘。这其实是由于安装镜像缺少IRST驱动导致的。1.硬盘模式设置为AHCI大多数戴尔机器出厂BIOS默认硬盘模式为Raid On而非AHCI&#xff0c;WIN10纯净版镜像中自带NVME驱动&#xff0c;可以识别AHCI模式下…

台积电后悔莫及,美国没有将它当成自己人,大陆市场重要性凸显

台积电对于赴美设厂可谓变了再变&#xff0c;日前台积电创始人张忠谋就发声指美国“美国认为他可以通钱来快速进入芯片市场&#xff0c;这太天真了”&#xff0c;这是在台积电获得美国的补贴少得可怜之后的表态&#xff0c;凸显出对美国的不满。台积电对于赴美设厂一开始出现截…

postman教程

一、前言 1、postman 是什么 postman 是一款 HTTP 客户端工具&#xff0c;它可以用来调试和测试接口。通过 HTTP 协议&#xff0c;将请求数据发送到服务端&#xff0c;并从服务端获取响应数据。 2、为什么要使用 postman 后端开发者写的代码在大多数情况下是要给到前端开发…

day46【代码随想录】动态规划之打家劫舍 III、买卖股票的最佳时机、买卖股票的最佳时机II

文章目录前言一、打家劫舍 III&#xff08;力扣337&#xff09;【较难】二、买卖股票的最佳时机&#xff08;力扣121&#xff09;三、买卖股票的最佳时机II&#xff08;力扣122&#xff09;前言 1、打家劫舍 III 2、买卖股票的最佳时机 3、买卖股票的最佳时机II 一、打家劫舍 …

C++——二叉树进阶oj题

目录二叉树创建字符串二叉树的分层遍历1二叉树的分层遍历2给定一个二叉树, 找到该树中两个指定节点的最近公共祖先二叉树搜索树转换成排序双向链表。根据一棵树的中序遍历与后序遍历构造二叉树根据一棵树的前序遍历与中序遍历构造二叉树二叉树创建字符串 题目链接 思路&#x…

Python 之 Matplotlib 散点图、箱线图和词云图

文章目录一、散点图1. scatter() 函数2. 设置图标大小3. 自定义点的颜色和透明度4. 可以选择不同的颜色条&#xff0c;配合 cmap 参数5. cmap 的分类5.1 Sequential colormaps&#xff1a;连续化色图5.2 Diverging colormaps&#xff1a;两端发散的色图 .5.3 Qualitative color…

Python获取zabbix问题触发器

背景&#xff1a;阿里云的ECS服务器因为阿里云升级插件&#xff0c;导致安全防护程序重启&#xff0c;产生不同的端口。导致低自动发现注册的端口 大量报警。 解决&#xff1a;杀掉关于因为非业务 变更的端口检测的触发器。 相关文档&#xff1a; Zabbix监控之主机端口监控自…

3. 投票 案例项目(合集)

3.投票-1创建项目和子应用 创建项目 命令$ python django-admin startproject mysite 目录结构mysite/ # 项目容器、可任意命名manage.py # 命令行工具mysite/ # 纯 Python 包 # 你引用任何东西都要用到它__init__.py # 空文件 告诉Python这…

面试官让你说说react状态管理?

hooks 为什么不能放在条件判断里 以 setState 为例&#xff0c;在 react 内部&#xff0c;每个组件(Fiber)的 hooks 都是以链表的形式存在 memoizeState 属性中 update 阶段&#xff0c;每次调用 setState&#xff0c;链表就会执行 next 向后移动一步。如果将 setState 写在条…

7-Zip压缩文件如何设置和清除密码?

7-zip拥有极高的压缩比&#xff0c;压缩比要比普通ZIP文件高30-50%&#xff0c;也是很多人选用的压缩格式。和其他压缩文件一样&#xff0c;7-Zip也可以设置密码保护&#xff0c;今天就来说说如何设置和清除7-Zip压缩包的密码。 首先&#xff0c;我们可以通过软件应用商店直接…

代码随想录算法训练营第三十四天 | 860.柠檬水找零,406.根据身高重建队列,452. 用最少数量的箭引爆气球

一、参考资料柠檬水找零https://programmercarl.com/0860.%E6%9F%A0%E6%AA%AC%E6%B0%B4%E6%89%BE%E9%9B%B6.html 根据身高重建队列 https://programmercarl.com/0406.%E6%A0%B9%E6%8D%AE%E8%BA%AB%E9%AB%98%E9%87%8D%E5%BB%BA%E9%98%9F%E5%88%97.html 用最少数量的箭引爆气球ht…