Linux驱动学习—设备树及设备树下的platform总线

1、什么是设备树?

设备树是一种描述硬件资源的数据结构。他通过bootloader将硬件资源传给内核,使得内核和硬件资源 描述相对独立。

2、设备树的由来

2.1 平台总线的由来

要想了解为什么会有设备树,设备树是怎么来的,我们就要先来回顾以下在没有设备树之前我们是怎么来写一个驱动程序的。以字符设备驱动代码框架为例,我们一起一起来回顾下。任何的设备驱动的编写,Linux已经为我们打好了框架,我们只需要做完形填空一样填写就可以了。

下面是注册字符设备驱动框架图:

具体过程可以参考下面这篇文章:

Linux驱动学习—字符设备驱动注册详解-CSDN博客

下面是注册杂项设备驱动框架图:

有关杂项设备驱动注册流程可以参考下面这篇:

Linux驱动学习—杂项设备驱动注册-CSDN博客

通过这些框架,我们可以很容易编写我们 的驱动代码,但是,当我们用这个框架非常熟练的时候,我们就会发现虽然这个方法很简单,但是非常不容易扩展,当有很多很多类似的设备的时候,如果我们都是按照这个框架来完成,那就要写很多遍这个流程,但是多个相似设备之间真正差异的地方只有初始化硬件部分,其他步骤的代码基本都是一样的。这样就会造成大量的重复代码。但是,我们在编写驱动代码的时候,要尽量做到代码的复用,也就是一套驱动尽量可以兼容很多设备,如果我们还按照这个来编写就不太符合我们的规则了。

为了实现这个目标,我们就要吧通用的代码和有差异的代码分离出来,来增强我们驱动代码的可移植性。所以,设备驱动分离的思想就应运而生了,在Linux中,我们是在写代码的时候进行分离。分离是把一些不相似的东西放到device.c,把相似的东西放到driver.c,如果有很多相似的设备或者平台,我们只要修改device.c就可以了,这样我们重复性的工作就大大的减少了。这就是平台总线的由来。

2.2 平台总线这个方法有什么弊端呢?(设备树由来)

当我们用这个方法用习惯以后就会发现,假如soc不变,我们每换一个平台,都要修改C文件,并且还要重新编译。而且会在arch/arm/plat-xxx和arch/arm/mach-xxx下面留下大量关于板级细节的代码。并不是说这个方法不好,只是从Linux的发展来看,这些代码相对于Linux内核来说就是“垃圾代码”,而且这些“垃圾代码”非常多。

为了改变这个现状,设备树也就被引进到Linux上了,用来剔除相对内核来说的“垃圾代码”,即用设备树文件来描述这些设备信息,也就是代替device.c文件,虽然拿到了内核外面,但是platform匹配基本不变,并且相比于之前的方法,使用设备树不仅可以去掉大量的“垃圾代码”,并且采用文本格式,方便阅读和修改,如果需要修改部分资源,我们也不用在重新编译内核了,只需要把设备树源文件编译成二进制文件,在通过bootloader传递给内核就可以了。内核对其进行解析和展开得到关于硬件的拓扑图。我们通过内核提供的接口剖获取设备树的节点和属性就可以了。即内核对于同一soc的不同主板,只需要换设备树文件dtb即可实现不同主板的无差异支持,而无需更换内核文件。

3、设备树的基本概念

3.1 为啥叫设备树呢?

因为他的语法结构像树一样,所以管它叫设备树

3.2常用名词解释

<1>DT:Device Tree           //设备树
<2>FDT:Flattened Device Tree//展开设备树//开放固件,设备树起源于OF,所以我们在设备树中可以看到很多of字母的函数
<3>device tree source(dts)  //设备树代码
<4>device tree source includeDTB(dtsi) //更通用的设备树代码,也就是相同芯片但不同平台都可以使用的代码
<5>device tree blob(dtb)    //DTS编译后得到的DTB文件
<6>device tree complier(dtc) //设备树编译器

dtsi:一个 SOC 可以作出很多不同的板子,这些不同的板子肯定是有共同的信息, 将这些共同的信息提取出来作为一个通用的文件,其他的.dts 文件直接引用这个通用文件即可,这个通用文件就是.dtsi 文件,类似于 C 语言中的头文件。

DTS,DTSI,DTB,DTC他们之间的关系如下:

4、设备树基本语法

4.1 设备树基本框架

<1>设备从根节点开始,每个设备都是节点。
<2>节点和节点之间可以相互嵌套,形成父子关系。
<3>设备的属性用key-value对(键值对)来描述,每个属性用分号结束

4.2 设备树语法

4.2.1节点

什么是节点呢?节点就好比一颗大树,从树的主干开始,然后有一节一节的树枝,这个就叫节点。在代码中的节点是什么样子的呢。我们把上面模板的根节点摘出来,如下所示,这个就是根节点。相当于大树的树干。

/{
};//分号

而树枝就相当于设备树的子节点,同样我们把子节点摘出来就是根节点里面的node1和node2,如下所示:

/{  //根节点
    node1//子节点node1
    {
    };
    node2//子节点node2
    {
    };
};//分号

一个树枝是不是也可以继续分成好几个树枝呢,也就是说子节点里面可以包含子子节点。所以child-node1和child-node2是node1和node2的子节点,如下所示:

/{  //根节点
    node1//子节点node1
    {
        child-node1//子子节点
        {
        };
    };
    node2//子节点node2
    {
        child-node2//子子节点
        {
        };
    };
};//分号
4.2.2 节点名称

节点的命名有一个固定的格式。

格式:<名称>[@<设备地址>]

(1)<名称>节点的名称也不是任意起的,一般要体现设备的类型而不是特点的型号,比如网口,应该命名为ethernet,而不是随意起一个,比如111。

(2)<设备地址>就是用来访问该设备的基地址。但并不是说在操作过程中来描述一个地址,其主要用来区分用。

(3)注意事项:A、同一级的节点只要地址不一样,名称是可以不唯一的。

B、设备地址是一个可选选项,可以不写。但为了任意区分和理解,一般是都写的。

4.2.3 节点别名

当我们找一个节点的时候,必须书写完整的节点路径,如果节点名很长,那么我们在引用的时候就十分不方便,所以,设备树允许我们用下面的形式为节点标注引用(起别名)。举例:

uart8:serial@02288000

其中uart8就是这个节点名称的别名,serial@02288000就是节点名称。

4.2.4 节点引用

一般往节点里面添加内容的时候,不会直接把直接添加的内容写到节点里面,而是通过节点的引用来添加。

举例:

&uart8{
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart8>;
    status = "okay";
};

&uart8表示引用节点别名为uart8的节点。并往节点添加以下内容:

    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart8>;
    status = "okay";

注意事项:编译设备树的时候,相同的节点的不同属性信息都会被合并,相同节点的先相同的属性会被重写,使用引用可以避免移植者四处找节点。如dts和dtsi里面都有根节点,但最终会合并成一个根节点。

4.2.5属性
(1)reg属性

reg属性用来描述一个设备的地址范围。格式:

reg=<add1 lenth1 [add2 length2]...>

举例:

serial@02288000{
    reg=<101F2000 0x1000>;//101F2000是起始地址,0x1000是长度
};
(2)#address-cells和#size-cells属性

#address-cells用来设置子节点中reg地址的数量

#size-cells用来设置子节点中reg地址长度的数量

举例:

cpu{
    #address-cells = <1>;//用来设置子节点中reg地址的数量
    #size-cells = <1>;//用来设置子节点中reg地址长度的数量
    serial@02288000{
        reg=<101F2000 0x1000>;//101F2000是起始地址,0x1000是长度
    };
};

其中#address-cells和#size-cells均为1,也就是说我们子节点里面的reg属性里这个寄存器组的起始地址只有一个,长度也只有一个。所以101F2000是起始地址,0x1000是长度。

(3)compatible属性

compatible是一个自负床列表,可以在代码中进行匹配。

举例:

compatible = “led";
(4)status属性

status属性的值类型是字符串,这里我们只要记住两个常用的即可,一个是okay,表示涉笔可以正常使用,一个disable,表示设备不能正常使用。

5、在设备树中添加自定义节点

5.1 命令查看设备树节点

<1> cd /proc/device-tree/下就可看到
<2> cd /sys/firmware/devicetree/base/下就可看到

这是设置uboot环境变量的:

5.2 安装dtc工具

(1)直接make dtbs出现这种情况,说明环境没有配置对,则需要安装dtc工具

(2)安装dtc工具

apt-get install device-tre-compiler

5.3 实验:添加一个节点

如下图,添加一个节点test,对这个节点取别名为test1,然后节点引用&test1,一般往节点里面添加内容的时候,不会直接把直接添加的内容写到节点里面,而是通过节点的引用来添加。所以最终的compatible和status属性是节点引用里面的内容。

添加完之后编译dts,在内核源码路径下输入以下命令即可编译:

make ARCH=arm CROSS_COMPILE=arm-linux-guneabihf-  dtbs

把编译的dtb烧录到开发板,cd /proc/device-tree/目录下可以看到test节点已经生成。cat /proc/devicetree/test/compatible发现是test1234,cat /proc/devicetree/test/status发现是okay。

6、设备树中常见的of操作函数

设备都是以节点的形式“挂”到设备树上的,因此姚秀昂获取这个设备的其他属性信息,必须先获取到这个设备的节点。linux内核实验device_node结构体来描述一个节点,此结构体的第一在文件include/linux/of.h中,如下:

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;
    struct  kobject kobj;
    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
};

节点的属性信息里面保存了驱动所需要的内容,因此对于属性值的提取非常重要,Linux内核中使用结构体property表示属性,此结构体同样定义在include/linux/of.h中,如下:

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

6.1获取设备树文件节点里面资源的步骤

<1>步骤一:查找我们要找的节点。

<2>步骤一:查找我们要找的属性值。

6.2 查找节点常用的of函数

<1>of_find_node_by_path函数

作用:函数通过路径来查找指定的节点。

函数原型:

static inline struct device_node *of_find_node_by_path(const char *path)
参数:
path:带有全路径的节点名,可以使用节点的别名,比如"/test"就是test这个节点的全路径。使用节点别名的路径是"/test1".
返回值:成功就返回找到的节点。失败则返回NULL。
<2>of_get_parent函数

作用:用于获取节点的父节点(如果有父节点的话)。

struct device_node *of_get_parent(const struct device_node *node);
node:要查找的父节点的节点
返回值:找到的父节点。
<3>of_get_next_child函数

作用:用于迭代的查找子节点。

static inline struct device_node *of_get_next_child(const struct device_node *node, 
struct device_node *prev)
参数如下:
node:父节点。
prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为NULL表示从第一个子节点开始。
返回值:找到的下一个子节点。

6.3 查找节点属性常用的of函数

<1>of_get_property函数

作用:用于查找指定的属性。

static inline const void *of_get_property(const struct device_node *node,
                const char *name,
                int *lenp)
参数如下:   
np:设备节点。
name:属性名字。
lenp:属性值的字节数。
返回值:找到的属性。
<2> of_property_read_u8、of_property_read_u16、of_property_read_u32、of_property_read_u64

有些属性只有一个整型值,者四个函数就是用于读取这种只有一个整型值的属性,分别用于读取u8、u16、u32、u64类型属性值,函数原型如下:

static inline int of_property_read_u8(const struct device_node *np,
                       const char *propname,
                       u8 *out_value);
static inline int of_property_read_u16(const struct device_node *np,
                       const char *propname,
                       u16 *out_value);
static inline int of_property_read_u32(const struct device_node *np,
                       const char *propname,
                       u32 *out_value);
static inline int of_property_read_u64(const struct device_node *np,
                       const char *propname, u64 *out_value);
参数如下:
np:设备节点。
proname:要读取的属性名字。
out_value:读取的值。
返回值:0,读取成功,负值,读取失败。
<3>of_property_read_u8_array、of_property_read_u16_array、of_property_read_u32_array、of_property_read_u64_array

这四个函数分别是读取属性中u8、u16、u32、u64类型的数组数据,比如大多数的reg属性都是数组数据,可以使用这4个函数一次读取reg属性中的所有数据。这四个函数的原型如下:

static inline int of_property_read_u8_array(const struct device_node *np,
            const char *propname,u8 *out_values, size_t sz);
static inline int of_property_read_u16_array(const struct device_node *np,
            const char *propname, u16 *out_values, size_t sz);
static inline int of_property_read_u32_array(const struct device_node *np,
            const char *propname, u32 *out_values, size_t sz);
static inline int of_property_read_u64_array(const struct device_node *np,
            const char *propname, u64 *out_values, size_t sz);
参数:
np:设备节点。
proname:要读取的属性名字。
out_values:读取的数组值。
返回值:0,读取成功,负值,读取失败。
<4>of_property_read_string

作用:用于读取属性只呢个字符串值

int of_property_read_string(const struct device_node *np,
                   const char *propname,
                   const char **out_string);
参数:
np:设备节点。
proname:要读取的属性名字。
out_values:读取的字符串值。
返回值:0,读取成功,负值,读取失败。     
 

6.4 实验:把5.3添加的一个节点的值和属性读取出来

 
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
​
struct device_node *test_device_node;
struct property *test_node_property;
int size;
u32 out_values[2]={0};
const char *str=NULL;
​
static int hello_init(void)
{
    int ret = 0;
    printk("hello_init\n");
    
    //查找要查找的节点
    test_device_node = of_find_node_by_path("/test");
    if(test_device_node == NULL) {
        printk("test_device_node find error\n");
        return -1;
    }
    printk("test_device_node name is %s\n",test_device_node->name);//test
    
    //获取compatible属性内容
    test_node_property = of_find_property(test_device_node, "compatible", &size);
    if(test_node_property == NULL) {
        printk("test_node_property find error\n");
        return -1;
    }
    printk("test_node_property name is %s\n",test_node_property->name);//compatible
    printk("test_node_property->value is %s\n",test_node_property->value);//test1234
    
    //获取reg属性内容
    ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
    if(ret < 0) {
        printk("of_property_read_u32_array is error\n");
        return -1;
    }
    printk("out_values[0]  is 0x%08x\n",out_values[0]);//0x020ac000
    printk("out_values[1]  is 0x%08x\n",out_values[1]);//0x00000004
    
    //获取status属性内容
    ret = of_property_read_string(test_device_node, "status", &str);
    if(ret < 0) {
        printk("of_property_read_string is error\n");
        return -1;
    }
    printk("status is %s\n",str);//okay
    
    return 0;
}
​
static void hello_exit(void) 
{
    printk("hello_exit\n");
}
​
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

加载驱动,打印如下:

7、设备树下的platform总线

7.1 传统方法下的platform总线

Linux驱动学习—平台总线模型-CSDN博客

之前这篇文章中是使用传统的方法对平台总线进行学习,什么是传统方法呢,就是硬件设备信息部分写在device.c,驱动部分写在driver.c中。而设备树下的platform总线 则是用设备树文件代替device.c。所以使用设备树的方法在配置好设备树文件后,只需编写driver.c。

7.2 of_iomap函数

作用:of_iomap函数用于直接内存映射,以前我们会通过ioremap函数来完成物理地址到虚拟地址的映射。

函数原型:

void __iomem *of_iomap(struct device_node *node, int index);
参数:
np:设备节点
index:reg属性中要完成内存映射的段,如果reg属性只有一段的话inbdex就设置0。
返回值:经过内存映射后的虚拟内存首地址,如果为NULL的话就表示内存映射失败。

7.3 实验代码

Linux驱动学习—平台总线模型-CSDN博客

直接在上面文章的4.3小节platform driver.c上修改,主要实现的功能就是映射GPIO5的数据寄存器的内存地址,实现对数据寄存器的操作,从而实现对蜂鸣器引脚控制。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h> 
#include <linux/of.h>
#include <linux/of_address.h>
​
struct device_node *test_device_node;
struct property *test_node_property;
int size;
u32 out_values[2]={0};
const char *str=NULL;
unsigned int *vir_gpio_dr;
​
static const of_device_id of_match_table_test[] = {//匹配表
    {.compatible = "test1234"},
};
​
static const platform_device_id beep_id_table ={
    .name = "beep_test",
};
​
/*设备树节点compatible属性与of_match_table_test的compatible相匹配就会进入该函数,pdev是匹配成功后传入的设备树节点*/
int beep_probe(struct platform_device *pdev)
{
    int ret = 0;
    printk("beep_probe\n");
    
    /*
    //查找要查找的节点 pdev是匹配成功后传入的设备树节点,所以不需要用之前的方法进行查找了
    test_device_node = of_find_node_by_path("/test");
    if(test_device_node == NULL) {
        printk("test_device_node find error\n");
        return -1;
    }
    printk("test_device_node name is %s\n",test_device_node->name);//test
    
    //获取compatible属性内容
    test_node_property = of_find_property(test_device_node, "compatible", &size);
    if(test_node_property == NULL) {
        printk("test_node_property find error\n");
        return -1;
    }
    printk("test_node_property name is %s\n",test_node_property->name);//compatible
    printk("test_node_property->value is %s\n",test_node_property->value);//test1234
    */
    
    //获取reg属性内容
    ret = of_property_read_u32_array(pdev->dev.of_node, "reg", out_values, 2);
    if(ret < 0) {
        printk("of_property_read_u32_array is error\n");
        return -1;
    }
    printk("out_values[0]  is 0x%08x\n",out_values[0]);//0x020ac000
    printk("out_values[1]  is 0x%08x\n",out_values[1]);//0x00000004
    
    vir_gpio_dr = of_iomap(pdev->dev.of_node, 0);
    if(vir_gpio_dr == NULL) {
        printk("of_iomap  error\n");
        return -1;
    }
    return 0;
}
​
int beep_remove(struct platform_device *pdev)
{
    pritnk("beep_remove \n");
    return 0;
}
​
strcut platform_driver beep_device = {
    .probe = beep_probe,
    .remove = beep_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name  = "123",
        .of_match_table = of_match_table_test,//匹配表 
    },
    .id_table = &beep_id_table,
};
​
static int beep_driver_init(void)
{
    int ret = -1;
    ret = platform_driver_register(&beep_device);
    if(ret < 0) {
        printk("platform_driver_register error \n");
    }
    printk("platform_driver_register ok\n");
    return 0;
}
​
static void  beep_driver_exit(void)
{
    platform_driver_unregister(&beep_device);
    printk("beep_driver_exit \n");
}
​
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");

编译加载驱动,这样寄存器地址就获取成功了,我们可以注册一个杂项设备对数据引脚进行操作高低电平,从而实现对蜂鸣器的操作。

*vir_gpio_dr |= (1<<1);
*vir_gpio_dr &= ~(1<<1);

7.3.1 匹配优先级

先是platform_driver.driver.of_match_table.compatible,然后是platform_driver.driver.id_table,最后是platform_driver.driver.name

注意:设备树节点compatible属性与of_match_table_test的compatible相匹配就会进入probe函数,其函数参数pdev是匹配成功后传入的设备树节点

7.3.2 reg有多组参数时,of_iomap如何传参

当然,如果reg有多组参数的话,这一个是不一样的,举个例子:

 

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

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

相关文章

网络安全—模拟IP代理隐藏身份

文章目录 网络拓扑安装使用代理服务器设置隐藏者设置 使用古老的ccproxy实现代理服务器&#xff0c;仅做实验用途&#xff0c;禁止做违法犯罪的事情&#xff0c;后果自负。 网络拓扑 均使用Windows Server 2003系统 Router 外网IP&#xff1a;使用NAT模式 IP DHCP自动分配或者…

提升软件质量与效率:UI自动化测试的重要性

在软件开发领域&#xff0c;UI自动化测试工具被广泛应用&#xff0c;其意义不仅仅体现在节省时间和资源上&#xff0c;更关系到软件质量的提升、团队效率的增加&#xff0c;以及用户体验的改善。本文将探讨使用UI自动化测试工具的重要性&#xff0c;以及它在软件开发生命周期中…

IDEA生成jar包

一、打开项目结构管理界面 英文版可以使用Ctrl Alt Shift S 打开 Project Structure 窗口 如下图 汉化idea 在设置中 tips&#xff1a;idea汉化包如果不能下载的话&#xff0c;可以手动下载安装 1、先确认自己安装的idea版本 2、来这里Chinese (Simplified) Language Pack…

一篇了解springboot3请求参数种类及接口测试

SpringBoot3数据请求&#xff1a; 原始数据请求&#xff1a; //原始方式RequestMapping("/simpleParam")public String simpleParam(HttpServletRequest request){//获取请求参数String name request.getParameter("name");String age request.getParame…

【备忘】今天写一下如何买免费证书

使用场景 使用微信支付宝支付转账时小游戏小程序接口开发时其它情况 开发中不可避免的会接触https&#xff0c;有的公司有运维去做这个事&#xff0c;有的是老板自己会搞https证书&#xff0c;咱多了解一项技术也是好事。 如何买证书 登录阿里云控制台&#xff0c;搜索ssl证…

transformers Trainer自定义optimizer和scheduler

1.需求 我自定义了一个evaluate方法&#xff0c;想在每一轮训练过后都执行一次。如果只是在TrainingArguments里设置warmup_steps100&#xff0c;那么每轮都会重置学习率&#xff0c;也就是每一轮开始的时候都会按照warmup刚开始的学习率进行训练&#xff0c;这就很头疼。 2.…

Android App从备案到上架全过程

不知道大家注意没有,最近几年来,新的移动App想要上架是会非常困难的,并且对于个人开发者和小企业几乎是难如登天,各种备案和审核。但是到底有多难,或许只有上架过的才会有所体会。 首先是目前各大应用市场陆续推出新的声明,各种备案截止日期到12月就要到最后期限责令整改…

MT8766安卓核心板规格参数_MTK8766核心板模块方案定制

MT8766安卓核心板&#xff1a;高性能、稳定可靠、集成度高的一体化解决方案 MT8766安卓核心板采用联发科MTK8766四核4G模块方案&#xff0c;是一款高度集成的安卓一体板。四核芯片架构&#xff0c;主频可达到2.0GHz&#xff0c;支持国内4G全网通。12nm制程工艺&#xff0c;支持…

全国计算机等级考试| 二级Python | 真题及解析(6)

全国计算机等级考试二级Python真题及解析(8)图文 一、选择题 1.python中表达式4**3=( )。 A.12 B.1 C.64 D.7 2.在Python中,通过( )函数查看字符的编码。 …

学生公寓安全用电管理系统应用案例

摘要&#xff1a;安全用电是学校公寓用电管理的首要任务&#xff0c;这就需要对一些恶性负载进行识别和控制&#xff0c;同时为了减少电工和后期管理人员的成本&#xff0c;引进了安全用电管理系统。本文在在描述了安全用电管理系统的工作原理和利用智能电表可实现的功能后,阐明…

B端产品经理学习-B端产品系统调研的工具

系统性调研目标的工具 系统性调研的目标 相对于背景调研&#xff0c;系统行调研是对公司可控因素&#xff08;公司内部&#xff09;和直接作用力&#xff08;消费者、竞争者&#xff09;进行的调研。系统性调研需要输出结论&#xff0c;为达成产品或公司的战略目标而制定行动的…

Dockerfile与DockerCompose

Docker的Image结构是怎样的&#xff1f; 镜像是将应用程序 及其需要的 系统函数库、环境、配置、依赖 打包而成。 镜像结构 入口&#xff08; Entrypoint &#xff09; 镜像运行入口&#xff0c;一般是程序启动的脚本和参数 层&#xff08; Layer &#xff09; 在BaseImage基…

Spring-IOC综述

文章迁移自语雀。 怎么查看spring的文档 ioc综述 说到spring的ioc,其实就是控制反转,为啥需要控制反转呢,其实是为了功能的增强,如果不用spring, 我们直接使用工厂方法,静态工厂方法, 都是是可以获取到对象的,但是如果需求变了,我们在类的生成时,添加了很多信息,使用工厂就不…

认真学SQL——MySQL入门之DQL多表查询

多表查询 本质: 把多个表通过主外键关联关系连接(join)合并成一个大表,再去查询 知识点&#xff1a; 外键 foreign key 外键概念: 在从表(多方)创建一个字段&#xff0c;引用主表(一方)的主键,对应的这个字段就是外键。 外键特点&#xff1a; 1:从表外键的值是对主表主键…

ubuntu安装vim报Package vim has no installation candidate

解决办法: sudo apt-get update sudo apt-get upgrade 然后再安装vim即可 sudo apt install vim fr:hunkxu

Java基础-----Date类(二)

文章目录 1. LocalDate:获取本地日期2. 单独获取日期时间类中的每个值3. 使用给定值修改日期4. 设置日期和时间的偏移量5. Instant类6. DateTimeFormatter格式化和解析6.1 将LocalDate转换成字符串String格式6.2 将时间戳转换成字符串String格式6.3 将字符串解析成日期6.4 将字…

爬虫与反爬-localStorage指纹(某易某盾滑块指纹检测)(Hook案例)

概述&#xff1a;本文将用于了解爬虫中localStorage的检测原理以及讲述一个用于检测localStorage的反爬虫案例&#xff0c;最后对该参数进行Hook断点定位 目录&#xff1a; 一、LocalStorage 二、爬虫中localStorage的案例&#xff08;以某盾滑块为例&#xff09; 三、如何…

安装beego

执行安装命令 go get github.com/astaxie/beego 报错 换一条执行命令 go install github.com/beego/bee/v2latest 查看是否执行成功 bee version cd 到项目目录上&#xff0c;创建项目 bee new 项目名称 成功&#xff01;

无人机低空视角:针对人群密集场景的检测、跟踪和计数技术

无人机低空视角&#xff1a;针对人群密集场景的检测、跟踪和计数技术 DroneCrowdPaper简介数据集ECCV2020挑战DroneCrowd&#xff08;完整版&#xff09; DroneCrowd Paper 无人机在人群中的检测、跟踪和计数&#xff1a;基准研究。 简介 本文提出了一种时空多尺度注意力网络…

关于标准那些事——第六篇 四象之“朱雀”(要素的表述)

两仪生四象——东方青龙&#xff08;木&#xff09;、西方白虎&#xff08;金&#xff09;、南方朱雀&#xff08;火&#xff09;、北方玄武&#xff08;水&#xff09; 分别对应标准编写之四象——层次的编写、要素的编写、要素的表述、格式的编排。 今天来分享一下 要素的表…