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

Linux系统中驱动入门设备树DTS(经典)

      

设备树DTS:device tree source),字面意思就是一块电路板上设备如上图中CPU、DDR、I2C、GPIO、SPI等,按照树形结构描绘成的一棵树。按照策略功能分离的思路,就是驱动代码(功能)和设备树DTS配置文件(策略)分开来进行设计,这样针对不同的电路板,Linux驱动代码就不用动了,只需要改改DTS就可以,DTS中的配置会决定哪些驱动去运行。

    Linux相关知识在嵌入式领域中很重要,要学习可以找一个能运行Linux代码的环境,最好有一个开发板,也可以用qemuubuntu上运行。

1. 设备树起源

    在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx中,如果外设发生相应的变化,那么驱动代码就需要改动。

    2011年,Linux之父Linus Torvalds发现这个问题后,就通过邮件向ARM-Linux开发社区发了一封邮件,不禁的发出了一句“This whole ARM thing is a f*cking pain in the ass”。之后,ARM社区就引入了PowerPC等架构已经采用的设备树(Flattened Device Tree)机制,将板级信息内容都从Linux内核中分离开来,用一个专属的文件格式来描述,即现在的.dts文件

    从 3.x 版本之后开始支持使用设备树,这样做的意义重大,可以实现驱动代码与设备的硬件信息相互的隔离,减少了代码中的耦合性。通过设备树对硬件信息的抽象,驱动代码只要负责处理逻辑,而关于设备的具体信息存放到设备树文件中,这样,如果只是硬件接口信息的变化而没有驱动逻辑的变化,开发者只需要修改设备树文件信息,不需要改写驱动代码。

    设备树由一系列被命名的节点(Node)和属性(Property)组成,而节点本身可包含子节点。在设备树中,可描述的信息包括:

  • CPU的数量和类别。

  • 内存基地址和大小。

  • 总线和桥。

  • 外设连接。

  • 中断控制器和中断使用情况。

  • GPIO控制器和GPIO使用情况。

  • 时钟控制器和时钟使用情况。

    基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

2. 基本概念介绍

图片

2.1 dts

    dts(device tree source设备树源文件)文件是一种ASCII文本格式的设备树描述文件,此文件适合人类阅读,主要是给用户看的。

    硬件的相应信息都会写在.dts为后缀的文件中,每一款硬件可以单独写一份xxxx.dts,一般在Linux源码中存在大量的dts文件,对于 arm 架构可以在arch/arm/boot/dts找到相应的dts,另外mips则在arch/mips/boot/dts,powerpc在arch/powerpc/boot/dts。

对于imx6ull开发板

arch/arm/boot/dts/100ask_imx6ull_qemu.dts

dts中一般会包一个公共部分的dtsi文件,如下:

#include "imx6ull.dtsi"

2.2 dtsi

    值得一提的是,对于一些相同的dts配置可以抽象到dtsi文件中,然后类似于 C 语言的方式可以include到dts文件中,对于imx6ull开发板arch/arm/boot/dts/imx6ull.dtsi

    对于同一个节点的设置情况,dts中的配置会覆盖dtsi中的配置。具体如下图所示;

图片

2.3 dtc

    dtc编译dts的工具,可以在Ubuntu系统上通过指令apt-get install device-tree-compiler安装dtc工具,不过在内核源码scripts/dtc路径下已经包含了dtc工具;

2.4 dtb

    dtb(Device Tree Blob),dts经过dtc编译之后会得到dtb文件,dtb通过Bootloader引导程序加载到内核。所以Bootloader需要支持设备树才行;Kernel 也需要加入设备树的支持;

dtb文件布局如下:

图片

从上图可以看出,DTB文件主要包含四部分内容:

  1. struct ftdheader:用来表明各个分部的偏移地址,整个文件的大小,版本号等;

  2. memory reservation block:在设备树中使用/memreserve/ 定义的保留内存信息;

  3. structure block:保存节点的信息,节点的结构;

  4. strings block:保存属性的名字,单独作为字符串保存;

    dtb文件代码级别的解析可以参考:

https://cloud.tencent.com/developer/article/1887823

(1) dtb 文件的结构图如下:

图片

 (2) 设备节点的结构图如下:

图片

2.5 DTB加载及解析过程

U-Boot处理如下:

图片

3. DTS基本框架

    下图是一个设备树文件的基本架构;大概看了一下有点类似于XML文件,简单概括一下有这几个部分;

图片

一个例子:

1 个双核ARM Cortex-A932 位处理器;ARM 本地总线上的内存映射区域分布有

两个串口(分别位于0x101F1000和0x101F2000)

GPIO控制器(位于0x101F3000)

SPI控制器(位于0x10170000)

中断控制器(位于0x10140000)

外部总线桥上连接的设备如下:

SMC SMC91111以太网(位于0x10100000)

I2C控制器(位于0x10160000)

64MB NOR Flash(位于0x30000000)

外部总线桥上连接的 I2C 控制器所对应的 I2C 总线上又连接了Maxim DS1338实时钟(I2C 地址为0x58)具体如下图所示;

图片

一个移植网卡的例子:

    比如dm9000网卡,就需要首先将示例信息挂接到我们的板级设备树上,并根据芯片手册和电路原理图将相应的属性进行配置,再配置相应的驱动。需要注意的是,dm9000的地址线一般是接在片选线上的,所以设备树中就应该归属与相应片选线节点,我这里用的exynos4412,接在了bank1,所以是"<0x50000000 0x2 0x50000004 0x2>"

最终的配置结果是:

图片

然后make menuconfig勾选相应的选项将dm9000的驱动编译进内核。

[*] Networking support  --->        Networking options  --->                <*> Packet socket                <*>Unix domain sockets                 [*] TCP/IP networking                [*]   IP: kernel level autoconfigurationDevice Drivers  --->        [*] Network device support  --->                [*]   Ethernet driver support (NEW)  --->                        <*>   DM9000 supportFile systems  --->        [*] Network File Systems (NEW)  --->                <*>   NFS client support                [*]     NFS client support for NFS version 3                [*]       NFS client support for the NFSv3 ACL protocol extension                [*]   Root file system on NFS

执行make uImage;make dtbs,tftp下载,成功加载nfs根文件系统并进入系统,表示网卡移植成功

详细语法参考:https://www.cnblogs.com/xiaojiang1025/p/6131381.html

4. 修改DTS试验

4.1 dts修改

    修改设备树文件

arch/arm/boot/dts/100ask_imx6ull_qemu.dts,添加一个我们自己的模块dts_tree1:

图片

    修改完成后执行make dtbs 重新编译设备树文件,编译完成后arch/arm/boot/dts/100ask_imx6ull_qemu.dtb,将其下载到芯片中。

或者用qemu运行的时候,修改参考指向这个新的dtb文件。

查看设备树节点进入内核,执行

ls  /proc/device-tree/

我们会发现刚刚创建的设备树节已经存在了

图片

图片

    跟我们在dts里面修改的一样,这里变成了一个个的文件形式。文件的名字是属性的名字,内容是值。

具体看看节点的内容,执行

图片

4.2 内核中添加驱动模块

参考:Linux驱动实践:带你一步一步编译内核驱动程序 - IOT物联网小镇 - 博客园

在/drivers文件夹下创建dts_test文件夹,然后创建Kconfig文件

Bash
 config  DTS_TEST
         tristate "dts test"
         default y
         help
             This is the dts test

创建Makefile文件

JavaScript
 obj-$(CONFIG_DTS_TEST) += dts_test.o

在drivers文件夹下的Kconfig和Makefile文件中分别添加

C
 source  "drivers/dts_test/Kconfig"
 obj-$(CONFIG_DTS_TEST)                += dts_test/

创建dts_test.c文件

C++
 #include  <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 
 #include <linux/miscdevice.h>
 #include <linux/fs.h>
 #include <linux/errno.h>
 #include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/gpio_keys.h>
 #include <linux/of_irq.h>
 #include <linux/gpio.h>
 #include <linux/property.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <linux/slab.h>
 #include <linux/device.h>
 
 #define DRIVER_NAME "imx6ul,dts-tree"
 
 
 static int devtree_probe(struct platform_device * pdev)
 {
     struct fwnode_handle *child;
     const char *p1,*p2[3];
     u32 p3[2],value;
     u8 testmac[6];
     int i=0;
 
     printk(KERN_INFO  "\n**********devtree_probe******************\n");
 
      device_property_read_string(&pdev->dev,"test-string",&p1);
     printk("devtree_probe  node  test-string is: %s\n",p1);
 
      device_property_read_string_array(&pdev->dev,  "test-strings", p2, 3);
     printk("devtree_probe node  test-strings is: %s%s%s\n",p2[0],p2[1],p2[2]);
 
      device_property_read_u32(&pdev->dev,"test-u32",&value);
     printk("devtree_probe node  test-u32 is: <%d>\n",value);
 
      device_property_read_u32_array(&pdev->dev,  "test-u32s", p3, 2);
     printk("devtree_probe node  test-u32s is: <%d>,<%d>\n",p3[0],p3[1]);
 
      device_property_read_string(&pdev->dev,"compatible",&p1);
     printk("devtree_probe node  compatible is: %s\n",p1);
 
      device_property_read_string(&pdev->dev,"status",&p1);
     printk("devtree_probe  node  status is: %s\n",p1);
 
     printk(" \n * devtree_probe  child node \n");
 
      device_for_each_child_node(&pdev->dev, child){
 
         printk("*************childnode%d*************\n",i++);     
          fwnode_property_read_string(child,"test-string",&p1);
         printk("childnode  test-string is: %s\n",p1);
 
          fwnode_property_read_string_array(child,"test-strings",p2,3);
         printk("childnode  test-strings is:  %s%s%s\n",p2[0],p2[1],p2[2]);
 
          fwnode_property_read_u32_array(child,"test-u32",&value,1);
         printk("childnode test-u32  is: <%d>\n",value);
 
          fwnode_property_read_u32_array(child,"test-u32s",p3,2);
         printk("childnode  test-u32s is:  <%d>,<%d>\n",p3[0],p3[1]);
 
          fwnode_property_read_u8_array(child,"test-u8s",testmac,6);
         printk("childnode  test-u32s is:  [%x,%x,%x,%x,%x,%x]\n",testmac[0],testmac[1],testmac[2],testmac[3],testmac[4],testmac[5]); 
 
     }
 
     return 0;
 }
 static int devtree_remove(struct platform_device * pdev)
 {
     printk(KERN_INFO  "devtree_remove\n");
 
     return 0;
 }
 
 static const struct of_device_id of_devtree_dt_match[] = {
     {.compatible = DRIVER_NAME},
     {},
 };
 
 MODULE_DEVICE_TABLE(of,of_devtree_dt_match);
 
 static struct platform_driver devtree_test_driver = {
     .probe  = devtree_probe,
     .remove = devtree_remove,
     .driver = {
         .name = DRIVER_NAME,
         .owner = THIS_MODULE,
         .of_match_table = of_devtree_dt_match,
         },
 };
 
 
 static int devtree_test_init(void)
 {
     int num=0,i=0,value;
     const char *p1;
     struct device_node  *node1,*childnode1;
     u32 p2[2];
     u8 testmac[6];
    
     pr_warn(KERN_INFO  "^^^^^^^^^^^^^^^^^^^devtree_test_init^^^^^^^^^^^ \n");
     printk(KERN_INFO  "^^^^^^^^^^^^^^^^^^^devtree_test_init^^^^^^^^^^^ \n");
     printk("\n*************devtree  init start ***************\n");
 
     node1 =  of_find_node_by_path("/dts-tree1");
     if(node1 == NULL){
         printk("of_find_node_by_path  failed\n");
         return -ENODEV;
     }
     else{
          printk("of_find_node_by_path dts-tree1 ok\n");
     }
     //read string
     of_property_read_string(node1,  "test-string", &p1);
     printk("dts-tree1 node  :test-string is: %s\n",p1);
     //read strings
     num =  of_property_count_strings(node1, "test-strings");
     printk("dts-tree1 node  test-strings num is: %d\n",num);
     for(i=0;i<num;i++){
          of_property_read_string_index(node1,"test-strings",i,&p1);
         printk("%s",p1);
     }
     //read string  "compatible"
     of_property_read_string(node1,  "compatible", &p1);
     printk("dts-tree1 node  compatible is: %s\n",p1);
 
     //read string "status"
     of_property_read_string(node1,  "status", &p1);
     printk("dts-tree1 node   status is: %s\n",p1);
 
     //read u32 "test-u32"
      of_property_read_u32(node1,"test-u32",&value);
     printk("dts-tree1 node  test-u32 is: <%d>\n",value);
 
     //read  u32s test-u32s
     of_property_read_u32_array(node1,  "test-u32s", p2, 2);
     printk("dts-tree1 node  test-u32s is:  <%d>,<%d>\n",p2[0],p2[1]);
 
     //read u8s test-u8s
     of_property_read_u8_array(node1,  "test-u8s", testmac, 6);
     printk("dts-tree1 node  test-u8s is:  <%x>,<%x>,<%x>,<%x>,<%x>,<%x>\n",testmac[0],testmac[1],testmac[2],testmac[3],testmac[4],testmac[5]);
 
     //get "dts_child_node1"  device node 
     childnode1 =  of_get_child_by_name(node1,"dts_child_node1");
     if(childnode1 == NULL){
          printk("of_get_child_by_name failed\n");
         return -ENODEV;
     }
     printk("of_get_child_by_name  dts_child_node1 ok\n");
     of_property_read_string(childnode1,  "test-string", &p1);
     printk("dts_child_node1 node  test-string is: %s\n",p1);
 
 
     return  platform_driver_register(&devtree_test_driver);
 }
 
 static void devtree_test_exit(void)
 {
     printk(KERN_INFO  "\ndevtree_test_exit\n");
      platform_driver_unregister(&devtree_test_driver);
 }
 
 module_init(devtree_test_init);
 module_exit(devtree_test_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("zheng");

Kconfig中是y,这样系编译运行后,会直接看到打印:

图片

4.3 常用OF API

    linux 内核中和设备树相关的函数内核关于设备树的驱动都放在/drivers/of下,用户可以使用这里面的函数对设备树进行操作。

图片

后记:

    入门系列还是比较适合公众号,一些零碎的拓展知识面学习。总感觉这种查询式的学习不利于系统的掌握一项技能,可以这么说:高手都不是这么学习的。答案估计是实际的工作经验+看书。系统深入的学习技术首先科班出身很重要,例如计算机专业四大专业课,然后就是对计算机经典书籍的阅读。

    在工作中可能遇到问题了,查点资料,感觉可能也就学了点奇巧淫技,这里需要把经验性的知识理论性的知识区分开,这就是科学技术的区别,经验性的知识没有师傅带就完蛋了,可以多寻求帮助,但是理论性的东西就需要看书,愿大家能沉下心来找点经典大部头书“读上那么一读,确实挺不错的”

相关文章:

Linux系统中驱动入门设备树DTS(经典)

设备树&#xff08;DTS:device tree source&#xff09;&#xff0c;字面意思就是一块电路板上设备如上图中CPU、DDR、I2C、GPIO、SPI等&#xff0c;按照树形结构描绘成的一棵树。按照策略和功能分离的思路&#xff0c;就是驱动代码&#xff08;功能&#xff09;和设备树DTS配置…...

关系型数据库与非关系型数据库类比

关系型数据库和非关系型数据库都有多种不同类型&#xff0c;每种类型都针对不同的数据存储需求和使用场景。以下是一些常见的关系型数据库和非关系型数据库类型&#xff1a; 关系型数据库类型&#xff1a; MySQL&#xff1a; 一种开源的关系型数据库管理系统&#xff0c;用于处…...

Ubuntu入门03——Ubuntu用户操作

1.Ubuntu如何进入root用户 进入ROOT用户的指令&#xff1a; Linux用su命令来切换用户&#xff1a; su root执行命令后&#xff0c;会提示你输入密码&#xff0c;而Ubuntu是没有设置root初始密码的。 若su命令不能切换root&#xff0c;提示su: Authentication failure&#x…...

输出图元(四)8-1 图元、屏幕坐标、指定二维世界坐标系统

用于图形应用的通用软件包称为计算机图形应用编程接口(CCAPI)它提供可以在C等程序设计语言中用来创建图形的函数库。如第3 章所指出的&#xff0c;函数库可以分成几种类型。创建图形时最先要做的一件事就是要描述显示场景的组成部分。图形的组成部分可以是树木和地形家具和墙壁…...

机器学习---决策树的划分依据(熵、信息增益、信息增益率、基尼值和基尼指数)

1. 熵 物理学上&#xff0c;熵 Entropy 是“混乱”程度的量度。 系统越有序&#xff0c;熵值越低&#xff1b;系统越混乱或者分散&#xff0c;熵值越⾼。 1948年⾹农提出了信息熵&#xff08;Entropy&#xff09;的概念。 从信息的完整性上进⾏的描述&#xff1a;当系统的有序…...

java解析json

1. 解析根节点为“{}”的json {"id": 1525490,"name": "有缘网" }代码&#xff1a; String jsonString "{\"id\":1525490\",\"name\":\"有缘网\"}";JSONObject jsonObject JSONObject.…...

PAT 1163 Dijkstra Sequence

个人学习记录&#xff0c;代码难免不尽人意。 Dijkstra’s algorithm is one of the very famous greedy algorithms. It is used for solving the single source shortest path problem which gives the shortest paths from one particular source vertex to all the other v…...

嵌入式学习之进程

1.进程间通信概述 UNIX系统IPC是各种进程通信方式的统称。 2.管道通信原理 特点&#xff1a; 1.它是半双工的&#xff08;即数据只能在一个方向上流动&#xff09;&#xff0c;具有固定的读端和写端。 2.它只能用于具有亲缘关系的进程之间通信&#xff08;也是父子进程或者…...

C#-单例模式

文章目录 单例模式的概述为什么会有单例模式如何创建单例模式1、首先要保证&#xff0c;该对象 有且仅有一个2、其次&#xff0c;需要让外部能够获取到这个对象 示例通过 属性 获取单例 单例模式的概述 总结来说&#xff1a; 单例 就是只有 一个实例对象。 模式 说的是设计模式…...

WSNs 安全技术

WSNs 多用于军事&#xff0c;特殊现场的警戒保护、商业区域的安防&#xff0c;作为任务型网 络&#xff0c;不仅要进行数据传输&#xff0c;而且要进行数据采集和融合&#xff0c;任务的协同控制等&#xff0c;如何 保证任务执行的机密性&#xff0c;数据产生的可靠性数据融合…...

H5如何做页面下拉刷新和上拉加载

这里以vant为例 结构 <van-pull-refreshv-model"isLoading"success-text"刷新成功"refresh"onRefresh"><van-liststyle"height:100%"v-model"loading":finished"finished"finished-text"没有更多了…...

Camunda 7.x 系列【42】事件子流程

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址:https://gitee.com/pearl-organization/camunda-study-demo 文章目录 1. 概述2. 案例演示2.1 流程模型2.2 测试1. 概述 事件子流程是由事件触发的子流程,可存在…...

JVM类的加载过程

加载过程 JVM的类的加载过程分为五个阶段&#xff1a;加载、验证、准备、解析、初始化。 加载   加载阶段就是将编译好的的class文件通过字节流的方式从硬盘或者通过网络加载到JVM虚拟机当中来。&#xff08;我们平时在Idea中书写的代码就是放在磁盘中的&#xff0c;也可以通…...

Jmeter如何设置中文版

第一步&#xff1a;找到 apache-jmeter-5.4.3\bin目录下的 jmeter.properties 第二步:打开 三&#xff0c;ctrf 输入languageen&#xff0c;注释掉&#xff0c;增加以行修改如下 四&#xff0c;ctrs 保存修改内容&#xff0c;重新打开jmeter就可以了...

flutter自定义按钮-文本按钮

目录 前言 需求 实现 前言 最近闲着无聊学习了flutter的一下知识&#xff0c;发现flutter和安卓之间&#xff0c;页面开发的方式还是有较大的差异的&#xff0c;众所周知&#xff0c;android的页面开发都是写在xml文件中的&#xff0c;而flutter直接写在代码里&#xff08;da…...

无涯教程-Android - CheckBox函数

CheckBox是可以由用户切换的on/off开关。为用户提供一组互不排斥的可选选项时,应使用复选框。 CheckBox 复选框属性 以下是与CheckBox控件相关的重要属性。您可以查看Android官方文档以获取属性的完整列表以及可以在运行时更改这些属性的相关方法。 继承自 android.widget.T…...

[Go版]算法通关村第十五关青铜——用4KB内存寻找重复元素

目录 题目&#xff1a;用4KB内存寻找重复元素思路分析&#xff1a;使用位存储如何存储这32000个整数&#xff1f;每个整数对应在位图中的存储状态举例如何判断是重复的&#xff1f;具体的步骤 复杂度&#xff1a;时间复杂度 O ( n ) O(n) O(n)、空间复杂度 O ( 1 ) O(1) O(1)Go…...

OJ练习第159题——消灭怪物的最大数量

消灭怪物的最大数量 力扣链接&#xff1a;1921. 消灭怪物的最大数量 题目描述 你正在玩一款电子游戏&#xff0c;在游戏中你需要保护城市免受怪物侵袭。给你一个 下标从 0 开始 且长度为 n 的整数数组 dist &#xff0c;其中 dist[i] 是第 i 个怪物与城市的 初始距离&#…...

Prometheus-Rules(规则)

文章目录 一、介绍二、配置 Prometheus 使用规则文件三、 规则文件语法规则文件语法全局Recording rules(记录规则)2 Alerting rules(警报规则)3 模板化如何使用四、检查规则文件语法五、发送警报通知一、介绍 Prometheus规则是一种逻辑表达式,可用于定义有关监控数据的逻…...

打卡智能中国(六):村里出了“飞行员”

提起返乡青年&#xff0c;你的第一印象是什么&#xff1f;失败、躺平、卷不动了&#xff1f; 我们在浙江、福建、青海等地&#xff0c;参观一些农业智能化项目时&#xff0c;陪同参观的“飞手”&#xff0c;高兴地跟我们分享自己的心路历程&#xff1a; 在家门口做农业无人机操…...

自动化运维工具Ansible之playbooks剧本

自动化运维工具Ansible之playbooks剧本 一、playbooks1.playbooks简述2.playbooks剧本格式3.playbooks组成部分 二、实例1.编写脚本2.运行playbook3.定义、引用变量4.指定远程主机sudo切换用户5.when条件判断6.迭代7.Templates 模块8.tags 模块9.Roles 模块 三、编写应用模块1.…...

K8S访问控制------认证(authentication )、授权(authorization )、准入控制(admission control )体系

一、账号分类 在K8S体系中有两种账号类型:User accounts(用户账号),即针对human user的;Service accounts(服务账号),即针对pod的。这两种账号都可以访问 API server,都需要经历认证、授权、准入控制等步骤,相关逻辑图如下所示: 二、authentication (认证) 在…...

开开心心带你学习MySQL数据库之第三篇上

学校的项目组有必要加入吗? 看你的初心. ~~如果初心是通过这个经历能够提高自己的技术水平 ~~是可以考虑的 ~~如果初心是通过这个经历提高自己找工作的概率 ~~这个是不靠谱的,啥用没有 ~~如果初心是通过这个体验更美好的大学生活 ~~靠谱的 秋招,应届生,找工作是非常容易的!!! …...

Mysql批量插入大量数据的方法

使用存储过程进行插入&#xff0c; 在navicate中示例如下&#xff1a; 输入需要的参数点击完成 在begin end中输入代码&#xff0c;示例代码如下 CREATE DEFINERskip-grants userskip-grants host PROCEDURE batch_insert() BEGINdeclare i int default 0; set i0;while i<1…...

centos安装nginx实操记录(加安全配置)

1.下载与安装 yum -y install nginx2.启动命令 /usr/sbin/nginx -c /etc/nginx/nginx.conf3.新建配置文件 cd /etc/nginx/conf.d vim index.conf配了一个负责均衡&#xff0c;如不需要&#xff0c;可将 server localhost: 多余的去掉 upstream web_server{server localhost…...

【中等】49. 字母异位词分组

原题链接&#xff1a;https://leetcode.cn/problems/group-anagrams 49. 字母异位词分组 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 示例 1: 输入: strs [“…...

Python3 条件控制

Python3 条件控制 Python 条件语句是通过一条或多条语句的执行结果&#xff08;True 或者 False&#xff09;来决定执行的代码块。 可以通过下图来简单了解条件语句的执行过程: 代码执行过程&#xff1a; if 语句 Python中if语句的一般形式如下所示&#xff1a; if conditi…...

IDEA自定义模板

IDEA自定义模板 &#xff08;1&#xff09;定义sop模板 ①在Live Templates中增加模板 ②先定义一个模板的组 ③在模板组里新建模板 ④定义模板 Abbreviation:模板的缩略名称Description:模板的描述Template text:模板的代码片段应用范围。比如点击Define。选择如下&…...

【Unity3D】UI Toolkit简介

1 前言 UI Toolkit 是一种基于 Web 技术的 GUI 框架&#xff0c;是为了解决 UGUI 效率问题而设计的新一代 UI 系统&#xff08;UGUI 的介绍详见→UGUI概述&#xff09;。与 UGUI 不同&#xff0c;UI Toolkit 没有采用 GameObject 的方式&#xff0c;而是参考了 Web 技术的 XML …...

QT 界面相关操作

1> 创建自定义类时需要指定父类 2> 第一个界面的相关操作 #include "widget.h" #include<iostream> //printf #include<QDebug> //qDebuf #include<QIcon> //图标的头文件 using namespace std; //coutWidget::Widget(QWidget *…...