经过前三篇的认识,现在应该已经大致认识到了进程到底是什么,也认识了进程的状态,进程的优先级,环境变量等知识。今天我们继续学习,来一起认识地址空间!!!
上一篇文章我们介绍了什么是环境变量,今天我们来看看如何创建获取环境变量
补充一下和环境变量相关的命令
1. echo: 显示某个环境变量值
2. export: 设置一个新的环境变量
3. env: 显示所有环境变量
4. unset: 清除环境变量
5. set: 显示本地定义的shell变量和环境变量
我们先在当前路径创建一个变量看看
我们可以通过echo 命令查到,说明HELLO在Bash中是存在的,只是没有把它当做环境变量。我们也可以通过下面一段代码来验证一下:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4
5
6
7 int main()
8 {
9
10 char *path = getenv("HELLO");
11 if(path == NULL) return 1;
12 printf("hello:%s\n",path);
13 return 0;
14 }
这样执行程序后会发现无法打印出来(因为子进程会继承父进程的环境变量表,所以证明不是环境变量,也就是本地变量)
可以使用export
可以加入bash的内存中。
我们先来做一个小实验,来看一个神奇的现象:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4
5 int g_val = 10000;
6
7 int main()
8 {
9
10 printf("father is running ,pid:%d,ppid:%d\n",getpid(),getppid());
11
12 pid_t id = fork();
13 if(id == 0)
14 {
15 //child
16 int cnt = 0;
17 while(1)
18 {
19 cnt++;
20 printf("I am child process,pid:%d,ppid:%d,g_val:%d, &g_val:%p\n",getpid(),getppid(),g_val,&g_val);
21 sleep(1);
22 if(cnt == 5)
23 {
24 g_val = 9999;
25 printf("I am child process,change %d -> %d\n",100,g_val);
26 }
27 }
28
29 }
30 else
31 {
32 //parent
33 while(1)
34 {
35 printf("I am parent process,pid:%d,ppid:%d,g_val:%d, &g_val:%p\n",getpid(),getppid(),g_val,&g_val);
36 sleep(1);
37 }
38
39 }
40
41 return 0;
42 }
来看执行的效果:
同一个变量,甚至地址都一样,但是为什么是不一样的值???
其实我们常说的地址并不是在磁盘或内存中的真正的地址,程序地址空间是在操作系统中来说的。
每一个进程都会有对应的地址空间,储存对应数据和代码。那么如何保证每个进程都正确的读取数据和代码,而不会与其他进程搞混呢?这就与其本质有关了:
程序地址空间的本质是结构体对象,通过这个结构体操作系统可以管理进程。子进程的页表会拷贝自父进程,所以子进程会继承父进程的数据。
当子进程想要修改g_val
时,如果父进程也被修改,那么就破坏了进程的独立性,可能导致程序崩溃,那么操作系统是如何解决这个问题的呢???
操作系统会检查该变量是不是子进程独有的,如果不是,那么就会重新开辟一个物理空间来储存新值,对应的页表映射也发生改变,注意页表的虚拟地址不变,改变的是映射的物理空间,就能够修改变量值了,而且打印的虚拟空间一致(物理空间不同)。
来注意一些细节:
可以打个比方,小学生的桌子都有一个三八线,这个划分区域就类似这样,保证每个人(进程)有独属于自己 的空间
struct area
{
int start;
int end;
}
struct destop
{
struct area left;
struct area right;
}
就类似这样,做到控制空间大小区域。 我们,来看源码里是如何调节的:
这里面进行了区域的划分。
如果直接使用物理地址,可以想象是很混乱的,很容易发生越界等危险操作。 而通过页表来进行映射,就明确了储存地址的范围,保证了数据读取的安全:
CPU可以储存页表,并且有一个简单的寄存器MMU,可以快速的将虚拟地址转化为物理地址。页表中有对应的位置会存入rwx权限
,做到保护物理地址,例如:
char* str = "Hello world";
*str = 'J';
这肯定会报错的,这个报错就是页表检查权限进而报错了,只读的权限不能进行写入操作。 操作系统可以进行一下检查:
可执行程序进行运行时,会将页表对应的物理内存的数据直接读取出来。等…
Linux是一个分时操作系统(与之对应的是实时操作系统,例如车机操作系统可以实时反应)。
普通优先级: 100~ 139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!) 实时优先级: 0~ 99(不需要关心这个)
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000.......
这个位图就可以快速查询(以32个比特位进行查找)。
在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增加,我们称之为进程调度O(1)算法!
由图所示。