• 《进程地址空间》


    【一】环境变量

    基本概念:环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:我们在编写C/C++代码的时候,在链接的时候,我们从来不知道我们所链接的动态库的静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关的环境变量帮助编译器进行查找,环境变量同城具有某些特殊用途,还有在系统当中通常具有全局特性。

    PATH:指定命令的搜索路径

    HOME:指定用户的主工作目录(即用户登录到Linux系统中时,默认的目录)

    SHELL:当前的Shell,他的值通常是/bin/bash。

    环境变量最大的意义,可以标识当前使用的Linux用户。例如:

    bash就是一个系统进程,我们平时定义的变量就是一个本地变量,只在当前进程内有效。

    当我们使用关键字export将本地变量转化为全程变量,就会被子进程继承下去,为了不同的应用场景。

    和环境变量相关的命令

    1.echo:显示某个环境变量值

    2.export:设置一个新的环境变量

    3.env:显示所有环境变量

    4.unset:清除环境变量

    5.set:显示本地定义的shell变量和环境变量

    环境变量的组织方式:

     每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以'\0'结尾的环境字符串。

    【二】通过代码如何获取环境变量

    1.命令行第三个参数

    1. #include
    2. int main(int argc, char *argv[], char *env[])
    3. {
    4. int i = 0;
    5. for(; env[i]; i++){
    6. printf("%s\n", env[i]);
    7. }
    8. return 0;
    9. }

    输出结果:

     这就是这台主机的全部环境变量

    2.通过第三方变量environ获取

    1. #include
    2. int main(int argc, char *argv[])
    3. {
    4. extern char **environ;
    5. int i = 0;
    6. for(; environ[i]; i++){
    7. printf("%s\n", environ[i]);
    8. }
    9. return 0;
    10. }

    全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时需要使用extern声明。

    结果和第一种方式是一模一样的

    3.通过系统调用获取活着设置环境变量

    1. #include
    2. #include
    3. int main()
    4. {
    5. printf("%s\n", getenv("PATH"));
    6. return 0;
    7. }

    结果也和以上是一样的。

    4.环境变量是具有全局性的,且环境变量通常是具有全局属性的,可以被子进程继承下去的,这是为什么呢?因为子进程也是一个独立的进程,既然是一个独立的进程就可以独立的完成某些任务,所以必须要有一个独立的环境变量去支持她完成这些任务。

    【三】程序地址空间

    当我们在讲C语言的时候,当时我们的老师和我们说地址的空间布局是长这样的:

    仅在32位平台下:

     看以下代码:

    1. #include<stdio.h>
    2. #include<unistd.h>
    3. int global_value=100;
    4. int main()
    5. {
    6. pid_t id=fork();
    7. if(id<0)
    8. {
    9. printf("fork error!\n");
    10. return 1;
    11. }
    12. else if(0==id)
    13. {
    14. int cnt =0;
    15. while(1)
    16. {
    17. printf("我是子进程,pid:%d,ppid:%d| global_value:%d,&global_value=%d\n",getpid(),getppid(),global_value,&global_value);
    18. sleep(1);
    19. cnt++;
    20. if(cnt==3)
    21. {
    22. global_value =300;
    23. printf("子进程已经更改了全局变量。。。\n");
    24. }
    25. }
    26. }
    27. else
    28. {
    29. while(1)
    30. {
    31. printf("我是父进程,pid:%d,ppid:%d| global_value:%d,&global_value=%d\n",getpid(),getppid(),global_value,&global_value);
    32. sleep(2);
    33. }
    34. }
    35. sleep(1);
    36. return 0;
    37. }

    输出的结果:

     刚开始数值是相同的,这个我们很好理解,因为子进程按照父进程为模板,父子并没有对变量进行任何修改,但是后面的你知道为什么吗?父子进程,输出地址是一致的,但是变量内容不一样,所以能得出如下结论:

    1.变量内容不一样,所以父子进程输出的变量绝不是同一个变量

    2.但是地址值是一样的,说明,该地址绝不是物理地址!

    3.Linux地址下,这种地址叫做虚拟地址

    4.我们在用C/C++语言所看到的地址全部都是虚拟地址,物理地址,用户一概看不到,由OS同意管理,OS必须负责将虚拟地址转化为物理地址。

    【四】分页&虚拟地址空间

    上面画的那个地址空间图,就是一个简单的虚拟地址空间,我们取地址取到的就是虚拟地址,那我们如何取获得真实的内存呢?这就需要一个中间的媒介叫做,页表,你可以简单的把页表理解为左边存储的是虚拟地址,右边存储的是物理地址,右边和左边的地址一一对应,

    我们需要读取内存空间的内容的时候,我们会先去虚拟地址空间去找相应的虚拟的地址,然后到页表中按照虚拟的地址寻找到物理地址,根据物理地址寻找到相应的内存,然后将内存中存储的数据加载到相应的进程中,这也就能解释上面的现象发生的原因了,因为我们父进程和子进程都有自己的页表,所以虚拟地址是可以相同的,但是在物理地址中,父进程和子进程都是有用自己的一块空间,所以存储的内容是不相同的。

    以上就是这期的全部内容,如果觉得写的还行的话,还请一件三连,顺便点点关注,毕竟码字不易。如果觉得哪里有问题的地方,还请位于评论区斧正,感激不尽。

  • 相关阅读:
    TCP四次挥手 & 2MSL & TIME_WAIT详解
    【CSS in Depth 2精译】2.5 无单位的数值与行高
    MySQL 6种索引数据结构详解:BTree、B+Tree、红黑树、平衡二叉树、二叉树、Hash
    4-6网络层-IP组播
    Spring 如何实现一个CGLlB动态代理呢?
    贷款测算应用PC端
    【Python脚本进阶】2.4、conficker蠕虫(上):Metasploit攻击Windows SMB服务
    『 MySQL数据库 』数据库之表的约束
    什么时候不要采用微服务架构
    Nacos相关概念小总结
  • 原文地址:https://blog.csdn.net/wuqin668/article/details/127825330