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

Linux设备树详解

Linux 设备树详解

Linux 操作系统早期是针对个人电脑设备而开发的操作系统,而个人电脑处理器产商较为单一(例如只有 Intel,AMD)同时个人电脑产商均使用 Intel 或 AMD 制造的处理器,业界形成了统一的总线/硬件接口标准,所以 Linux 系统只要遵循这些标准即可。

然而随着 ARM 处理器在嵌入式微型设备上的广泛应用,Linux 也支持了 ARM 处理器。但是由于 ARM 架构的授权机制使得任何产商都可以制造 ARM 处理器,各产商没有统一标准,制造出的 ARM 处理器各不相同。

所以 Linux 系统源码中添加了各产商 ARM 芯片描述代码,才能支持各产商的芯片,这也导致 Linux 源码包含了大量的 ARM 芯片描述代码。

1. 前言

在 Linux 没有设备树之前 ARM 架构的板级芯片硬件细节通过 C 源码的形式编写在 “arch/arm/plat-xxx” 和 “arch/arm/mach-xxx” 形式命名的文件中,不同的硬件对应不同的文件,这些不可复用的文件参杂在 Linux 内核源码目录中。

为了从 Linux 内核源码中去除芯片描述代码就引入了设备树,设备树的本质是不再使用 C 源码去描述芯片,而是使用设备树DTS结构化脚本语法去描述各种芯片。

引入设备树后不同的芯片还是需要对应不同的设备树文件,所以设备树文件也很多,那引入设备树的意义在哪?在于(使用一种专属文件去描述)可以做到芯片描述与内核源码的分离,同时设备树描述的硬件信息更结构化,更清晰易懂。

需要知道的是设备树是集成在 OpenFrame 中的开源项目,并不是 Linux 的原创,现在知道 Linux 设备树中 OF 操作函数命名的由来了吧。

2. 设备树

设备树 Device Tree,故名思意就是由各类设备组成的树,设备树文件叫做 DTS 即DeviceTree Source。DTS 文件采用树形结构语法描述板级设备信息,比如芯片上 CPU 数量,DDR 内存基地址,SPI/I2C 接口连接的设备。

请添加图片描述

设备树用于描述硬件设备,本质就是用于描述芯片外设的寄存器地址,而芯片外设寄存器都是连接在系统总线上的,所以设备树树的主干就是系统总线,如上图。

3. 设备树工具

现在把芯片的描述内容从 Linux 内核源码中分离出来了,并且使用了 .dts 设备树文件格式,所以芯片描述部分代码不能再和 Linux 内核源码一起编译了,或是说不能再用 GCC 去编译了。此时需要一个专门编译器将设备树文件编译成二进制文件,这个编译器叫做 DTC,编译后的二进制文件为 .dtb 格式。

总结:设备树源码文件 DTS,设备树编译器 DTC,DTS编译后的二进制文件DTB。

3.1 DTC 编译器

DTC 编译器和 GCC 一样由 C 语言编写而成,其源码位于内核的 “scripts/dtc” 目录下,该目录下默认是没有 DTC 的可执行文件的,而是编译 Linux 时再编译出可执行文件,所以在 “scripts/dtc” 目录下除了源码还有一个 Makefile 文件,这个用于构建 DTC 源码生成可执行文件。

请添加图片描述

从 Makefile 文件可以看出 DTC 编译器源码有 dtc.c,flattree.c,fstree.c,util.c ,ftdput.c 等文件。

3.2 DTC 使用

如果要使用 DTC 编译 DTS 文件的话只需要进入到 Linux 源码根目录下,执行命令 make dtbsmake all 即可。执行后 Linux 根目录下的主 Makefile 文件就会调用 “scripts/dtc” 目录下的 DTC 可执行文件去编译设备树目录下指定的 .dts 文件并生成 .dtb 文件。

如果只是编译设备树的话建议使用 make dtbs 命令,make dtbs 会编译选中的所有设备树文件。如果只要编译指定的某个设备树,比如全志 F1C200S 的,那可以执行 make suniv-f1c200s-lctech-pi.dtb 命令。

$ make suniv-f1c200s-lctech-pi.dtb
DTC arch/arm/boot/dts/suniv-f1c200s-lctech-pi.dtb

3.3 添加 DTS

打开设备树文件所在的 “arch/arm/boot/dts/” 目录,打开该目录下的 Makefile 文件,可以看到全志 SUNIV 系列芯片相关的设备树文件。

dtb-$(CONFIG_MACH_SUNIV) += \suniv-f1c100s-licheepi-nano.dtb \suniv-f1c200s-lctech-pi.dtb \suniv-f1c200s-popstick-v1.1.dtb

可知只要配置 CONFIG_MACH_SUNIV 选项为 y 后,使用全志 SUNIV 系列芯片的板子对应的 .dts 文件都会被编译为 .dtb 文件。

比如要添加一个新的 SUNIV 系列芯片相关的设备树文件, 只需要新建一个对应的 .dts 文件,再把对应的 .dtb 文件名添加到 dtb-$(CONFIG_MACH_SUNIV) 分支下即可,这样编译设备树的时候就会将对应的 .dts 文件编译为 .dtb 文件。

4. DTS 语法

设备树文件和编程语言一样有一套特定的编写语法规则,编写设备树文件就要遵循这套规则,不建议自己编写完整的设备树,而是复制半导体产商提供的设备树文件再修改定制即可。

4.1 头文件 dtsi

和 C/C++ 一样,DTS 设备树也支持头文件引用,设备树头文件后缀名为 .dtsi,引用头文件使用 #include 语句。

设备树 .dts 可以引用 .dtsi.dts,甚至 C 语言的头文件 .h,语法如下所示。

#include "suniv.dtsi"
#include "suniv.dts"
#include "suniv.h"

dtsi 的主要作用

实际应用中 .dtsi 文件用于描述芯片的核心信息(比如 CPU 架构,主频)以及外设信息(比如 UART,USB,GPIO寄存器地址范围)。芯片产商会把同一个系列芯片共有的外设信息提炼到一个 .dtsi 文件里,差异化部分的内容分布到具体芯片的 .dts 文件,这样可减少代码的冗余。

例如同一系列的芯片他们的 CPU 架构和 CPU 主频肯定是相同的,那这部分信息就可以提炼到一个全系列芯片共有的 .dtsi 文件。

4.2 设备节点

普通的树木由主干,枝条和叶子组成,而设备树由根节点(主干),子节点(枝条),节点属性(叶子)构成,每个节点属性记录着各类设备信息,并按所属关系依层次排列,就像一棵树木一样。

根节点

设备树根节点名称固定使用 / 表示,节点范围用 {} 括号标明,属于根节点的属性或子节点就放置在 {} 内部,如下所示。

/ {............
}

每个设备树文件只有一个根节点,如果引用了别的包含根节点的设备树文件,那这些根节点会合并为一个根节点,内容也会叠加合并为一份。

子节点

设备树子节点名称命名规则为 node-name@unit-address,子节点范围用 {} 括号标明,属于子节点的属性或子节点就放置在 {} 内部,如下所示。

/ {node-name@unit-address {............};
}

其中 node-name 是节点名字,为 ASCII 字符串,可以任意意命名,但是最好能够体现节点的功能,比如 serial@1c25000 就表示这个节点是串口外设。

unit-address 是节点所代表设备的地址或寄存器首地址,如果某个节点没有地址或者寄存器的话 unit-address 可忽略,比如 cpus,soc,cpu@0。

虽然 node-name 代表节点名字,但是完整的节点名字为 node-name@unit-address如果要访问节点要使用完整节点名字,或标签。

子节点标签

还可以给子节点命名标签,规则为 label: node-name@unit-address,其中 label 是节点的标签,而 : 后面的是节点名字 node-name,例如 uart0:serial@1c25000 其中 uart0 就是节点标签。

引入 label 的目的是为了方便访问节点,有了标签可以通过 &label 来访问节点,比如通过 &uart0 就可以访问 serial@1c25000 这个节点,而不用输入完整的节点名字。再比如节点 sram:sram@10000000,节点 label 是 sram,而节点名字就很长了,为 sram@10000000。所以通过 &sram 来访问 sram@10000000 节点要方便得多。

设备树实例

主要观察实例中根节点以及子节点部分的命名以及规则,节点内部涉及到节点的属性相关内容再下一小节讲解。

/ {#address-cells = <1>;#size-cells = <1>;interrupt-parent = <&intc>;clocks {osc24M: clk-24M {#clock-cells = <0>;compatible = "fixed-clock";clock-frequency = <24000000>;clock-output-names = "osc24M";};};cpus {#address-cells = <1>;#size-cells = <0>;cpu@0 {compatible = "arm,arm926ej-s";device_type = "cpu";reg = <0x0>;};};soc {compatible = "simple-bus";#address-cells = <1>;#size-cells = <1>;ranges;sram-controller@1c00000 {compatible = "allwinner,suniv-f1c100s-system-control","allwinner,sun4i-a10-system-control";reg = <0x01c00000 0x30>;#address-cells = <1>;#size-cells = <1>;ranges;};}
}

节点属性

节点属性可以理解为编程语言的变量,可以存储数据。赋值采用 key=value 对的形式,键值对的值可以为空或任意的字节流。

/ {node-name@unit-address {key=value};
}

节点属性支持几种常用的数据类型具体下:

(1) 字符串类型,例如 compatible = “arm,arm926ej-s” 设置 compatible 属性的值为字符串 “arm,arm926ej-s”。

(2) 32 位无符号整数类型,例如 reg = <0x0> 设置 reg 属性的值为整数 0。

(3) 字符串列表类型,例如 compatible = “licheepi,licheepi-nano”, “allwinner,suniv-f1c100s” 设置属性 compatible 的值为 “licheepi,licheepi-nano” 字符串和 “allwinner,suniv-f1c100s” 字符串。

4.3 标准属性

节点是由属性组成,一个节点代表一个设备,不同的设备需要的属性不同,我们可以自定义属性,也可以使用 Linux 支持的标准属性。

compatible 属性

compatible 属性叫 “兼容性” 属性,兼容属性对驱动至关重要,用于设备匹配驱动程序(换句话说是 Linux 内核根据该设备节点的兼容属性为该设备分配一个设备驱动程序)。兼容属性值是一个字符串列表,字符串列表用于选择设备所要使用的驱动程序,具体格式如下。

compatible = "manufacturer,model";

其中 manufacturer 段表示厂商名称,而 model 是指硬件模块对应驱动程序的名称,例如 compatible = “winbond,w25q128” 表示产商是 winbond(华邦),硬件模块是 w25q128 (存储芯片)。

我们知道属性支持字符串列表数据类型,所以 compatible 可存属性值列表,这样设备就有多个兼容属性值,设备将多个兼容值逐个的和 Linux 内核驱动程序匹配,直到有合适的驱动程序。

实例

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

#ifdef CONFIG_OF
static const struct of_device_id i2c_nuvoton_of_match[] = {{.compatible = "nuvoton,npct501"},{.compatible = "winbond,wpct301"},{.compatible = "nuvoton,npct601", .data = OF_IS_TPM2},{},
};
MODULE_DEVICE_TABLE(of, i2c_nuvoton_of_match);
#endif

model 属性

model 属性用于描述设备名称(这里的设备指的是具体设备产品比如电脑,而非芯片外设),该属性属于字符串数据类型,例如 model = “Lichee Pi Nano”。

status 属性

status 属性表示设备状态,设备树使用字符串描述设备状态信息,所以 status 属性的类型也是字符串,可选的状态如下表所示:

status 值描述
“okay”说明设备是可操作(可读可写)的。
“disabled”说明设备当前不可操作(无法读写),但是状态可再转变为可操作的(比如热插拔设备插入以后)详细部分可以查看设备的绑定文档。
“fail”说明设备不可操作(无法读写),设备出现了一系列的错误,并且状态无法再转变为可操作的。
“fail-sss”和 “fail” 相同,sss 部分用于表示检测到的错误内容。

reg 属性

reg 属性的值一般是 <address,length> 对,该属性一般用于描述设备地址空间资源信息或者设备地址(即寄存器)信息,比如描述 UART 寄存器地址范围信息,或者 I2C 器件的设备地址。

其中 address 指的是起始地址,length 则指的是地址长度,reg 属性可以同时存放多组 <address,length> 地址对,每个 <address length> 组合表示一个地址范围,具体格式如下。

reg = <address1 length1 address2 length2 address3 length3 ...>;

下面 serial 设备节点描述了全志 F1C200S 芯片 UART0 相关信息,可以看到 reg 属性中 UART0 的起始地址为 0x01c250,地址长度为 0x400。

uart0: serial@1c25000 {compatible = "snps,dw-apb-uart";reg = <0x01c25000 0x400>;interrupts = <1>;reg-shift = <2>;reg-io-width = <4>;clocks = <&ccu CLK_BUS_UART0>;resets = <&ccu RST_BUS_UART0>;status = "disabled";
};

但是在设置 reg 属性时需要设置几组 <address, length> 地址对,这如何确定?这就是下一节涉及的 #address-cells 和 #size-cells 属性的作用。

#address-cells 和 #size-cells 属性

这两个属性一般是配对使用的,所以一起讲解,这两个属性的类型都是 32 位无符号整形,拥有子节点的设备节点用 #address-cells#size-cells 属性设置其子节点的 reg 属性字长。

#address-cells 属性指定 reg 属性的 address 所占用的字长,#size-cells 属性指定 reg 属性的 length 所占用的字长。

#address-cells = <value>
#size-cells = <value>

注意上一节说过 reg 属性可以同时存放多组 <address,length> 地址对,所以这里的占用字长指的是 reg 属性 address 值或 length 值得个数。

所以 #address-cells 属性指定 reg 属性中 address 的个数,#size-cells 属性指定 reg 属性中 length 的个数。

实例 1

soc {......#address-cells = <1>;#size-cells = <1>;ranges;uart0: serial@1c25000 {compatible = "snps,dw-apb-uart";reg = <0x01c25000 0x400>;interrupts = <1>;reg-shift = <2>;reg-io-width = <4>;clocks = <&ccu CLK_BUS_UART0>;resets = <&ccu RST_BUS_UART0>;status = "disabled";};......
}

实例 2

soc {......#address-cells = <1>;#size-cells = <0>;ranges;uart0: serial@1c25000 {compatible = "snps,dw-apb-uart";reg = <0x01c25000>;interrupts = <1>;reg-shift = <2>;reg-io-width = <4>;clocks = <&ccu CLK_BUS_UART0>;resets = <&ccu RST_BUS_UART0>;status = "disabled";};......
}

ranges 属性

ranges 是一个地址映射/转换表,ranges 属性每个项目由子地址,父地址和地址空间长度这三部分组成:

rangs = <child-bus-address parent-bus-address length>;

(1) child-bus-address,子总线地址空间的物理地址,由父节点的 #address-cells 属性确定此物理地址所占用的字长。
(2) parent-bus-address,父总线地址空间的物理地址,同样由父节点的#address-cells 属性确定此物理地址所占用的字长。
(3) length,子地址空间的长度,由父节点的 #size-cells 属性确定此地址长度所占用的字长。

注意 ranges 属性值可以为空,例如 rangs; 这样,如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换。

对于一些芯片来说,子地址空间和父地址空间完全相同,因此会在其设备树中看到不少设备节点得 ranges 属性为空值。

实例

sram-controller@1c00000 {......#address-cells = <1>;#size-cells = <1>;......sram_d: sram@10000 {......ranges = <0 0x00010000 0x1000>;......};
};

name 属性

name 属性用于记录节点名字,name 属性值为字符串数据类型。不过 name 属性已经被弃用,只能在较老的设备树文件中看见 name 属性,所以了解即可。

device_type 属性

该属性用于描述设备的 FCode,属性值为字符串数据类型,IEEE 1275 会用到此属性。该属性只能用于 cpu 节点或者 memory 节点,但是设备树没有 FCode,所以该属性也被弃用,所以了解即可。

5. 内核兼容检查

普通设备的 compatible 兼容属性用于在 Linux 内核中匹配设备驱动程序,而根节点 / 下的 compatible 兼容属性则用于 Linux 内核检查当前设备树对应的设备类型(注意这里的设备不是指芯片外设,而是完整的硬件产品)。

因为设备树是和硬件设备绑定的,所以 Linux 内核检查设备树的类型就可以知道当前内核是否支持该硬件设备,如果支持则启动 Linux 内核。

以下是全志 F1C200S 芯片的设备树,可以看到根节点的兼容属性为 “licheepi,licheepi-nano” 和 “allwinner,suniv-f1c100s”。

/ {model = "Lichee Pi Nano";compatible = "licheepi,licheepi-nano", "allwinner,suniv-f1c100s";aliases {mmc0 = &mmc0;serial0 = &uart0;spi0 = &spi0;};chosen {stdout-path = "serial0:115200n8";};reg_vcc3v3: vcc3v3 {compatible = "regulator-fixed";regulator-name = "vcc3v3";regulator-min-microvolt = <3300000>;regulator-max-microvolt = <3300000>;};
};

设备检查原理

在 Linux 内核的源码目录 arch/arm/include/asm/mach/ 目录下的 arch.h 文件中定义了 machine_desc 结构体宏定义 DT_MACHINE_START,通过该宏定义可以根据芯片架构指定名称定义一个专有名称的 machine_desc 结构体,并初始化,宏定义如下。

#define DT_MACHINE_START(_name, _namestr)       \
static const struct machine_desc __mach_desc_##_name    \__used                         \__section(".arch.info.init") = {           \.nr     = ~0,               \.name       = _namestr,#endif

宏定义 DT_MACHINE_START 并不完整,因为结构体缺少了 } 括号,实际上还有配套宏定义 MACHINE_END,通过这两个宏定义即可定义完整的结构体。

#define MACHINE_END                \
};

在 Linux 内核的源码目录 arch/arm/mach-xxx 目录下包含各类芯片的设备描述machine_desc 结构体定义,例如全志 F1C200S 芯片对应的 machine_desc 结构体定义如下。

static const char * const suniv_board_dt_compat[] = {"allwinner,suniv-f1c100s",NULL,
};DT_MACHINE_START(SUNIV_DT, "Allwinner suniv Family").dt_compat  = suniv_board_dt_compat,
MACHINE_END

machine_desc 结构体成员变量 .dt_compat 保存着相应设备的兼容值,查看全志 F1C200S 设备树根节点的 compatible 属性值可知与 suniv_board_dt_compat 保存的兼容值相同,因此 Linux 内核支持该设备。

6. 节点追加内容

前面说过,芯片产商会把同一个系列芯片相同的外设信息提炼到一个 .dtsi 文件中,需要这部分信息的 .dts 设备树文件就可以包含这个头文件,这样可减少代码的冗余。

所以如果要添加或修改 .dtsi 头文件中的节点内容,就不能直接在 .dtsi 文件修改,直接修改会影响包含该头文件的其他设备树,那该怎么办呢。

通过前面子节点内容知道可以给子节点命名标签(label),有了节点标签后就可以在 .dts 设备树文件中通过标签访问节点,此时即可体现子节点标签的作用,通过标签访问节点的规则如下:

&label {}

比如现在要修改设备节点 serial@1c25000 的 status 属性,那么在 .dts 文件直接通过它的 uart0 标签访问即可(如果要访问的节点没有标签,先命名标签)。

&uart0 {pinctrl-names = "default";pinctrl-0 = <&uart0_pe_pins>;status = "okay";
};

7. DTS与RootFS

Uboot 启动 Linux 内核的同时会将设备树 .dtb 文件传递给 Linux 内核,Linux 内核会解析出设备树的节点信息,并根据节点名字在根文件系统 /proc/device-tree 目录下创建不同文件夹,再将节点内容保存到这些文件夹下。

我们知道设备树属于树状层级结构,恰好文件系统目录结构也是如此,节点类似文件夹,属性类似于文件夹中的文件,所以 Linux 内核将解析的设备树节点在根文件系统中表示为 文件夹,属性表示为 文件

例如 clocks,soc 属于 / 的子节点,所以它们是文件夹的形式,compatible,#address-cells,size-cells 属于 / 的属性,所以它们是文件的形式,如下图。

请添加图片描述

不仅根节点 / 如此,所有节点都是这样,例如 soc 节点的子节点以文件夹的形式表示,属性以文件的形式表示,如下图。

请添加图片描述

8. 特殊节点

8.1 aliases 节点

aliases 节点的主要作用是给节点定义别名,定义别名的目的就是为了方便访问节点,不过这不常用,现在更多是使用节点标签来访问节点。

8.2 chosen 节点

chosen 节点主要作用是用于 Uboot 向 Linux 内核传递数据,重点用于 bootargs 参数传递。 Uboot 会在 chosen 节点添加 bootargs 属性,并且设置 bootargs 属性值为 bootargs 环境变量的值。

详细查看:

https://blog.csdn.net/WANGYONGZIXUE/article/details/115600699

相关文章:

Linux设备树详解

Linux 设备树详解 Linux 操作系统早期是针对个人电脑设备而开发的操作系统&#xff0c;而个人电脑处理器产商较为单一&#xff08;例如只有 Intel&#xff0c;AMD&#xff09;同时个人电脑产商均使用 Intel 或 AMD 制造的处理器&#xff0c;业界形成了统一的总线/硬件接口标准…...

.netcore grpc服务端流方法详解

一、服务端流式处理概述 客户端向服务端发送请求&#xff0c;服务端可以将多个消息流式传输回调用方和客户端流相反&#xff0c;客户端流发出请求&#xff0c;服务端可以传输一批消息给客户端&#xff0c;直至本次请求响应完全结束。针对文件分段传输下载&#xff0c;该方式非…...

python爬虫数据解析xpath、jsonpath,bs4

数据的解析 解析数据的方式大概有三种 xpathJsonPathBeautifulSoup xpath 安装xpath插件 打开谷歌浏览器扩展程序&#xff0c;打开开发者模式&#xff0c;拖入插件&#xff0c;重启浏览器&#xff0c;ctrlshiftx&#xff0c;打开插件页面 安装lxml库 安装在python环境中的Scri…...

go语言的database/sql结合squirrel工具sql生成器完成数据库操作

database/sql database/sql是go语言内置数据库引擎&#xff0c;使用sql查询数据库&#xff0c;配置datasource后使用其数据库操作方法对数据库操作&#xff0c;如下&#xff1a; package mainimport ("database/sql""fmt"_ "github.com/Masterminds…...

LVS集群和分布式

LVS 一.集群和分布式概念 1.1 集群 在计算机领域&#xff0c;集群早在 1960 年就出现&#xff0c;随着互联网和计算机相关技术的发展&#xff0c;现在 集群这一技术已经在各大互联网公司普及。 1.1.1 集群概念 计算机集群指一组通过计算机网络连接的计算机&#xff0c;它们…...

使用QT可视化设计对话框详细步骤与代码

一、创建对话框基本步骤 创建并初始化子窗口部件把子窗口部件放到布局中设置tab键顺序建立信号-槽之间的连接实现对话框中的自定义槽 首先前面三步在这里是通过ui文件里面直接进行的&#xff0c;剩下两步则是通过代码来实现 二、项目创建详细步骤 创建新项目 为项目命名 为…...

TFTP Server

简介 TFTP&#xff08;Trivial File Transfer Protocol,简单文件传输协议&#xff09;是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议&#xff0c;提供不复杂、开销不大的文件传输服务。端口号为69。 TFTP和FTP的区别 安全性区别 FTP支持登录安全&…...

登录验证码实现

Hutool代码改造 Hutool 有参考文档&#xff1b;很多工具类&#xff1b;把一些功能都封装好&#xff1b;都不用你自己去写&#xff1b;直接调用它的工具类 它这里会详细告诉你引入方式Hutool <dependency><groupId>cn.hutool</groupId><artifactId>hu…...

2. 获取自己CSDN文章列表并按质量分由小到大排序(文章质量分、博客质量分、博文质量分)(阿里云API认证)

文章目录 写在前面步骤打开CSDN质量分页面粘贴查询文章url按F12打开调试工具&#xff0c;点击Network&#xff0c;点击清空按钮点击查询是调了这个接口https://bizapi.csdn.net/trends/api/v1/get-article-score用postman测试调用这个接口&#xff08;不行&#xff0c;认证不通…...

在Windows和MacOS环境下实现批量doc转docx,xls转xlsx

一、引言 Python中批量进行办公文档转化是常见的操作&#xff0c;在windows状态下我们可以利用changeOffice这个模块很快进行批量操作。 二、在Windows环境下的解决文案 Windows环境下&#xff0c;如何把doc转化为docx&#xff0c;xls转化为xlsx&#xff1f; 首先&#xff…...

【网络编程(二)】NIO快速入门

NIO Java NIO 三大核心组件 Buffer&#xff08;缓冲区&#xff09;&#xff1a;每个客户端连接都会对应一个Buffer&#xff0c;读写数据通过缓冲区读写。Channel&#xff08;通道&#xff09;&#xff1a;每个channel用于连接Buffer和Selector&#xff0c;通道可以进行双向读…...

【Vue-Router】嵌套路由

footer.vue <template><div><router-view></router-view><hr><h1>我是父路由</h1><div><router-link to"/user">Login</router-link><router-link to"/user/reg" style"margin-left…...

MySQL索引总结

MySQL索引总结 1.索引的概念、作用与使用场景 本质上就是减少读写磁盘的次数。 索引是一种特殊的文件&#xff0c;包含这对数据表中所有记录的引用指针&#xff0c;可以对表中的一列或多列创建索引&#xff0c;并指定索引的类型&#xff0c;每种类型都有对应数据结构实现。 …...

谷粒商城第十二天-基本属性销售属性管理功能的实现

目录 一、总述 二、前端部分 三、后端部分 四、总结 一、总述 前端的话&#xff0c;依旧是直接使用老师给的。 前端的话还是那些增删改查&#xff0c;业务复杂一点的话&#xff0c;无非就是设计到多个字段多个表的操作&#xff0c;当然这是后端的事了&#xff0c;前端这里…...

利用安全区域的概念解决移动端兼容不同手机刘海的问题

移动端 安全区 在做移动端的项目时&#xff0c;由于不同的手机设备设置的不同&#xff0c;有些手机在上方有刘海的设计&#xff0c;我们需要做适配&#xff0c;即把想要展示的内容放在安全区域内展示。 1.自定义导航栏 在pages.json中修改如下配置 {"path":"…...

数据结构---图

这里写目录标题 图的基本概念和术语基本概念和术语1基本概念和术语2 图的类型定义抽象数据类型定义二级目录二级目录 一级目录二级目录二级目录二级目录二级目录二级目录二级目录 图的基本概念和术语 基本概念和术语1 V代表顶点的有穷非空集合 E代表边的有穷集合 n为顶点 有向…...

励志长篇小说《周兴和》书连载之十八 内外交困搞发明

内外交困搞发明 路灯发出昏黄而惺忪的光影。 周兴和疲惫地从车间出来&#xff0c;拖着沉重的腿爬上几级石阶&#xff0c;准备回到家里去。可走到家门口&#xff0c;他想了想&#xff0c;又折了回去&#xff0c;在车间的一条长条椅子上&#xff0c;他用一块试验用的废料当枕头&…...

web基础入门和php语言基础入门 二

web基础入门和php语言基础入门 二 MySQL入门-续MySQL之数据查询操作MySQL其他知识点 php语言基础入门认识PHPPHP的工作流程安装PHP环境认识一个PHP程序PHP基础知识点进入正题 PHP与WEB交互PHP与MySQL交互总结 MySQL入门-续 MySQL之数据查询操作 WHERE 子句&#xff0c;条件限…...

typeScript 之 Array

工具: PlayGround 源码&#xff1a;GitHub TypeScript 数组简介 在TypeScript中&#xff0c; 使用[]表示数组&#xff0c; 它的结构&#xff1a;let valus: 类型名[] 数据; // 数字 let numList: number[] [1, 2, 3]; // 字符串 let strList: string[] ["hello"…...

【题解】二叉树的前中后遍历

文章目录 二叉树的前序遍历二叉树的中序遍历二叉树的后序遍历 二叉树的前序遍历 题目链接&#xff1a;二叉树的前序遍历 解题思路1&#xff1a;递归 代码如下&#xff1a; void preorder(vector<int>& res, TreeNode* root){if(root nullptr) return;//遇到空节点…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...