目录
一、环境变量
1、什么是环境变量
首先先举个环境变量的例子:
我们在Linux中,运行ls、pwd之类的命令,直接输入就能运行,输入路径**/usr/bin/ls/pwd**当然也能运行
那我们有一个可执行文件test,为什么不能直接输入test运行,必须要输入**./test**或是它的具体路径才能运行,如下:
其实很简单,因为像ls/pwd这些命令,是在环境变量path中已经配置过了,所以是默认可被找到的,如下图可见**echo path**:环境变量前加就可以看到里面内容
里面的路径是用冒号分割开的
可以看到里面有/usr/bin这个路径,而我们上面的pwd、ls都是在这个搜索路径当中
而我们的可执行程序并不在环境变量的搜索路径中,所以我们也就没办法直接执行
但是下面的export命令可以帮我们做到这一点
2、常见的环境变量
PATH:指定命令的搜索路径
HOME:指定用户的主工作目录
SHELL:当前的shell值,一般是/bin/bash
3、环境变量相关命令
①查看某个环境变量的值
echo $[环境变量名字]
②查看全部环境变量
查看当前系统全部的环境变量:env
③设置一个新的环境变量
export
执行export PATH=$PATH:/home/fcy/lesson后,再echo,就可以发现我们的/home/fcy/lesson这个路径,也被添加到搜索路径当中了
④清除环境变量
unset
⑤显示定义本地的shell变量和环境变量
set
二、地址空间
1、进程地址空间
首先回顾一下,地址空间的布局如下图:
下面用Linux验证一下地址空间的布局:
运行结果:
可以清楚看到,运行结果从上到下,分别是代码区,已初始化,未初始化,堆,栈
从上面的分布图可以知道,代码区,已初始化,未初始化,堆,栈是从低地址到高地址存储的,所以运行结果也是从小到大的
我们也可以发现,堆和栈的差别是非常大的,因为堆是由低地址向高地址存储,而栈是由高地址向低地址存储的,所以他们中间相隔的地址是很大的
下面验证堆是由低地址向高地址存储,而栈是由高地址向低地址存储的:
我们malloc的数据时在堆中开辟的,所以变量p0、p1、p2、p3是在堆上的,而开辟出来的p0、p1、p2、p3变量的地址是存在栈上的
结果如下:
观察结果可以看到,堆从heap0到heap3是在递增的,而栈从stack0到stack3是递减的
说明我们上面所说的堆是由低地址向高地址存储,而栈是由高地址向低地址存储的:
堆栈相对而生
static修饰的局部变量,本质就是将该变量开辟在全局区域
下面的内存打印结果可以看出来:
观察打印的地址可以看到,局部变量a1是栈里的,而a2前面加上了static,这时a2的地址就到了val与un_val那里得全局区域了
这就是为什么static修饰的局部变量,它的生命周期是全局属性的
2、虚拟地址空间
我们要知道虚拟地址空间的存在,至于怎么证明这个虚拟地址空间是存在的,看下面的证明:
有一个全局变量val,赋初始值为5,然后执行fork函数,有两个进程,分别进入if和else语句执行死循环,其中子进程在运行三次后,改变val的值,而父进程不改变,每次循环都打印val和val的地址
结果如下:
可以发现,在第四次循环后,子进程由于改变了val值,打印时val变为10,而父进程val仍为5,并且观察他们打印出来的val的地址,都是0x60103c
这时我们可以明白,一个变量val,值不同,一个5一个10,反而val的地址却是相同的,所以这里打印出来的0x60103c肯定不是真实的物理地址,而是虚拟地址
所以证明了虚拟地址空间的存在
接下来用虚拟地址空间解释一下上面代码中出现的情况
首先父子进程被创建,子进程继承了父进程的许多属性,所以他们的地址空间分布也基本一样,所以父子进程的val在虚拟地址空间中所处的位置也就相同,页表的映射关系也一样,所以父子进程的val打印出来的的虚拟地址相同
而当子进程通过页表的映射关系,想要找到对应的物理地址改变自己的val值时,操作系统又会给给子进程的val重新开辟一段物理空间,并改变子进程页表的映射关系,这时虽然父子进程val的虚拟地址相同,但通过页表映射的物理地址却是不同的了
所以做到了val值不同,打印出来的虚拟地址却是相同的
这种情况就叫做写时拷贝
那么为什么要有地址空间的存在呢?
1、地址空间和页表都是OS创建并维护的,所以想要使用地址空间和页表进行映射,都是在OS的监管之下的,所以凡是非法的访问或者映射,OS都会识别到,并终止这个进程,从而有效的保护了物理内存
2、地址空间的存在,可以将进程管理和内存管理通过页表进行解耦,两个模块互不影响,只需要通过页表建立映射关系即可
3、以统一的视角看待所有的进程,在系统底层用页表映射到不同的物理内存处,完成进程独立性的实现