文章目录
- 前言
- 一、环境变量
- 1.概念
- 2.常见环境变量
- 3.一个疑问
- 4.通过系统调用获取或设置环境变量
- 二、地址空间
- 1.引入
- 2.分页&进程地址空间
- 1.页表
- 2.写时拷贝
- 3.为什么要有地址空间
- 总结
前言
今天我们继续学习进程相关知识。
一、环境变量
1.概念
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
2.常见环境变量
PATH : 指定命令的搜索路径
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
SHELL : 当前Shell,它的值通常是/bin/bash。
3.一个疑问
为什么有些指令可以直接执行,不需要带路径,而我们的二进制程序需要带路径才能执行?
- 执行程序前,系统会在特定路径下查找对应程序
- 而PATH的作用是辅助系统进程指令查找,PATH变量储存的就是可能存在指令或者程序的路径
也就是说,如果我们把自己程序所在路径加入环境变量PATH当中,就可以直接执行。
测试:
[wkj@VM-4-13-centos ~]$ export PATH=$PATH:/home/wkj/lesson8
[wkj@VM-4-13-centos lesson8]$ ll
total 36
-rw-rw-r-- 1 wkj wkj 77 Nov 7 18:22 makefile
-rwxrwxr-x 1 wkj wkj 8464 Nov 12 21:23 myproc
-rw-rw-r-- 1 wkj wkj 169 Nov 12 21:23 myproc.c
-rwxrwxr-x 1 wkj wkj 9616 Nov 12 19:44 mytest
-rw-rw-r-- 1 wkj wkj 243 Nov 12 19:43 mytest.c
[wkj@VM-4-13-centos lesson8]$ myproc
hello world,20905
hello world,20905
hello world,20905
注意看,添加以后不需要./即可直接执行。
添加方法只在当前有用,退出登陆后不会保存,因此大家可以添加以下玩一玩,不用担心污染环境变量库。
4.通过系统调用获取或设置环境变量
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
常用getenv和putenv函数来访问特定的环境变量。
tips:环境变量通常具有全局属性,可以被子进程继承下去
二、地址空间
我们经常在各个论坛看到类似以下的图片。
1.引入
#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;
}
与环境相关,我们观察现象即可
parent[2995]: 0 : 0x80497d8
child[2996]: 0 : 0x80497d8
我们发现,输出出来的变量值和地址是一模一样的,很好理解呀,因为子进程按照父进程为模版,父子并没有对变量进行任何修改。可是将代码稍加改动:
#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;
}
与环境相关,我们观察现象即可
child[3046]: 100 : 0x80497e8
parent[3045]: 0 : 0x80497e8
怎么回事呢?地址一样,变量内容不一样,我接受不了哇!
我们可以得出以下结论:
- 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量地址值是一样的,说明,该地址绝对不是物理地址。
- 在Linux地址下,这种地址叫做 虚拟地址。
- 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理。
- OS必须负责将 虚拟地址 转化成 物理地址 。
2.分页&进程地址空间
1.页表
页表就是一个用于将虚拟地址转换为物理地址的工具。在 riscv32 中,如果 satp 的第 31 位为 1 ,则表示启用页表机制。在访问虚拟内存时(开启页表时只允许访问虚拟内存),内存管理单元 MMU(Memory Management Unit) 会用过页表将虚拟地址转换为物理地址,再进行访问。
2.写时拷贝
一个变量为什么可以标识不同的值?
父子进程各自在物理内存中,都有属于自己的变量空间,只不过在用户层用同一个变量(虚拟地址)来标识了。
3.为什么要有地址空间
- 凡是非法的访问或者映射,OS都可以设备到,并终止这个进程。
- 因为有地址空间、页表,在物理内存中,可以堆未来的数据进行任意位置的加载。
- 物理内存理论上可以进行任意位置加载,那么他很可能是乱序的。页表将虚拟地址和物理地址进行映射,内存分布可以有序化。
总结
以上就是今天要讲的内容啦。