环境变量与地址虚拟空间
文章目录
1.概念
环境变量是指操作系统中用来指定操作系统运行的一些参数,换句话说,操作系统通过环境变量来找到运行时的一些资源
例如:链接时帮助连接器找到动态库,执行ls命令时帮助用户找到位置。
2.常见的环境变量与查看当前环境变量方法
- PATH:指定可执行程序的搜索路径,程序员执行的命令之所以能找到,它功不可没
- HOME:登录到LInux操作系统的用户家目录
- SHELL:当前的命令行解释器,默认是“/bin/bash”
查看方法:
echo $[环境变量名称]
env:查看所有的环境变量
unset:清除环境变量
set:显示本地定义的shell变量与环境变量
3.环境变量对应的文件
- 系统级文件:针对各个用户都起作用(仅root用户修改),强烈不建议修改,影响其他用户(/etc/bashrc);
- 用户级环境变量文件:只对自己的环境变量做出修改(~/.brshrc , ~/.bash_profile , ~/.bash_profile 包含 ~/.bashrc包含 ~/.etc/bashrc)
4.修改环境变量
命令行中修改:
新增:export 环境变量名称=[新添加的环境变量内容]
例如:
修改:export 环境变量名称=[$环境变量名称] [新添加的环境变量内容](echo $path:打印变量的内容)
注意:
- 如果修改老的环境变量,必须加[$环境变量名称],否则之前的环境变量就找不到了
例如:- 在当前命令中添加的环境变量,生命周期跟随当前终端,当前终端如果退出了,环境变量就没有了,即临时有效
文件中修改:
修改完毕后不会立即生效,需要配合source [环境变量名称] 才可以永久生效
新增:在文件末尾直接添加: export环境变量名称=[新添加的环境变量的内容]
如果修改老的:在老的后面添加“:[新添加的环境变量的内容]”
5.环境变量的组织方式
环境变量是以字符指针数组的方式进行组织的,最后的元素以NULL结尾(即程序读取环境变量到NULL就结束)
如图:
6.代码获取环境变量
1.main函数的参数:
main(int argc,char* argv[],char* env[])
例如在LINUX下运行如下程序
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}
得到结果
2.通过第三方变量environ获取
#include <stdio.h>
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++){
printf("%s\n", environ[i]);
}
return 0;
}
3.通过getenv
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
7.C语言中的程序地址空间图
8.虚拟地址
通过两段代码比较得出结论:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 0;
}
else if(id == 0){ //child
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
}else{ //parent
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
sleep(1);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 0;
}
else if(id == 0){ //child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取
g_val=100;
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
}else{ //parent
sleep(3);
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
sleep(1);
return 0;
}
我们发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论:
- 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
- 但地址值是一样的,说明,该地址绝对不是物理地址!
- 在Linux地址下,这种地址叫做 虚拟地址
- 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理
- OS必须负责将虚拟地址转化为物理地址
9虚拟地址空间
10.页表
- 映射关系:虚拟地址空间分成一页一页,物理内存分成一块一块,使用页表将页和块映射起来
- fork创建子进程的时候,每个进程都有自己的页表,子进程最初的页表映射的内容就是来自于父进程,后面子进程在运行的时候,可能就会有不同的映射关系写道自己的页表中
- 通过虚拟地址+页表的方式找到具体的物理地址:
虚拟地址=页号+页内偏移
页号=虚拟地址/页的大小
页内偏移=虚拟地址%页的大小- 内存分配时采用离散分配的方式,不采用连续分配的方式防止内存碎片