嵌入式驱动学习第四周——设备树

news2024/9/21 8:02:00

前言

   掌握设备树是 Linux 驱动开发人员必备的技能!因为在新版本的 Linux 中,ARM 相关的驱动全部采用了设备树。本篇博客重点介绍一下设备树与设备树语法。

   嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程,未来预计四个月将高强度更新本专栏,喜欢的可以关注本博主并订阅本专栏,一起讨论一起学习。现在关注就是老粉啦!

目录

  • 前言
  • 1. 设备树简介
    • 1.1 设备树介绍
    • 1.2 dtb、dts、dtc、dtsi文件的关系
    • 1.3 编译设备树
    • 1.4 设备树特点
  • 2. 设备树语法
    • 2.1 基本构成
    • 2.2 节点的格式
    • 2.3 节点属性
      • 2.3.1 compatible属性
      • 2.3.2 model属性
      • 2.3.3 status属性
      • 2.3.4 #address-cells和#size-cell
      • 2.3.5 reg属性
      • 2.3.6 ranges属性
      • 2.3.7 name和device_type
      • 2.3.8 特殊节点
        • 2.3.8.1 aliases子节点
        • 2.3.8.2 chosen子节点
  • 3. 获取设备树节点信息
    • 3.1 查找节点
      • 3.1.1 根据节点路径:
      • 3.1.2 根据节点类型和compatible属性寻找节点函数
      • 3.1.3 其他方式
    • 3.2 获取属性值
      • 3.2.1 device_node结构体
      • 3.2.2 获取节点属性
      • 3.2.3 其他of函数
  • 4. Linux设备树调试
  • 参考资料

1. 设备树简介

1.1 设备树介绍

   描述设备树的文件叫DTS,该文件采用树形结构描述板级设备即开发板上的设备信息:CPU数量,内存基地址,IIC接口上接了哪些设备,如下所示:

在这里插入图片描述

   设备树是一种描述硬件的数据结构,在Linux3.x版本上才开始使引入,采用了设备树之后,许多硬件的细节可以直接通过它传递给Linux,而不再需要在内核中进行大量的冗余编码,它通过bootloader将硬件资源传给内核,使得内核和硬件资源描述相对独立。

   ARM 社区引入了 PowerPC 等架构已经采用的设备树(Flattened Device Tree),将这些描述板级硬件信息的内容都从 Linux 内中分离开来,用一个专属的文件格式来描述,这个专属的文件就叫做设备树,文件扩展名为.dts。一个 SOC 可以作出很多不同的板子,这些不同的板子肯定是有共同的信息,将这些共同的信息提取出来作为一个通用的文件,其他的.dts 文件直接引用这个通用文件即可,这个通用文件就是.dtsi 文件,类似于 C 语言中的头文件。一般.dts 描述板级信息(也就是开发板上有哪些 IIC 设备、SPI 设备等),.dtsi 描述 SOC 级信息(也就是 SOC 有几个 CPU、主频是多少、各个外设控制器信息等)。

1.2 dtb、dts、dtc、dtsi文件的关系

   这四个代表四种不同的文件格式,可以类比到C语言中的相关知识来理解。

   我们都知道,C语言编写.c文件的时候需要添加C库.h文件来添加我们所需要用到的函数,宏等。然后要用编译器将.c文件编译成计算机能理解的二进制文件。

   同样的,在设备树中,dts是设备树源码,相当于.c文件,是我们编写和能看懂的文件,然后需要添加.dtsi文件来得到一些板级信息,相当于.h文件。最后要将这个文本文件编译成计算机理解的二进制文件,即用dtc编译工具编译成.dtb这个二进制文件。总结下来,对应关系如下所示:

.dts   -->  .c文件
.dtsi  -->   .h文件
.dtb   -->   .exe文件
dtc   -->   编译器

   dtc的源码存放于scripts/dtc目录中,对应于该目录下Makefile中hostprogs-y:=dtc这一编译目标

生成dts文件对应的dtb文件
dtc -I dts -O dtb -o xxx.dtb xxxdts
反过来生成dts文件
dtc -I dtb -O dts -o xxx.dts xxxdtb

1.3 编译设备树

   进入到Linux源码根目录下,然后执行如下指令进行编译:

make dtbs	(这个指令只编译设备树)
或者
make all (这个指令是编译所有的东西,包括.ko,zImage)

1.4 设备树特点

在这里插入图片描述

   设备树可以用树状结构描述硬件资源,如上图所示,在根节点/下,挂载本地总线的SPI总线,UART总线等的树干为根节点的子节点。若是SPI下的设备不止一个,那么又可以从这根树枝下分出枝干

   设备树可以复用,例如多个硬件平台都使用i.MX6ULL作为主控芯片, 那么我们可以将i.MX6ULL芯片的硬件资源写到一个单独的设备树文件里面一般使用“.dtsi”后缀, 其他设备树文件直接使用“# includexxx”引用即可。

2. 设备树语法

   设备树文件存放地址:

源码地址/arch/arm/boot/boot/dts

   此处打开正点原子的imx6ull-alientek-emmc.dts文件来学习一下设备树语法,此处提一个事情,设备树语法中的注释用/* ... */表示

2.1 基本构成

   首先来看以下一段:

#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"

   这一段首先是包含头文件,设备树是可以像C语言那样使用“#include”引用“.h”后缀的头文件,也可以引用设备树“.dtsi”后缀的头文件。 imx6ull.dtsi由NXP官方提供,是一个imx6ull平台“共用”的设备树文件。.dts 文件引用 C 语言中的.h 文件,甚至也可以引用.dts 文件。

/ {
	model = "Freescale i.MX6 ULL 14x14 EVK Board";
	compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

	key {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "atkmini-key";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_key>;
		key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;
		interrupt-parent = <&gpio1>;
		interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
		status = "okay";
	};

	gpioled {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "atkmini-gpioled";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led>;
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

	mini {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "atkmini-led";
		status = "okay";
		reg = < 0x020c406c 0x04		/* CCM_CCGR1_BASE         */
				0x020e0068 0x04		/* SW_MUX_GPIO1_IO03_BASE */
				0x020e02f4 0x04		/* SW_PAD_GPIO1_IO03_BASE */
				0x0209c000 0x04		/* GPIO1_DR_BASE          */
				0x0209c004 0x04>;		/* GPIO1_GDIR_BASE        */
	};
	chosen {
		stdout-path = &uart1;
	};

	dht11 {
		compatible = "alientek, dht11";
		pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_dht11>;
		dht11-gpio = <&gpio1 1 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

	ds18b20 {
		compatible = "alientek, ds18b20";
		pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_ds18b20>;
		ds18b20-gpio = <&gpio1 1 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

	memory {
		reg = <0x80000000 0x20000000>;
	};

	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		linux,cma {
			compatible = "shared-dma-pool";
			reusable;
			size = <0x8000000>;
			linux,cma-default;
		};
	};
	/* 以下内容省略
}

   这一段是设备树节点,每个{}就是一个节点,最外面的/{...}就是根节点,每个设备树只有一个根节点。但是如果打开imx6ull.dtsi文件可以发现它也有一个根节点,虽然imx6ull-alientek-emmc.dts引用了imx6ull.dtsi文件, 但这并不代表imx6ull-alientek-emmc.dts设备树有两个根节点,因为不同文件的根节点最终会合并为一个

   然后我们可以看到根节点内部也有很多{...},比如ds18b20 {...}memory {}这些都是根节点的子节点。

   最后来看下一段:

&cpu0 {
	arm-supply = <&reg_arm>;
	soc-supply = <&reg_soc>;
	dc-supply = <&reg_gpio_dvfs>;
};

&clks {
	assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
	assigned-clock-rates = <722534400>;
};

&csi {
	status = "okay";

	port {
		csi_ep: endpoint {
			remote-endpoint = <&camera_ep>;
		};
	};
};

   这一部分是设备树节点的追加内容,最明显的特点就是添加了一个&符号。该符号表示向已经存在的子节点追加数据,这些已经存在的节点可以是本文件中的,也可以是#include中定义的。

2.2 节点的格式

   知道了设备树的组成后,来具体看看一个节点如何定义:

node-name@unit-address {
	属性1 = ""
	属性2 = ""
	属性3 = ""
	子节点
}

   node-name是节点名称,长度为1~31个字符,最好使用大写或小写字母开头,且能描述设备类别。根节点是一个特殊的节点,其用/指代。

   @unit-address是指定单元地址,@可理解为分隔符,unit-address的值要与节点“reg”属性的第一个地址一致,如果没有reg节点,可以省略,这时就要求同级设备树下,节点名唯一。因此要么节点名唯一,要么节点名重复单单元地址不同,总之就是node-name@unit-address这个整体要求同级唯一。

   还有一种方式就是添加了节点标签:

label:node-name@unit-address
比如:
cpu0:cpu@0

   引入 label 的目的就是为了方便访问节点,可以直接通过&label 来访问这个节点,比如通过&cpu0 就可以访问“cpu@0”这个节点,而不需要输入完整的节点名字。此外还有一个很重要的作用就是对接点进行扩展,当其他位置需要引用时可以使用节点标签来向该节点中追加内容。

2.3 节点属性

   设备树源码中常用的几种数据形式:字符串、32位无符号整数

2.3.1 compatible属性

   属性值类型:字符串

   一般compatible属性的格式如下所示,manufacturer 表示厂商,model 一般是模块对应的驱动名字。

compatible = "manufacturer,model"
例如:
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

   设备树中的每一个代表了一个设备的节点都要有一个compatible属性。 compatible是系统用来决定绑定到设备的设备驱动的关键。 compatible属性是用来查找节点的方法之一。

   一般驱动程序文件都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。如下:

static const struct of_device_id imx6ull_of_match {
	{.compatible = "fsl,imx6ull-14x14-evk"},
	{ //Sentinel }
};

static struct platform_driver imx6ull_driver = {
	.driver = {
		.name   = "xxx",
		.of_match_table = imx6ull_of_match,
	},
	.probe = imx6ull_probe,
	.remove = imx6ull_remove,
};

2.3.2 model属性

   属性值类型:字符串

   一般 model 属性描述设备模块信息,比如名字什么的。

model = "wm8960-audio";

2.3.3 status属性

   属性值类型:字符串

   该属性是设备的状态信息,可选状态如表所示:

status值描述
“okay”表示设备可操作
“disable”表示设备当前是不可操作的,但在未来可以变为可操作,比如热插拔设备插入后
“fail”表明设备不可操作,设备检测到了一系列错误,且设备不大可能变得可操作
“fail-sss”含义与"fail"相同,后面的sss是检测到的错误内容

2.3.4 #address-cells和#size-cell

   属性值类型:整数

   #address-cells 和#size-cells 这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。#address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值。具体的结合下面的reg属性讲解。

2.3.5 reg属性

   属性值类型:整数(表示地址)

   reg的形式如下:

reg = <address1 length1 address2 length2 address3 length3……>

   #address-cells控制address的数量,#size-cells控制length的数量。如下:

spi4 {
	compatible = "spi-gpio";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_spi4>;
	status = "disabled";
	gpio-sck = <&gpio5 11 0>;
	gpio-mosi = <&gpio5 10 0>;
	num-chipselects = <1>;
	#address-cells = <1>;
	#size-cells = <0>;

	gpio_spi: gpio_spi@0 {
		compatible = "fairchild,74hc595";
		gpio-controller;
		#gpio-cells = <2>;
		reg = <0>;
		registers-number = <1>;
		registers-default = /bits/ 8 <0x57>;
		spi-max-frequency = <100000>;
	};
};

   父节点设置了#address-cells = <1>以及#size-cells = <0>,于是在子节点中就是reg<0>,表示只设置了起始地址,没有设置地址长度。

2.3.6 ranges属性

   属性值类型:任意数量的 <子地址、父地址、地址长度>编码

   比如对于#address-cells和#size-cells都为1的话,以ranges=<0x0 0x10 0x20>为例,表示将子地址的从0x0~(0x0 + 0x20)的地址空间映射到父地址的0x10~(0x10 + 0x20)

   可以为空,如下所示:

soc {
	...
	ranges;
	...
}

   不为空时如下所示:

soc {
	compatible = "simple-bus";
	#address-cells = <1>;
	#size-cells = <1>;
	ranges = <0x0 0xe0000000 0x00100000>;
	
	serial {
		device_type = "serial";
		compatible = "ns16550";
		reg = <0x4600 0x100>;
		clock-frequency = <0>;
		interrupts = <0xA 0x8>;
		interrupt-parent = <&ipic>;
	};
};

   节点 soc 定义的 ranges 属性,值为<0x0 0xe0000000 0x00100000>,此属性值指定了一个 1024KB(0x00100000)的地址范围,子地址空间的物理起始地址为 0x0,父地址空间的物理起始地址为 0xe0000000。

   serial 是串口设备节点,reg 属性定义了 serial 设备寄存器的起始地址为 0x4600,寄存器长度为 0x100。经过地址转换,serial 设备可以从 0xe0004600 开始进行读写操作,0xe0004600=0x4600+0xe0000000。

2.3.7 name和device_type

   属性值类型:字符串

   这两个属性值已经被抛弃了。

2.3.8 特殊节点

2.3.8.1 aliases子节点

   aliases子节点的作用就是为其他节点起一个别名,如下所示:

aliases {
    can0 = &flexcan1;
    can1 = &flexcan2;
    ethernet0 = &fec1;
    ethernet1 = &fec2;
    gpio0 = &gpio1;
    gpio1 = &gpio2;
    gpio2 = &gpio3;
    gpio3 = &gpio4;
    gpio4 = &gpio5;
    i2c0 = &i2c1;
    i2c1 = &i2c2;
    /*----------- 以下省略------------*/
}

   以can0 = &flexcan1;为例。flexcan1是一个节点的名字, 设置别名后我们可以使用can0来指代flexcan1节点,与节点标签类似。 在设备树中更多的是为节点添加标签,没有使用节点别名,别名的作用是“快速找到设备树节点”。 在驱动中如果要查找一个节点,通常情况下我们可以使用“节点路径”一步步找到节点。 也可以使用别名“一步到位”找到节点。

2.3.8.2 chosen子节点

   chosen子节点不代表实际硬件,它主要用于给内核传递参数,此外这个节点还用作uboot向linux内核传递配置参数的“通道”, 我们在Uboot中设置的参数就是通过这个节点传递到内核的, 这部分内容是uboot和内核自动完成的。

3. 获取设备树节点信息

3.1 查找节点

3.1.1 根据节点路径:

   就和windows下查找文件一样,我们也可以通过节点路径查找节点。

/*
 * @description: 根据节点路径查找节点
 * @param-path : 指定节点在设备树中的路径
 * @return     : 返回device_node结构体指针,如果查找失败返回NULL,否则返回device_node类型的结构体指针,保存设备节点的信息。
 */
struct device_node *of_find_node_by_path(const char *path)

   得到device_node结构体之后我们就可以使用其他of 函数获取节点的详细信息。

3.1.2 根据节点类型和compatible属性寻找节点函数

/*
 * @description     : 根据节点类型和compatible属性寻找节点函数
 * @param-from      : 指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为NULL表示从根节点开始查找
 * @param-type      : 要查找节点的类型,这个类型就是device_node-> type
 * @param-compatible: 要查找节点的compatible属性
 * @return          : device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL
 */
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

3.1.3 其他方式

  常用的为以上几种,如果想看其他的话可以看野火的文档。

3.2 获取属性值

   找到一个设备节点就会返回这个设备节点对应的结构体指针(device_node*)。这个过程可以理解为把设备树中的设备节点“获取”到驱动中。“获取”成功后我们再通过一组of函数从设备节点结构体(device_node)中获取我们想要的设备节点属 性信息。其of函数存放在以下目录下:

内核源码/include/linux/of.h

3.2.1 device_node结构体

struct device_node {
    const char *name;
    const char *type;
    phandle phandle;
    const char *full_name;
    struct fwnode_handle fwnode;

    struct  property *properties;
    struct  property *deadprops;    /* removed properties */
    struct  device_node *parent;
    struct  device_node *child;
    struct  device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
    struct  kobject kobj;
#endif
    unsigned long _flags;
    void    *data;
#if defined(CONFIG_SPARC)
    const char *path_component_name;
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};
  • name: 节点中属性为name的值
  • type: 节点中属性为device_type的值
  • full_name: 节点的名字,在device_node结构体后面放一个字符串,full_name指向它
  • properties: 链表,连接该节点的所有属性
  • parent: 指向父节点
  • child: 指向子节点
  • sibling: 指向兄弟节点

3.2.2 获取节点属性

/*
 * @description: 寻找指定属性
 * @param-np   : 设备节点
 * @param-name : 属性名字
 * @param-lenp : 属性值的字节数
 * @return     : 找到的属性
property *of_find_property(const struct device_node *np, const char *name, int *lenp)

   属性的property结构体:

struct property {
	char	*name;		// 属性名字
	int	length;			// 属性长度
	void	*value;		// 属性值
	struct property *next;	// 下一个属性
	unsigned long _flags;
	unsigned int unique_id;
	struct bin_attribute attr;
};

3.2.3 其他of函数

   太多了,可以看原子的资料,或者查看这位博主的博客:Linux 学习笔记: 设备树—常用OF操作函数

4. Linux设备树调试

   我们可以在Linux下查看设备树信息:

cd /proc/device-tree
ls

在这里插入图片描述

   Linux内核在启动时会解析设备树的各个节点信息,并在/proc/device-tree目录下根据节点名字创建不同的文件或文件夹

   如果要查看其下面有哪些属性和节点,cd进去即可,比如我要看最后一个spi4:

cd spi4
ls

在这里插入图片描述

参考资料

[1] 【正点原子】I.MX6U嵌入式Linux驱区动开发指南 第四十三章
[2] 【野火】嵌入式Linux驱动开发实战指南——基于I.MX6ULL系列
[3] Device Tree -----设备树
[4] Linux 学习笔记: 设备树—常用OF操作函数

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

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

相关文章

Leetcode热题100:图论

Leetcode 200. 岛屿数量 深度优先搜索法&#xff1a; 对于这道题来说&#xff0c;是一个非常经典的图的问题&#xff0c;我们可以先从宏观上面来看问题&#xff0c;也就是说在不想具体算法的前提下&#xff0c;简单的说出如何找到所有的岛屿呢&#xff1f; 如图中所示&#x…

C#探索之路基础篇(1):编程中面向过程、数据、对象的概念辨析

文章目录 C#探索之路基础篇(1)&#xff1a;编程中面向过程、数据、对象的概念辨析1 面向过程编程1.1 概念1.2 示例代码&#xff1a;1.3 使用范围与时机&#xff1a;1.4 注意事项&#xff1a;1.5 通俗讲法 2 面向对象编程2.1 概念2.2 示例代码2.3 使用范围2.4 注意事项2.5 通俗讲…

将数据转换成xml格式的文档并下载

现在有一个实体类对象的集合&#xff0c;需要将它们转换为xml文档&#xff0c;xml文档就是标签集合的嵌套&#xff0c;例如一个学生类&#xff0c;有姓名、年龄等&#xff0c;需要转换成一下效果&#xff1a; <student><age>14</age><name>张三</na…

十、C#基数排序算法

简介 基数排序是一种非比较性排序算法&#xff0c;它通过将待排序的数据拆分成多个数字位进行排序。 实现原理 首先找出待排序数组中的最大值&#xff0c;并确定排序的位数。 从最低位&#xff08;个位&#xff09;开始&#xff0c;按照个位数的大小进行桶排序&#xff0c;将…

STL —— string(1)

目录 1. 模板 1.1 泛型编程 1.2 函数模板 1.2.1 函数模板概念 1.2.2 函数模板格式 1.2.3 函数模板的原理 1.2.4 显式实例化 1.2.5 模板参数的匹配原则 1.3 类模板 1.3.1 类模板定义格式 1.3.2 类模板的实例化 2. STL —— string类 2.1 STL 简介 2.2 标准库中的s…

地理信息数据处理-线面数据转化和数据合并(二)

需求 1.数据为LineString&#xff0c;需要转化为Polygon 2.数据为多个分散的线、面数据&#xff0c;需要转化为一条Multi类型数据 解决方案使用&#xff0c;arcgis转化工具。 需求1&#xff1a;线-面数据转化 1.在arcgis中选中对应图层&#xff0c; 然后在“数据管理工具-要…

pcl采样:随机采样

pcl 随机采样,实际上就是抽稀 头文件 代码 结果

阿里云2024最新优惠:WoSign SSL证书首购4折

阿里云SSL证书 2024 最新优惠来啦&#xff01;阿里云SSL证书新用户&#xff0c;wosign SSL证书低至4折&#xff0c;WoSign SSL提供全球信任RSA SSL证书和国密算法SM2 SSL证书&#xff01;阿里云官网官方优惠&#xff0c;需要开年采购SSL证书的用户抓紧申请这波优惠&#xff01;…

mybatis-flex入门体验(一)

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 趁着下午的空闲时间&#xff0c;研究了一下mybatis-flex&#xff0c;看着对我还…

【php基础】输出、变量、布尔类型、字符串

php基础补充 1. 输出2.和"的区别3.变量3.1变量的命名规则3.2 两个对象指向同一个值3.3 可变变量 4.变量的作用域5. 检测变量6. 布尔类型7.字符串定义与转义8.字符串常用函数9.常量 1. 输出 echo: 输出 print: 输出&#xff0c;输出成功返回1 print_r(): 输出数组 var_dum…

HarmonyOS入门学习

HarmonyOS入门学习 前言快速入门ArkTS组件基础组件Image组件Text组件TextInput 文本输入框Buttonslider 滑动组件 页面布局循环控制ForEach循环创建组件 List自定义组件创建自定义组件Builder 自定义函数 状态管理Prop和LinkProvide和ConsumeObjectLink和Observed ArkUI页面路由…

【网络安全】CobaltStrike 使用

本文章仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若读者因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与作者无关。 Cobalt Strike是一款渗透测试神器&#xff0c;Cobalt Strike已经不再使用MSF而是作为单独的平台使…

如何避免OKR成为形式主义,真正发挥其作用?

OKR&#xff08;Objectives and Key Results&#xff09;作为一种目标管理方法&#xff0c;旨在通过设定明确的目标和可衡量的关键成果&#xff0c;帮助企业实现高效、精准的管理。然而&#xff0c;在实际应用中&#xff0c;许多企业却陷入了形式主义的泥潭&#xff0c;使OKR失…

1.什么是exchange的自签名证书?安装exchange后,默认生成的证书介绍?如何查看exchange的证书情况?命令是?

目录 1.exchange的自签名证介绍 2.如何查看exchange的证书&#xff1f; 3.默认生成证书介绍 第一张&#xff1a;Microsoft Exchange 第二张&#xff1a;Microsoft Exchange Server Auth Certificate 第三张&#xff1a;WMSVC 总结&#xff1a; 1.exchange的自签名证介绍…

JAVA 获取系统当前时间、时间格式互相转化工具类,2024百度Java岗面试真题收录解析

DateTimeFormatter ftf DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss”); LocalDateTime parse LocalDateTime.parse(strTime, ftf); return LocalDateTime.from(parse).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); } //通过LocalDateTime获取当…

CPP容器vector和list,priority_queue定义比较器

#include <iostream> #include <bits/stdc.h> using namespace std; struct VecCmp{bool operator()(int& a,int& b){return a>b;/*** 对于vector和list容器&#xff0c;这里写了&#xff1e;就是从大到小* 对于priority_queue容器&#xff0c;这里写…

YOLO改进模块出现的问题及改进方法

1.grid_sampler_2d_backward_cuda 在对YOLOv9进行改进的过程中&#xff0c;有的时候就会出现这种报错&#xff1a;RuntimeError: grid_sampler_2d_backward_cuda does not have a deterministic implementation&#xff0c;but you set torch.use_deterministic_algorithms(Tr…

【SpringSecurity】十七、OAuth2授权服务器 + 资源服务器Demo

文章目录 0、库表准备1、项目结构2、基于数据库的认证3、授权服务器配置4、授权服务器效果测试5、资源服务器配置6、其他授权模式测试6.1 密码模式6.2 简化模式6.3 客户端模式6.4 refresh_token模式 相关&#x1f4d5;&#xff1a;【Spring Security Oauth2 配置理论部分】 0、…

[C#]winformYOLO区域检测任意形状区域绘制射线算法实现

【简单介绍】 Winform OpenCVSharp YOLO区域检测与任意形状区域射线绘制算法实现 在现代安全监控系统中&#xff0c;区域检测是一项至关重要的功能。通过使用Winform结合OpenCVSharp库&#xff0c;并结合YOLO&#xff08;You Only Look Once&#xff09;算法&#xff0c;我们…

Docker之docker compose!!!!

一、概述 是 Docker 官方提供的一款开源工具&#xff0c;主要用于简化在单个主机上定义和运行多容器 Docker 应用的过程。它的核心作用是容器编排&#xff0c;使得开发者能够在一个统一的环境中以声明式的方式管理多容器应用的服务及其依赖关系。 也就是说Docker Compose是一个…