环境变量
以下是系统默认搜索路径,我们要运行ls,pwd这一类的指令是不需要指定路径的,还有动态库,静态库等,但我们要执行自己的程序是需要指定路径的,所以我们可以知道,环境变量指的是系统默认指令搜索路径,每个程序都会收到一张环境表,这个环境表是一个字符指针数组
创建环境变量
echo $环境变量 #显示环境变量
set#显示本地定义的shell变量和环境变量
此时我们这个路径下有很多个可执行文件
PATH=$PATH:路径
可以看到我们现在的环境变量里有多一条路径,which也是通过我们的环境变量搜索指令的
要区别一下环境变量和本地变量
VALUE=123456#本地变量,在env里是查不到的,只能在set查
如果想把本地变量变为环境变量
export VALUE=123456
如果想取消
unset VALUE
env
所以我们可以按下列方法使用
命令行参数
main函数其实是可以传参的
int main(int argc,char* argv[])
{
//函数体
return 0;
}
所以在bash看来,其实我们输入的是一个字符串,bash帮我们把字符串按空格作为分隔符打散
所以c/c++代码有两个核心向量表,一个是环境变量表,一个是命令行参数表,命令行参数表和环境变量表结构是一样的,我们运行的程序都是子进程,bash本身启动的时候,会从操作系统的配置文件读取到环境变量,子进程会继承父进程给的环境变量,如果父进程的某个环境变量取消了,子进程的对应环境变量也会取消,所以环境变量具有全局性,本地变量不会被子进程继承,只在当前bash内部有效
即使我们不用第三个参数,其实也是可以拿到环境变量的
其实这里有一个问题,我们不是说本地变量是不会被子进程继承的吗,而我们又说命令行后面的命令都是bash的子进程,这不是相互矛盾吗
所以我们这边可以发现,有的命令行后面并不是bash的子进程,系统中的命令分为两种:
1.常规命令:通过创建子进程完成
2.内建命令:bash不创建子进程,而是由自己完成,相当于bash调用自己写的/系统提供的函数,比如说cd更改bash的当前目录,echo等
进程地址空间
引入
下面这张图是地址空间,从下往上地址0000…0000—FFFF…FFFF,命令行参数和环境变量在栈之上,这也就是为什么子进程可以继承父进程的环境变量,因为子进程可以通过页表找到父进程的环境变量的,再上层就是内核空间了,栈和堆之间是共享区
线性地址&虚拟地址
可以看到,明明是同一块地址,父进程拿出的值和子进程拿出的值是不一样的,所以这肯定不是物理地址,只能说是线性地址或者逻辑地址,这就涉及到页表的概念,每一个进程都有一个自己的地址空间,叫做进程地址空间
所以父子进程里在相同的地址取出不同的值是因为每一个进程都有一个自己的页表,子进程拷贝了父进程的页表,在硬件上就是电平充放电的过程,当我们修改一个父子进程公用的值的时候,系统会识别到,就会发现我们要访问的目标数据是与父进程共享的,所以在我们写入的时候开辟了一段新空间,把子进程的页表对应的物理地址修改了,所以父子进程的被修改的值的物理地址是不一样的。
进程地址空间指的是一个进程可视化的范围的大小,本质上是内核的一个数据结构对象,类似于PCB,被操作系统创建和管理
关于地址空间上的区域划分,就是把地址空间分成几块
空间区域调整,实际上就是地址空间里几块区域的大小调整
//虚拟地址/线性地址
struct area_part{
int begin;
int end;
}
struct area{
struct area_part p1;//代码区
struct area_part p2;//字符常量区
....
struct area_part pn;//等等
}
存在原因
1.虚拟地址其实是为了虚存服务的,让每个进程都觉得自己拥有所有的空间,因为一个进程无法用完所有的空间,所以可以营造一种可用空间很大的假象,这就是虚存
2.增加虚拟地址可以让我们在访问内存的时候多加一个转换的过程,在这个过程中我们可以进行检查
3.因为有地址空间和页表的存在,所以可以把进程管理和内存管理解耦合
4.地址空间让我们看不到物理内存,可以保护内存
简单页表
操作系统采用惰性加载方式
下图是页表,初始地址存在cpu里的cr3寄存器里,其实这里还缺了一块,除了逻辑地址和物理地址,还有一个标志位,标志这个地方是可读/可写/只读等,还有另外一个标志位,判断这里的代码和数据是否已经加载到内存,虚拟内存可以用于权限管理,因为物理内存是没有只读这种概念的,而虚拟内存可以
当我们在进行访问的时候,发现我们的某页没有加载到内存里,就会发生缺页中断,比如说写时拷贝就是缺页中断,把缺的页加载到内存里,重新建立映射关系,这样就实现了边使用边加载
所以我们创建一个进程的时候,先要创建内核数据结构,再慢慢加载可执行程序
在我们管理的时候有进程管理和内存管理两部分,在缺页中断的时候就需要内存管理的参与
所以现在在我们看来,进程=内核数据结构(task_struct+mm_struct地址空间+页表)+程序的代码和数据