Linux:环境变量的理解
Posted Fox!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux:环境变量的理解相关的知识,希望对你有一定的参考价值。
目录
在讲环境变量之前,我们先把上次遗留知识点给总结了(僵尸进程和孤儿进程)
1 Z(zombie)-僵尸进程
- 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲) 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程 。
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
- 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
我们来创建一个僵尸进程的栗子:
1 #include<iostream>
2 #include<unistd.h>
3 #include<stdio.h>
4 using namespace std;
5
6 int main()
7
8 pid_t ret= fork();
9 if(ret==0)
10
11 while(1)
12
13 //child
14 printf("我是一个子进程,pid:%d,ppid;%d\\n",getpid(),getppid());
15 sleep(1);
16 return 1;
17
18
19 else
20
21 while(1)
22
23 //parent
24 printf("我是一个父进程,pid:%d,ppid;%d\\n",getpid(),getppid());
25 sleep(1);
26
27
28 return 0;
29
当我们查看该进程时:
不难发现子进程已经处于僵尸状态了,那这样子进程不就没法回收了吗?该进程就会一直占有CPU资源,那不就造成了内存泄露了吗,对的。另外僵尸进程是不能够用命令杀掉的(因为已经退出)。
我们总结下僵尸进程的危害:
- 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态!维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护!
- 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费。因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间! 最终会造成内存泄漏!
如何避免我们将放到后面来讲。
2 孤儿进程
父进程如果提前退出,那么子进程后退出,进入 Z 之后,那该如何处理呢? 父进程先退出,子进程就称之为 “ 孤儿进程 ” 孤儿进程被 1 号 init 进程领养 。
上面我们提到了子进程先退出就是僵尸进程,那么父进程先退出呢?
我们想想,此时父进程会是僵尸状态吗?
答案是不会的,父进程在此时会被他自己的父进程(bash)回收,而它的子进程则会交给1号进程领养,我们可以修改一下代码,让父进程先退出,然后运行:
这时子进程已经被1号进程给领养了,我们想要杀掉该进程就可以用
killall "文件名"
我们不难看出孤儿进程正常情况下是不会有内存泄漏的。
3 环境变量
3.1 基本概念
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。
相信大家在学习Java的时候都应该配置过环境变量,这是大家学习Java的第一步,相信大家在配置环境变量是心里非常疑惑,为啥要配置环境变量呢?接下来我会慢慢为大家解答的。
首先为问大家一个问题:为啥我们Makefile生成了可执行文件后再运行要加上./ ?
不加./就找不到该文件了吗?答案是是的,只有加上了./我们才能够正确定位到我们想要找的位置,但是像我们使用的一些基本命令像:whoami pwd 等等为啥就不用了呢?
这就是我们今天要讲的主题:因为配置了环境变量。
我们可以使用 env 来查看环境变量:
[grm@VM-8-12-centos lesson7]$ env
XDG_SESSION_ID=342794
HOSTNAME=VM-8-12-centos
SHELL=/bin/bash
TERM=xterm
HISTSIZE=3000
SSH_CLIENT=117.172.173.113 7924 22
OLDPWD=/home/grm
SSH_TTY=/dev/pts/0
USER=grm
LD_LIBRARY_PATH=:/home/grm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
MAIL=/var/spool/mail/root
PWD=/home/grm/lesson7
LANG=en_US.utf8
HOME=/home/grm
SHLVL=2
LOGNAME=grm
SSH_CONNECTION=117.172.173.113 7924 10.0.8.12 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; history -a; history -a; history -a; printf "\\033]0;%s@%s:%s\\007" "$USER" "$HOSTNAME%%.*" "$PWD/#$HOME/~"
XDG_RUNTIME_DIR=/run/user/0
HISTTIMEFORMAT=%F %T
_=/usr/bin/env
不难发现上面出现了很多配置了的环境变量,像:
PATH : 指定命令的搜索路径 HOME : 指定用户的主工作目录 ( 即用户登陆到 Linux 系统中时 , 默认的目录 ) SHELL : 当前 Shell, 它的值通常是 /bin/bash
还有很多,这里就不一一列举了,大家可以在上面找到。
假设我们要查询pwd在哪个目录下,可以用命令:
[grm@VM-8-12-centos lesson7]$ which pwd
/usr/bin/pwd
我们查看一下环境变量的方法:
echo $NAME
例如:
[grm@VM-8-12-centos lesson7]$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
我们很容易验证pwd的绝对路径:
[grm@VM-8-12-centos lesson7]$ ls /usr/bin/pwd
/usr/bin/pwd
那我们是不是只要将我们可执行文件添加到环境变量中就可以不用加./了呢?
我们可以来试试:
添加环境变量的方法:
export PATH=$PATH:程序所在路径
这时我们直接运行hello程序依旧能够跑起来:
假如我们不小心将命令写成了这个样子:
export PATH=程序所在路径
这时我们系统自带的环境变量将被我们新加入的环境变量所覆盖,大家这时也不要担心,我们将XShell关闭后重新打开就好了。
3.2 测试HOME
[grm@VM-8-12-centos lesson7]$ cd ~
[grm@VM-8-12-centos ~]$ pwd
/home/grm
[grm@VM-8-12-centos ~]$ echo $HOME
/home/grm
我们不难发现我们平常用的pwd指令本质上就是将其添加到了环境变量中。
3.3 和环境变量相关的命令
1. echo: 显示某个环境变量值 2. export: 设置一个新的环境变量 3. env: 显示所有环境变量 4. unset: 清除环境变量 5. set: 显示本地定义的 shell 变量和环境变量
我们看下面的命令:
[grm@VM-8-12-centos ~]$ val=20
[grm@VM-8-12-centos ~]$ echo val
val
[grm@VM-8-12-centos ~]$ echo $val
20
下面我们加入的val在环境变量中吗?
我们通过env命令查询后发现没有在环境变量中,这种叫做本地变量,只在Shell内部有效。
要想导入环境变量得用export命令,就像上面我们使用export导入环境变量一样。当我们使用set时就能够看见我们写入的本地变量和环境变量,不过这个命令很少用。
3.4 环境变量的组织方式
大家心中的main函数应该是无参的,因为我们平时写代码从来都不会些main函数的参数。但是实际上main函数1最多是有3个参数的,分别是:int argc, char *argv[], char *env[]
我们可以来看看命令行第3个参数究竟是什么?
我们创建一个测试文件,并向里面写入:
1 #include<iostream>
2 using namespace std;
3
E> 4 int main(int argc,char* argv[],char* env[])
5
6 for(int i=0;env[i];i++)
7
8 cout<<env[i]<<endl;
9
10 return 0;
11
运行后发现:
这不就是我们刚才通过env查到的环境变量吗?对的,其实main函数中第三个参数是一个指针数组,指向的就是环境变量表。
我们也可以通过第三方变量environ获取:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
extern char **environ;
int i = 0;
for(; environ[i]; i++)
printf("%s\\n", environ[i]);
return 0;
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。
我们还可以通过系统调用获取或设置环境变量。这样做的好处是每次寻找环境变量不用每次都遍历环境变量表。
#include <iostream>
#include <cstdlib.h>
int main()
printf("%s\\n", getenv("PATH"));
return 0;
常用getenv和putenv函数来访问特定的环境变量。
putenv我们放到后面来讲解。
那么char* argv[]又是什么鬼呢?
我们来看一段代码:
1 #include<iostream>
2 using namespace std;
3
4 int main(int argc, char* argv[],char* env[])
5
6 for(int i=0;i<argc;i++)
7
8 cout<<argv[i]<<endl;
9
10
当我们这样运行时:
不难发现argv[]将我们在命令行上敲出来的选项都打印出来了,这也是我们输入一些命令时(例如:ls )附带一些选项时的原理,为什么当执行不同的选项时结果会是不同的,就是因为当执行不同的选项时将选项的结果都保存到了argv[]中,当执行时就会拿出保存的结果来执行,具体的执行方式我们将放到后面来讲。
3.5 环境变量通常是具有全局属性的
环境变量通常具有全局属性,可以被子进程继承下去.
#include <stdio.h>
#include <stdlib.h>
int main()
char * env = getenv("MYENV");
if(env)
printf("%s\\n", env);
return 0;
直接查看,发现没有结果,说明该环境变量根本不存在:
当我们导出环境变量export MYENV="hello world"
再次运行程序,发现结果有了!说明:环境变量是可以被子进程继承下去的!(这张表是由bash制作而成的)
总结:
- 环境变量本质就是内存级的一张表,这张表在用户登录系统的时候,进行给特定的用户形成属于自己的环境变量表。
- 环境变量中的每一个都有自己的应用场景,有的是按路径查找的,有的是进行身份验证的,有的是进行动态库查找的,有的是用来确定当前路径等等。
- 环境变量的相关数据是从相关的配置文件中读到的,每一个元素都是kv的。
linux环境变量介绍
一 linux环境变量概念的重要性
最初用linux是为了学习机器人操作系统(ros),由于之前没有接触过linux,所以并不了解其中的一些关键概念,导致走了许多弯路,如在ros的安装过程中,没有理解安装教程中的1.6 环境变量建立中的指令的意义,所以导致有时在自己工作空间建立的包会有找不到的情况。因此,本文结合ros安装教程中的部分指令,重点介绍linux环境变量的概念及应用。
二 环境变量的概念
环境变量,就是描述环境的变量。环境就是与shell相关的会话和工作环境,变量就是可变的量。
三 变量的分类及操作
变量分为全局变量和局部变量;
全局变量对shell和shell创建的子进程可见(继承性);
局部变量只对创建它们的shell可见(临时性)。
局部变量可以变为全局变量,供其创建的子进程使用(export);
显示全部环境变量(printenv);
显示所有变量(set)
需要注意的是:这些自己定义的全局变量和局部变量在注销bash时就会失效。
四 PATH环境变量
PATH环境变量决定命令的查找位置和PATH变量中的查找顺序;
为什么是PATH变量中的查找顺序,因为命令运行的顺序是按照以下四个方式查找:
1 以相对/绝对路径执行命令
2 由alias找到该命令
3 bash内置的命令
4 PATH变量中的顺序
五 环境变量配置文件
系统环境配置文件的作用是让bash启动时可直接读取这些文件,而不需要我们自己定义。
配置文件分为全体系统配置文件和个人用户配置文件。
六 启动shell的不同方式
shell启动方式不同,会启用不同的配置文件。
shell启动方式分为三种:
1 登录shell(默认登录shell):需要输入用户账号和密码的shell
2 非登录shell(交互式shell):不需要输入账号密码的shell
3 运行脚本(非交互shell)
登录shell和非登录shell具体如何调用哪些配置文件,本文不做说明,我们只需要知道都调用了环境配置文件 ~/.bashrc 即可。
七 示例:ros安装教程中环境变量的建立
安装ros的官方教程,我们会发现在建立环境变量时会有给我们三种选择:
第一种:可以在你每次在启动新的shell时自动添加ROS的环境变量
echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc source ~/.bashrc
第二种:只是在你当前的shell中添加ROS的环境变量
source /opt/ros/kinetic/setup.bash
第三种:使用的是zsh,而不是bash
echo "source /opt/ros/kinetic/setup.zsh" >> ~/.zshrc source ~/.zshrc
这里我们使用前两种进行说明。
我们上面说过自己定义的全局变量和局部变量在注销bash时就会失效,想让自己定义的变量不失效的办法:写入配置文件
因此,就有了ros中第一种环境变量的建立:
echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc source ~/.bashrc
第一行表示把" "中的字符串写入到~/.bashrc中(需要注销再登陆才会生效,>> 表示数据流输出重定向"追加",>表示"替换")
第二行表示把配置文件读入当前的shell中。(立即生效)
所以有ros第二种环境变量建立方法
source /opt/ros/kinetic/setup.bash
把配置文件读取到当前的shell中。
至此,环境变量的大部分概念都已经介绍完毕。
以上是关于Linux:环境变量的理解的主要内容,如果未能解决你的问题,请参考以下文章