草庐IT

Linux设备树(Device Tree)

Eddy_l 2023-12-17 原文

Linux设备树


设备树Logo

简介


  在Linux3.x版本下,Linux内核中ARM架构的板级信息大量放置在 arch/arm/mach-xxx arch/arm/plat-xxx 文件夹下,例如platform设备、resource、spi_board_info以及各种硬件的platform_data,这些信息对Linux内核来说无关紧要,会造成大量的冗余编码,导致ARM的merge工作量较大。当采用设备树(device tree)之后,许多硬件的细节可以直接透过设备树传递给Linux内核,大大减少了Linux内核的冗余代码量。

  设备树并不是在这时被重新发明,在Linux内核的其他架构如PowerPC,很早便开始使用设备树来对硬件进行描述。

  在Linux内核中ARM相关文件存放位置如下:

  • ARM核心代码存储在 arch/arm 文件夹下;
  • ARM SoC核心架构代码存储在 arch/arm 文件夹下;
  • ARM SoC周边外设模块驱动存储在 drivers 文件夹下;
  • ARM SoC特定代码存储在 arch/arm/mach-xxx 文件夹下;
  • ARM SoC板级代码被移除,由设备树机制来负责传递硬件拓扑和硬件资源信息。

设备树实例

  Linux设备树可描述的信息如下:

  • CPU的数量和类别;
  • SoC内存基地址和大小;
  • SoC内部总线和桥;
  • SoC外设连接;
  • 中断控制器和中断使用情况;
  • 各种外设控制器和外设使用情况;

  在系统上电后,BootLoader会将设备树传递给Linux内核,内核根据识别的树信息展开为platform_device、spi_device等设备,并且这些设备用到的内存、中断、等资源也被传递给内核,内核会将这些资源绑定到相应的设备中。

Device Tree组成与结构

DTS(Device Tree Source)

  .dts文件是一种ASCII文本格式的Device Tree描述。在ARM Linux中,一个.dts文件对应一个ARM的machine,一般被放置在 arch/arm/boot/dts/ 目录。

DTSI(Device Tree Source Include)

  由于一个SoC基本都会对应多个machine,这样便会存在许多共同的部分,Device Tree将一些公用的部分使用.dtsi文件保存,类似于C语言的头文件。特定于machine的.dts文件一般都会引用这个.dtsi文件。

DTC(Device Tree Compiler)

  dtc是编译dts的工具,可以将dts文件转换为二进制.dtb文件。DTC的源代码位于内核的scripts/dtc目录,在Linux内核使能了Device Tree的情况下, 编译内核的时候主机工具dtc会被编译出来。

DTB(Device Tree Block)

  .dtb文件是 .dts 被 DTC 编译后的二进制格式的设备树文件,它可以被linux内核解析。

DTS存储结构

  Device Tree的基本单元是node,这些node会被组装成树结构,除了根节点,每个节点都只有一个父节点。一个设备树中只有一个根节点,每个节点包含若干个属性值来描述节点的一些特性。

  上图设备树实例图的对应.dts文件如下所示

/ {
    module="fsl,mpc8572ds"
    compatible="fsl,mpc8572ds"
    #address-cells=<1>
    #size-cells=<1>

    cpus{
        #address-cells=<1>
        #size-cells=<0>

        cpu@0{
            device_type="cpu"
            reg=<0>
            timebase-frequency=<825000000>
            clock-frequency=<825000000>
        };

        cpu@1{
            device_type="cpu"
            reg=<1>
            timebase-frequency=<825000000>
            clock-frequency=<825000000>
        };
    };

    memory@0{
        device_type="memory"
        reg=<0 0x20000000>
    };

    uart@fe001000{
        compatible="ns16550"
        reg=<0xfe001000 0x100>
    };

    chose{
        bootarg="root=/dev/sda2";
    };

    aliases{
        serial0="/uart@fe001000"
    };
};

  根据上面的.dts文件,可简单概括为以下几个部分:

  • 根节点: \
  • 设备节点:例如: uart@fe001000
    • 节点名称:例如: uart
    • 节点地址:例如: reg=<0xfe001000 0x100>
    • 子节点:例如: cpu@0
  • 属性:属性名称(Property name)和属性值(Property value)
  • 标签:例如 uart@fe001000

设备树语法

头文件

  设备树的头文件引用和C语言类似,使用#include来进行引用,.dts文件可以引用.h、.dts以及.dtsi文件。

\arch\arm\boot\dts\imx6ull.dts
#include <dt-bindings/clock/imx6ul-clock.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "imx6ull-pinfunc.h"
#include "imx6ull-pinfunc-snvs.h"
#include "skeleton.dtsi"

节点

  设备树中的基本单元称为节点(node)

  设备节点格式:

[label:] node-name[@unit-address] 
{
   [properties definitions]
   [child nodes]
};

  node-name 是设备的节点名称,为ASCII字符串,一般节点的名字能清晰描述出节点的功能,例如“uart1”表示这个节点是UART1外设。

  unit-address 一般表示设备的地址活寄存器首地址,如果某个节点没有地址或寄存器的话,此处可省略,例如:“cpu0: cpu@0”。

  label 表示节点标签,冒号后的才是节点的名称。引入标签是为了更方便的访问节点,再次访问节点是可以直接通过&label来访问这个节点。

  {} 里用于节点内容的描述,其中 [properties definitions] 表示节点属性。[child nodes] 是这个挂在设备节点上的子节点。

  例:

ecspi2: ecspi@0200c000 {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
    reg = <0x0200c000 0x4000>;
    interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_ECSPI2>,
            <&clks IMX6UL_CLK_ECSPI2>;
    clock-names = "ipg", "per";
    dmas = <&sdma 5 7 1>, <&sdma 6 7 2>;
    dma-names = "rx", "tx";
    status = "disabled";
};

常用的节点属性

  • compatible

  compatible表示“兼容”,每个节点都会有一个compatible属性,用来标识特定的设备。内核启动时,会根据描述的先后顺序找到可以使用的驱动程序。
  根节点也会有compatible属性,它用来选择哪一个“machine desc”,一个内核可以支持machine A也可以支持machine B,内核启动会根据节点的compatible属性找到相对应的machine desc结构体,执行其中的初始化函数。

  • address-cells、#size-cells

  cell指一个32位的数值,address-cells表示address要用多少个32位来表示;size-cell表示size要用多少个32位数来表示。

  例:

/ {
	#address-cells = <1>;
	#size-cells = <1>;

	memory {
		reg = <0x00000000 0x04000000>,
		    <0x08000000 0x04000000>;
	};
}

  上例中,address-cells为1,所以reg中用1个数来表示地址,即用0x00000000和0x08000000表示内存地址,size-cells为1,所以reg中用1个数表示大小,即用0x04000000表示大小。

  • model

  model属性与compatible属性有些类似,compatible它是一个字符列表,表示可以与硬件兼容A、B、C等驱动;model用来准确定义这个硬件是什么。

例:

/ {
    model = "Freescale i.MX6 ULL DDR3 ARM2 Board";
    compatible = "fsl,imx6ull-ddr3-arm2", "fsl,imx6ull";
}

  在上例中,表示可以兼容imx6ull-ddr3-arm2,也兼容imx6ull。

  • status

  dtsi文件定义了很多设备,若在编写时不希望或者这些设备没有,可以添加status属性,并设备为“disabled”。

描述
“okay”设备正常运行
“disabled”表示该设备目前尚未运行,但将来可能会运行
“fail”表示设备无法运行。在设备中检测到严重错误,需修复
“fail-sss”设备无法运行。在设备中检测到严重错误,需修复,sss表示错误信息
  • reg

  reg属性描述了设备资源在其父总线定义的地址空间内的地址,如果一个节点存在reg属性,那么节点的名字必须包含unit-address,unit-address值来源于reg属性的第一个地址值。

  例:

intc: interrupt-controller@00a01000 {
         compatible = "arm,cortex-a7-gic";
         #interrupt-cells = <3>;
         interrupt-controller;
         reg = <0x00a01000 0x1000>,
               <0x00a02000 0x100>;
     };
  • ranges

  ranges可以理解为地址映射/转换表,其目的主要是显示的把设备地址映射为CPU可以使用的地址。ranges属性值可以为空或者按照(child-bus-address, parent-bus-address, length)格式编写的数字矩阵。

  child-bus-address: 子总线地址空间的物理地址,由子节点的#address-cells确定此物理地址所占用的字长;

  parent-bus-address:父总线地址空间的物理地址,同样由父节点的#address-cells确定此物理地址所占用的字长。

  length:子地址空间的长度,由父节点的#size-cells确定此地址长度所占用的字长。

  如果ranges为空,表示子地址空间和父地址空间一一对应,不需要地址翻译。

注意:如果某个节点缺少ranges,除了其父节点,其他任何节点都不能直接访问。

  例:

crypto: caam@30900000 {
    compatible = "fsl,imx7d-caam", "fsl,sec-v4.0";
    #address-cells = <1>;
    #size-cells = <1>;
    reg = <0x30900000 0x40000>;
    ranges = <0 0x30900000 0x40000>;
    interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX7D_CAAM_CLK>,
        <&clks IMX7D_AHB_CHANNEL_ROOT_CLK>;
    clock-names = "caam_ipg", "caam_aclk";

    sec_jr0: jr0@1000 {
            compatible = "fsl,sec-v4.0-job-ring";
            reg = <0x1000 0x1000>;
            interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
    };

    sec_jr1: jr1@2000 {
            compatible = "fsl,sec-v4.0-job-ring";
            reg = <0x2000 0x1000>;
            interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
    };

    sec_jr2: jr2@3000 {
            compatible = "fsl,sec-v4.0-job-ring";
            reg = <0x3000 0x1000>;
            interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
    };
};

  如上例所示,sec_jr0的地址起始地址为(0x1000-0x0)+0x30900000,大小为0x1000;sec_jr1的地址起始地址为(0x2000-0x0)+0x30900000,大小为0x1000;sec_jr2的地址起始地址为(0x3000-0x0)+0x30900000,大小为0x1000。

/ {
    compatible = "acme,coyotes-revenge";
    #address-cells = <1>;
    #size-cells = <1>;
    interrupt-parent = <&intc>;

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        cpu@0 {
            compatible = "arm,cortex-a9";
            reg = <0>;
        };
        cpu@1 {
            compatible = "arm,cortex-a9";
            reg = <1>;
        };
    };

    serial@101f0000 {
        compatible = "arm,pl011";
        reg = <0x101f0000 0x1000 >;
        interrupts = < 1 0 >;
    };

    serial@101f2000 {
        compatible = "arm,pl011";
        reg = <0x101f2000 0x1000 >;
        interrupts = < 2 0 >;
    };

    gpio@101f3000 {
        compatible = "arm,pl061";
        reg = <0x101f3000 0x1000
               0x101f4000 0x0010>;
        interrupts = < 3 0 >;
    };

    intc: interrupt-controller@10140000 {
        compatible = "arm,pl190";
        reg = <0x10140000 0x1000 >;
        interrupt-controller;
        #interrupt-cells = <2>;
    };

    spi@10115000 {
        compatible = "arm,pl022";
        reg = <0x10115000 0x1000 >;
        interrupts = < 4 0 >;
    };

    external-bus {
        #address-cells = <2>
        #size-cells = <1>;
        ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
                  1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
                  2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash

        ethernet@0,0 {
            compatible = "smc,smc91c111";
            reg = <0 0 0x1000>;
            interrupts = < 5 2 >;
        };

        i2c@1,0 {
            compatible = "acme,a1234-i2c-bus";
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <1 0 0x1000>;
            interrupts = < 6 2 >;
            rtc@58 {
                compatible = "maxim,ds1338";
                reg = <58>;
                interrupts = < 7 3 >;
            };
        };

        flash@2,0 {
            compatible = "samsung,k8f1315ebm", "cfi-flash";
            reg = <2 0 0x4000000>;
        };
    };
};

  由上例可知:external-bus子地址空间的#address-cells为2,父地址空间的#address-cells值为1,因此 0 0 0x10100000 0x10000 的前两个cell为external-bus片选0上相对该片选的基地址移0,第三个cell表示external-bus片选0上地址偏移0的地址空间被映射到0x10100000位置,第四个cell表示映射的大小为0x10000。

  • device_type

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

  例:

cpus {
    #address-cells = <1>;
    #size-cells = <0>;

    cpu0: cpu@A00 {
        device_type = "cpu";
        compatible = "arm,cortex-a9";
        reg = <0xA00>;
        cooling-min-level = <13>;
        cooling-max-level = <7>;
        #cooling-cells = <2>; /* min followed by max */
    };

    cpu@A01 {
        device_type = "cpu";
        compatible = "arm,cortex-a9";
        reg = <0xA01>;
    };
};

中断信息节点

  中断一般包括中断产生设备和中断处理设备。中断控制器负责处理中断,每一个中断都有对应的中断号及触发条件。中断产生设备可能有多个中断源,有时多个中断源对应中断控制器中的一个中断,这种情况中断产生设备的中断源称之为中断控制器中对应中断的子中断。一般情况中断产生设备数量要多于中断控制器,多个中断产生设备的中断都由一个中断控制器处理,这种多对一的关系也很像一个树形结构,所以在设备树中,中断也被描述成树,叫中断树。

  一般使用以下几个部分描述中断:

  • interrupt-controller

  表示中断控制器,其值为空,用来接收中断信号。

  • interrupt-cells

  跟#address-cells和#size-cells类似,用来说明有多少个中断说明符,即interrupt属性的大小,也就是一条信息有几个cells,每个cells都是32位整型。

  • interrupt-parent

  指向interrupt-controller的phandle,如果该节点没有该属性,默认继承其父节点的中断属性。

  • interrupts

  包含了许多中断描述符(interrupt specifier,也就是中断源),比如中断类型(PPI,SPI……)、中断号、中断触发类型等信息。

  例:

intc: interrupt-controller@00a01000 {
    compatible = "arm,cortex-a7-gic";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0x00a01000 0x1000>,
            <0x00a02000 0x100>;
};

soc {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "simple-bus";
    interrupt-parent = <&gpc>;
    ranges;

    ...

    gpmi: gpmi-nand@01806000{
        compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";
        #address-cells = <1>;
        #size-cells = <1>;
        reg = <0x01806000 0x2000>, <0x01808000 0x4000>;
        reg-names = "gpmi-nand", "bch";
        interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
        interrupt-names = "bch";
        clocks = <&clks IMX6UL_CLK_GPMI_IO>,
                <&clks IMX6UL_CLK_GPMI_APB>,
                <&clks IMX6UL_CLK_GPMI_BCH>,
                <&clks IMX6UL_CLK_GPMI_BCH_APB>,
                <&clks IMX6UL_CLK_PER_BCH>;
        clock-names = "gpmi_io", "gpmi_apb", "gpmi_bch",
                    "gpmi_bch_apb", "per1_bch";
        dmas = <&dma_apbh 0>;
        dma-names = "rx-tx";
        status = "disabled";
    };

...

};

  由上例可知,中断控制器为 intc ,其中断信息为 3,在节点gpmi下interrupts为<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,符合中断控制器中断信息的描述。

alias节点

  alias节点用来引用其他节点的节点,一般情况下引用一个节点需要写出器相对根节点的完整路径,当需要新增许多节点时,还需要考虑节点具体的位置,所以这个时候在需要引用的节点前设置一个label,引用的时候只需要&label即可。别名尽量固定,更改真名的情况下,只需要更改alias处即可,不需要更改每一个用到的driver,和typedef定义类型有点类似。

  例:

aliases {
    can0 = &flexcan1;
    can1 = &flexcan2;
    ethernet0 = &fec1;
    ethernet1 = &fec2;
    gpio0 = &gpio1;
    gpio1 = &gpio2;
    gpio2 = &gpio3;
    gpio3 = &gpio4;
    gpio4 = &gpio5;
    i2c0 = &i2c1;
    i2c1 = &i2c2;
    i2c2 = &i2c3;
    i2c3 = &i2c4;
    mmc0 = &usdhc1;
    mmc1 = &usdhc2;
    serial0 = &uart1;
    serial1 = &uart2;
    serial2 = &uart3;
    serial3 = &uart4;
    serial4 = &uart5;
    serial5 = &uart6;
    serial6 = &uart7;
    serial7 = &uart8;
    spi0 = &ecspi1;
    spi1 = &ecspi2;
    spi2 = &ecspi3;
    spi3 = &ecspi4;
    usbphy0 = &usbphy1;
    usbphy1 = &usbphy2;
};

chose节点

  chose节点主要用于传递数据,一般用于传递启动参数。

  例:

chosen {
    bootargs = "console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2";
};

设备树的编译、加载过程图

设备树编译、加载过程

参考

《devicetree-specification-v0.2.pdf》
《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.pdf》
《嵌入式Linux应用开发完全手册》
ARM Linux 3.x的设备树(Device Tree)
Device Tree常用方法解析
设备树详解


🍙

🤔

🥱

有关Linux设备树(Device Tree)的更多相关文章

  1. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  2. ruby-on-rails - 禁用设备的 :confirmable on-the-fly to batch-generate users - 2

    Devise是一个Ruby库,它为我提供了这个User类:classUser当写入:confirmable时,注册时会发送一封确认邮件。上周我不得不批量创建300个用户,所以我在恢复之前注释掉了:confirmable几分钟。现在我正在为用户批量创建创建一个UI,因此我需要即时添加/删除:confirmable。(我也可以直接修改Devise的源码,但我宁愿不去调和它)问题:如何即时添加/删除:confirmable? 最佳答案 WayneConrad的解决方案:user=User.newuser.skip_confirmation

  3. 【Linux操作系统】——网络配置与SSH远程 - 2

    Linux操作系统——网络配置与SSH远程安装完VMware与系统后,需要进行网络配置。第一个目标为进行SSH连接,可以从本机到VMware进行文件传送,首先需要进行网络配置。1.下载远程软件首先需要先下载安装一款远程软件:FinalShell或者xhell7FinalShellxhell7FinalShell下载:Windows下载http://www.hostbuf.com/downloads/finalshell_install.exemacOS下载http://www.hostbuf.com/downloads/finalshell_install.pkg2.配置CentOS网络安装好

  4. Linux磁盘分区中物理卷(PV)、卷组(VG)、逻辑卷(LV)创建和(LVM)管理 - 2

    文章目录一基础定义二创建逻辑卷2-1准备物理设备2-2创建物理卷2-3创建卷组2-4创建逻辑卷2-5创建文件系统并挂载文件三扩展卷组和缩减卷组3-1准备物理设备3-2创建物理卷3-3扩展卷组3-4查看卷组的详细信息以验证3-5缩减卷组四扩展逻辑卷4-1检查卷组是否有可用的空间4-2扩展逻辑卷4-3扩展文件系统五删除逻辑卷5-1备份数据5-2卸载文件系统5-3删除逻辑卷5-4删除卷组5-5删除物理卷六LVM逻辑卷缩容6-1缩容注意事项6-2标准缩容步骤一基础定义LVM,LogicalVolumeManger,逻辑卷管理,Linux磁盘分区管理的一种机制,建立在硬盘和分区上的一个逻辑层,提高磁盘分

  5. ruby - 如何在 Ruby 中获取 linux 系统信息 - 2

    如何在Ruby中获取linux系统(这必须适用于Fedora、Ubuntu等)的软件/硬件信息? 最佳答案 Chef背后的优秀人才,拥有一颗名为Ohai的优秀gemhttps://github.com/opscode/ohai以散列形式返回系统信息,例如操作系统、内核、规范、fqdn、磁盘、空间、内存、用户、接口(interface)、sshkey等。它非常完整,非常好。它还会安装命令行二进制文件(也称为ohai)。 关于ruby-如何在Ruby中获取linux系统信息,我们在Stack

  6. ruby - rbenv:在 Linux Mint 上找不到 gem 命令 - 2

    我在LinuxMint17.2上。我最近使用apt-getpurgeruby​​删除了ruby​​。然后我安装了rbenv然后rbenvinstall2.3.0所以现在,~/.rbenv/versions/2.3.0/bin/ruby存在。但是现在,我无法执行geminstallrubocop。我明白了:$geminstallrubocoprbenv:gem:commandnotfoundThe`gem'commandexistsintheseRubyversions:2.3.0但是我可以~/.rbenv/versions/2.3.0/bin/geminstallrubocop。但是,

  7. ruby - 在 Linux 上编译 Ruby 1.9.2 所需的先决条件? - 2

    我是Ruby和RoR的新手。我有一个带有Ubuntu镜像的干净Linode实例,我想从源代码编译Ruby而不是使用apt-get。我已经在谷歌上搜索了执行此操作的说明,但经过一些尝试后,当我尝试运行一些教程示例时,我不断收到有关缺少zlib和其他一些包的错误。任何人都可以给我详细的说明(或链接),教我如何在从源代码编译Ruby之前安装必要的必备包吗?我的目的是编译Ruby的最新稳定版本,然后安装Rubygems和Rails。提前感谢您的帮助!!! 最佳答案 Thisblogpost涵盖从源代码编译ruby​​所需的包和安装过程;它引

  8. Linux网络编程必备的POSIX API的细节 - 2

    目录POSIXAPI大集合五元组三次握手的过程,内核协议栈分析listen函数DDOS攻击,洪水攻击DDOS攻击的应对措施数据发送 怎么保证顺序?如何保证包地顺序到达(序号+确认应答机制+重传)TCP断开连接的过程问题1.大量的CLOSE_WAIT+FIN_WAIT2是为啥?time_wait状态存在的原因?POSIXAPI大集合五元组(sip,sport,dip,dport,protocol)三次握手的过程,内核协议栈分析内核协议栈中是有内核数据结构的.  我们send/write数据,都是先发送到内核协议栈中,然后由内核协议栈封装发送到物理介质中传输到对端的对端的接收过程也是经有内核协议栈

  9. Linux export 命令及如何删除export设置的环境变量 - 2

    背景:Linuxexport命令用于设置或显示环境变量。在shell中执行程序时,shell会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅限于该次登陆操作。语法:export[-fnp][变量名称]=[变量设置值]参数说明:-f 代表[变量名称]中为函数名称。-n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。-p 列出所有的shell赋予程序的环境变量。实例:列出当前所有的环境变量#export-p//列出当前的环境变量值定义环境变量赋值#exportMYENV=7//定义环境变量并赋值添加环境变量:默认保存在

  10. Linux操作系统CentOS7安装Nginx[详细版] - 2

    Nginx安装1.官网下载Nginx2.使用XShell和Xftp将压缩包上传到Linux虚拟机中3.解压文件nginx-1.20.2.tar.gz4.配置nginx5.启动nginx6.拓展(修改端口和常用命令)(一)修改nginx端口(二)常用命令1.官网下载Nginxhttp://nginx.org/en/download.html这里我下载的是1.20.2版本,大家按需下载对应稳定版即可2.使用XShell和Xftp将压缩包上传到Linux虚拟机中没有XShell可以参考《Linux操作系统CentOS7连接XShell》3.解压文件nginx-1.20.2.tar.gz1)检查是否存

随机推荐