前言

        不管是日常开发还是使用电脑的过程中,我们几乎无法避免与环境变量打交道,这是因为环境变量在系统的运行中发挥着不可或缺的作用,它作为系统的运行时参数,保存了一些进程运行时所需的信息。环境变量顾名思义是一个变量,作为变量所以它是用来存取数据的。但也是并不是什么格式的数据都能存,一般使用键值对的形式来存储字符串,但是一般存储的字符串也不会太长。所以从某种角度讲,说它是一种数据库也不过分。作为开发者,应该没有比对PATH变量更熟悉的了吧。安装一个新的软件并且希望它随处运行,这时候就会想到把它加入到环境变量。毕竟没有谁愿意反复的输入那么长的文件名吧。

一、环境变量的分类

       1.系统环境变量

          区别于用户环境变量,它是全局性的,影响范围更大,设置生效后能对系统的所有用户生效。所以不建议随意更改系统环境变量,因为它是共享的。这个程序修改了可能就影响到了另外的程序,引起不确定的问题发生,一般是使用文件进行存储。

以windows系统为例,系统环境变量存储在注册表位置为HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager\Environment,见下图

容器的环境变量配置 环境容量是变量吗_容器的环境变量配置

linux系统以Centos为例,系统环境变量PATH可以通过修改 /etc/profile文件或者/etc/environment文件来实现

      使用 vi /etc/profile 命令打开/etc/profile文件 后在文件末尾空行追加

      export PATH=/app/root2:$PATH

       

容器的环境变量配置 环境容量是变量吗_java_02

         然后可以使用 source /etc/profile 使之立即生效

       

        2.用户环境变量

          区别于系统环境变量,他是相对局部的,独立的,影响范围更小,设置生效后只对当前用户生效。如果多个进程共享相同的环境变量,修改环境变量可能也会引起一些问题。以windows系统为例,系统环境变量存储在注册表位置为

HKEY_CURRENT_USER\Environment

容器的环境变量配置 环境容量是变量吗_java_03

linux系统以Centos为例,

用户环境变量PATH 可以通过修改 /.bashrc 文件来实现

下面使用vi  ~/.bashrc命令打开~/.bashrc文件并且在文件末尾追加了一行 

export PATH=/app/root:$PATH

容器的环境变量配置 环境容量是变量吗_batch命令_04

修改完成之后可以使用 source ~/.bashrc使其立即生效,关闭shell之后重新开启一个shell窗口也会生效,下图使用 echo $PATH查看环境变量

容器的环境变量配置 环境容量是变量吗_容器的环境变量配置_05

 要注意的是在shell中切换登录用户时会追加重复的路径

容器的环境变量配置 环境容量是变量吗_batch命令_06

        3.进程环境变量

          区别于系统环境变量和用户环境变量,它的作用域更小,它只在进程内有效。所以它的隔离性相对更好,可以认为它的变量是存储在内存中的。

   linux系统以Centos为例,进程环境变量可以在shell中设置

容器的环境变量配置 环境容量是变量吗_batch命令_07

 设置完成后立即生效

二、环境变量在主流编程语言中的用法以及特点

      C#:

               获取所有环境变量:    Environment.GetEnvironmentVariables();

               分别获取系统环境变量、用户环境变量、进程环境变量

  使用Environment.GetEnvironmentVariable

                   

容器的环境变量配置 环境容量是变量吗_环境变量_08

              如上图所示,当系统环境变量、用户环境变量、进程环境变量中存在相同名称的环境变量时该如何取舍呢,很显然,进程环境变量会覆盖用户环境变量,用户环境变量会覆盖系统环境变量。

              分别设置系统环境变量、用户环境变量、进程环境变量

  使用Environment.SetEnvironmentVariable

容器的环境变量配置 环境容量是变量吗_容器的环境变量配置_09

 Java:

            根据名称获取单个环境变量可以使用 System.getenv,遗憾的是没有设置环境变量的方法。

如果确实要获取环境变量,只能调用JNI了。kernel32.dll中的


SetEnvironmentVariableA 和 GetEnvironmentVariableA 两个Windows API函数 可以用来设置和获取环境变量的值


容器的环境变量配置 环境容量是变量吗_环境变量_10

 调用GetEnvironmentVariableA获取环境变量path的值

容器的环境变量配置 环境容量是变量吗_batch命令_11

 调用 SetEnvironmentVariableA 设置环境变量path的值

容器的环境变量配置 环境容量是变量吗_java_12

环境变量的继承性

         因为进程是以树状结构存在,所以存在父子关系。一般的,父进程在创建子进程的时候,如果不指定环境变量,那么子进程将继承父进程的环境变量。

         下面的例子验证了这一点

   上面的例子在父进程中设置了一个进程内变量inheritvar,然后启动了一个基于自身镜像的子进程,子进程的命令行多了一个参数args0,待子进程启动后在子进程中读取进程进程内变量inheritvar

容器的环境变量配置 环境容量是变量吗_batch命令_13

 运行结果如下

     

容器的环境变量配置 环境容量是变量吗_batch命令_14

 说明子进程默认继承了父进程的进程变量

三、环境变量的应用

       1.环境变量与批处理

          环境变量在命令行和批处理中可以说使用是最广泛的了,而且在命令行和批处理中添加修改环境变量都很方便,因为命令行最常用的功能就是创建一个子进程

          使用set命令设置或者查看环境变量,当然这个环境变量属于进程内环境变量

          set 环境变量名可以查看环境变量

容器的环境变量配置 环境容量是变量吗_环境变量_15

          set 环境变量名=环境变量值 可以设置环境变量,比如我们可以通过设置path环境变量,使程序

 ConsoleProcessFormat.exe可以在其它工作目录快捷运行

容器的环境变量配置 环境容量是变量吗_环境变量_16

 我们在设置的时候使用了 %path%,这个是拓展环境变量,通过这个方式可以引用已经存在的环境变量path原有的值。所以在命令行我们有两种方式可以查看环境变量的值,一种是使用set 环境变量名,另外一种是使用拓展环境变量

 

容器的环境变量配置 环境容量是变量吗_batch命令_17

 拓展环境变量在C#可以使用方法取值

容器的环境变量配置 环境容量是变量吗_java_18

2. java 中的 java.library.path 与 path 环境变量关系

java中加载dll一般是通过System.load和System.loadLibrary来实现的,这两个函数在加载动态库时

会查看系统属性 java.library.path 和 sun.boot.library.path,查看ClassLoader.loadLibrary可知

容器的环境变量配置 环境容量是变量吗_batch命令_19

那么在命令行参数中没有显性指定 java.library.path 系统属性的时候,我们可以发现java.library.path取的是 path 环境变量的值

容器的环境变量配置 环境容量是变量吗_容器的环境变量配置_20

那么要注意的是 如果我们显性的指定了 java.library.path的时候,如果没有把原有的path加进去,很可能导致系统一些基础的依赖库找不到。所以手动指定java.library.path的时候最好把原有的path环境变量的值追加上去。

进程内设置java.library.path属性不生效的原因

有的时候我们想在进程内通过 System.setProperty 来设置 java.library.path 的值 来动态追加dll文件的搜索路径会发现不会生效。追本溯源,发现是缓存的原因。

System.load 会调用 ClassLoader类的 loadLibrary方法,loadlibrary方法部分代码如下

容器的环境变量配置 环境容量是变量吗_环境变量_21

java进程启动后,ClassLoader类的静态属性sys_paths 已经被赋值了,所以默认usr_paths取的是默认的 java.library.path 属性值

容器的环境变量配置 环境容量是变量吗_c#_22

所以要想自己设置的java.library.path生效,需要重置sys_paths的值为null或者 直接修改usr_paths变量追加相应的path,这两种方式都需要反射来完成。

下图中在修改sys_paths变量前后打印出loadlibrary加载XBootLib.dll的结果

容器的环境变量配置 环境容量是变量吗_batch命令_23

 

 注意的是System.loadLibrary加载DLL不带后缀名,使用System.load加载DLL则需要指定dll文件全路径