一、环境变量
1.概念介绍
环境变量(environment variables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数,如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪 ⾥,但是照样可以链接成功,⽣成可执⾏程序,原因就是有相关环境变量帮助编译器进⾏查找,环境变量通常具有某些特殊⽤途,还有在系统当中通常具有全局特性。
通俗的概念介绍不能让我们会很好的理解什么是环境变量,为了更好地找到切入点,这里引入一个问题,main函数有参数吗?这里看一段代码:
#include<stdio.h>
int main(int argc,char* argv[]){
for(int i=0;i<argc;i++){
printf("argv[%d]->%s \n",i,argv[i]);
}
return 0;
}
现象如下:
因此我们可以知道,main函数是有参数的,其中参数二就是字符指针数组,以空格为间隔,里面存着的是指令行里的每一个选项,参数一就是字符指针数组的有效大小。
由此我们可以引入一个概念: 进程中有一张参数表,用来支持选项的功能,而main函数的参数表,就是实现程序不同子功能的方法。
我们在运行自己的程序时,需要输入的指令是./code,而我们知道./的意思是在当前路径的意思,但是我们在执行系统的指令时,是不需要./的。要执行一条指令或者是一个程序,必须先找到它。那么是谁找?显而易见是由bash(用户在登录时系统就帮我们创建的进程)找,在哪里找?通过环境变量PATH来找(环境变量)。
因此我们可以输出一个结论:PATH是系统中默认搜索指令的默认搜索路径。
2.理解环境变量
显示所有的环境变量指令:env 。其中env显示出的基本格式:名称 = 内容
查看具体环境变量的指令:echo $环境变量名称。
1.那么我们该如何理解环境变量呢?
从存储的角度来说,bash会在内部形成一张环境变量表,表中放着的为字符串指针,指向的就是环境变量的“名称 = 内容”。
因此bash内部有两张表,一张命令行参数表,一张环境变量表。
2.环境变量最开始是从哪里来的?
从系统的相关配置文件来的,在每个用户登录时,系统都会生成对应的bash进程,生成所对应的配置文件,如图:
用户可以更改环境变量配置文件,在linux机器退出之后再次登录,系统会吧你配置好的对应的配置文件对系统生成的文件进行覆盖从而长久生效,因此不推荐这样改动。
3.认识更多的环境变量
如图所示:
4.获取环境变量的方法
1.命令行
export、env、echo $XXX unset
2.代码
<一>.环境变量具有全局特性,可以被子进程继承,因此我们可以通过父进程(bash)获取环境变量,即系统环境变量数组(extern char **environ)
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
extern char** environ;
int main(){
for(int i=0;environ[i]!=NULL;i++){
printf("environ[%d]->:%s \n",i,environ[i]);
}
return 0;
}
<二>.调用getenv()函数
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
extern char** environ;
int main(){
char* env = getenv("PATH");
if(env == NULL){
printf("the PATH not found");
return -1;
}
else{
printf("the PATH is:%s \n",env);
}
return 0;
}
<三>main函数的第三个参数(char* envp[ ]),遍历方式和第一种基本一致且不通用也不推荐,此处不做详细介绍。
二、程序地址空间
1.正确认识 “ 程序地址空间 ”
学过C语言的我们通常认为程序地址空间是如图片这样的,:
然而真的是这样吗?先看一个现象:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(){
int count = 0;
int id = fork();
if(id == 0){
//child
int cnt = 3;
while(cnt--){
count++;
printf("我是子进程,我的pid:%d,父进程pid:%d,count的地址:%p,count:%d \n",getpid(),getppid(),&count,count);
sleep(1);
}
}
else{
//father
int cnt = 3;
while(cnt--){
printf("我是父进程,我的pid:%d,父进程pid: %d,count的地址:%p,count:%d \n",getpid(),getppid(),&count,count);
sleep(1);
}
}
return 0;
}
图中我们发现count的地址居然相同,难道真的是BUG???事实并非如此。
我们直接输出一个结论:我们所认为的程序地址空间其实是进程地址空间(虚拟地址空间),里面放的地址都是虚拟地址而并非内存中的物理地址,包括我们用C/C++创建的指针用到的地址都是虚拟地址,物理地址都被系统保护起来了,无法直接去访问!!!
更详细的关于程序地址空间的知识我会在下一篇博客介绍。