[Linux 进程(四)] 再谈环境变量,程序地址空间初识

这篇具有很好参考价值的文章主要介绍了[Linux 进程(四)] 再谈环境变量,程序地址空间初识。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

1、前言

上一篇我们讲了环境变量,如果有不明白的先读一下上一篇文章:环境变量讲解
本篇文章我们继续完善环境变量这章剩下的内容,以及main函数第三个参数的详解,进程地址空间的初始。

2、环境变量

2.1 main函数第三个参数 – 环境参数表

看完上一篇文章的同学,肯定知道了如何查看环境变量,命令行输入 env:
我们查看一下:
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

我们main函数的参数列表中,第三个就是环境变量表,没错它里面就记录着这些环境变量。
它与第二个参数一样,都是char类型的指针数组。我们写一段代码,打印一下环境变量表中的内容:

#include <stdio.h>

int main(int argc, char* argv[], char* env[])
{
	int i = 0;
    for( ; env[i]; i++)
    {
        printf("env[i]: %s\n", i, env[i]);
    }
    
    return 0;
}

[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

我们发现main函数的第三个参数,环境参数表跟我们env所查出来的内容是一模一样的。
这是因为我们程序在运行时,环境变量是系统给传的。
问题:
那这个给程序传环境变量的系统是谁?
在学习fork的时候,我们知道fork()函数是可以创建子进程的。同时我们也知道命令行启动的进程都是shell/bash的子进程,子进程的命令行参数和环境变量,是父进程bash给传递的!
问题:
那父进程bash的环境变量信息又是从哪里来的?
我们在上一篇中讲到,PATH内容修改后,下一次启动Xshell时,PATH内容又自动恢复了。这其实是我们直接更改了bash进程内部的环境变量信息!
每一次重新登录,都会给我们形成新的bash解释器并且新的bash解释器自动从 哪里 读取自己的环境变量表信息!
虽然我们现在还不知道父进程环境变量哪里来的,但是环境变量存在哪我就直接说了:环境变量信息是以脚本配置文件的形式存在的!
在登录目录下有一个.bash_profile脚本文件,每一次登录的时候,bash进程都会读取 .bash_profile配置文件中的内容,为我们bash形成一张环境变量表信息!
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

2.2 本地环境变量和env中的环境变量

我们可以在bash中直接定义环境变量:环境变量名 = 内容
我们来试一下:
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

用户定义的环境变量是本地的,env是查看不了本地环境变量的。
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

如果我们想要把我们自己定义的环境变量,导出到env所能查看到的环境变量中,我们可以使用以下命令:

export 环境变量名

[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

还可以直接使用export定义环境变量:
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

当我们把自己定义的环境变量放入了bash的环境变量后,我们在main函数所打印的环境变量中能不能找到呢,我们试一下:
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

我们在命令行中输入的命令都是bash的子进程,这就验证了子进程的环境变量是由父进程给传递的。

2.3 配置文件与环境变量的全局性

问题:
我们要是重新登录Xshell的话,我们刚导出的环境变量还在吗?
在不在我们测试一下就出来了:
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

明显是不在的,我们刚才导出是直接给bash进程的内部导出了。我们重新启动后 .bash_profile脚本文件会重新执行,bash进程内部的环境变量会重新读取并形成一份新的环境变量表。
如果我们想要每次登录后,bash的环境变量表都有我们自己添加的环境变量,我们就需要在 .bash_profile脚本文件中添加,此后再登录后bash的环境变量表中就有了添加的环境变量。

[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维
我们再介绍一个获取环境变量的系统调用函数,const char ** environ
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维
我们发现environ的类型是一个二级指针,这里不卖关子了,它其实指的是环境变量表首元素:
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维
我们写一段代码将其内容打印出来:

#include <stdio.h>
#include <unistd.h>

int main()
{
	extern char** environ;
    int i = 0;
    for(i = 0; environ[i]; i++)
    {
        printf("environ[%d]: %s\n", i, environ[i]);
    }
    
	return 0;
}

[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

问题: 大家有没有想过,如果我们本地环境变量,子进程会将我们添加的本地环境变量也继承下来吗?
我们做一下实验:
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维
显然是没有的(这里细心的人会发现echo为什么就继承下来了,其实echo不是子进程,别急下一个话题我们就会讲到)。如果我们将本地变量导出到系统环境变量中,子进程就会将其继承下来。
总结:
1、迄今为止我们学到了三种获取环境变量的方式:

  • getenv();
  • main函数传参方式;
  • extern char** environ;

2、bash再去创建子进程时,bash会给子进程传递一份同样的环境变量,子进程再创建子进程天然的就继承了父进程的环境变量表,也就间接获取了bash所传递的环境变量所以系统环境变量具有全局属性!

3、本地环境变量 vs 系统环境变量

  • 本地环境变量只在bash进程内部有效,不会被子进程继承下去;
  • 系统环境变量通过让所有子进程继承的方式,实现自身的全局性!

2.4 内建命令与常规命令

通过上一篇的学习,我们知道了,bash自己的指令可以直接使用,不用加 ./,因为这些指令在系统默认路径PATH下,现在我们将PATH置空,这些指令就运行不了了!
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

我们发现,有些指令确实不能运行了,但是有些指令仍然可以运行。这是为什么呢?
Linux下命令分为两类:

  • 常规命令:shell通过fork创建子进程,让子进程执行的;
  • 内建命令:shell命令行的一个函数。

原来pwd,echo都是内建命令。
那为什么内建命令就直接能读取环境变量呢?
内建命令是shell内部的一个函数,父进程内部执行,它是可以直接看到父进程内部的,当然可以直接读取shell内部定义的本地变量!

2.5 环境变量相关的命令

  1. echo: 显示某个环境变量值
  2. export: 导出一个新的环境变量
  3. env: 显示所有环境变量
  4. unset: 清除环境变量
  5. set: 显示本地定义的shell变量和环境变量
    [Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

3、程序地址空间

C/C++程序员认为,程序内存分布是这样子的:
[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维
我们写一段代码验证一下:

#include <stdio.h>
#include <stdlib.h>

int uninit_global;
int init_global = 0;
int main(int argc, char* argv[], char* env[])
{
    printf("code address: %p\n", main); // 代码区
    const char* str = "Hello Linux!\n";
    printf("read only char address: %p\n", str); // 字符常量区
    printf("init global value address: %p\n", &init_global);
    printf("uninit global value address: %p\n", &uninit_global);

    char* heap1 = (char*)malloc(sizeof(char)*100);
    char* heap2 = (char*)malloc(sizeof(char)*100);
    char* heap3 = (char*)malloc(sizeof(char)*100);
    char* heap4 = (char*)malloc(sizeof(char)*100);
    static int a = 0;
    printf("heap1 address: %p\n", heap1);                                                                                         
    printf("heap2 address: %p\n", heap2);
    printf("heap3 address: %p\n", heap3);
    printf("heap4 address: %p\n", heap4);

    printf("stack address: %p\n", &str);
    printf("stack address: %p\n", &heap1);
    printf("stack address: %p\n", &heap2);
    printf("stack address: %p\n", &heap3);
    printf("stack address: %p\n", &heap4);
	printf("a address: %p\n," &a);

    int i = 0;
    for(i = 0; i < argc; i++)
    {
        printf("argv[%d]: %p\n", i, argv[i]);
    }

    for(i = 0; env[i]; i++)
    {
        printf("env[%d]: %p\n", i, env[i]);
    }
    return 0;
}

[Linux 进程(四)] 再谈环境变量,程序地址空间初识,Linux,linux,运维

我们在这里就能看出来:
1、堆上的地址内存的使用是不断增大;
2、栈上的地址内存的使用是不断减小的但是在我们c/c++中,定义数组、结构体或者整型,它们的地址是向上增长的。比如,开辟一个十个元素的整型数组,一次整体开出来,首元素地址在最低,因此遍历时是++操作。
3、static修饰的静态成员变量,其实就是全局变量,他就放在已初始化全局变量区,所以它在全局就一份。
4、栈区之上先是命令行参数,再是环境变量,不断向上增上的。文章来源地址https://www.toymoban.com/news/detail-799835.html

到了这里,关于[Linux 进程(四)] 再谈环境变量,程序地址空间初识的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【看表情包学Linux】进程地址空间 | 区域和页表 | 虚拟地址空间 | 初识写时拷贝

       🤣  爆笑 教程  👉 《看表情包学Linux》👈   猛戳订阅     🔥 💭 写在前面: 本章核心主题为 \\\"进程地址空间\\\",会通过验证 Linux 进程的地址空间来开头,抛出 \\\"同一个值能有不同内容\\\" 的现象,通过该现象去推导出 \\\"虚拟地址\\\" 的概念。然后带着大家理解为什么虚拟地

    2024年01月20日
    浏览(51)
  • 【Linux】程序地址空间?进程地址空间

    了解进程的运行:  运行结果:我们会发现这打印的结果乱七八糟,因为它也不知道什么时候该干什么  我们让代码睡眠1秒:打印的结果就正常了  以前我们学习的内存管理(程序地址空间):  为了验证上面虚拟地址,我们运行下面代码: (这种问题出现的原因在下面的为

    2024年02月13日
    浏览(81)
  • [Linux 进程(五)] 程序地址空间深度剖析

    Linux学习路线比较线性,也比较长,因此一个完整的知识点学习就会分布在两篇文章中,没有连贯起来,订阅的朋友谅解一下,再次感谢订阅! 上一篇文章最后讲到了程序地址空间分布,大家可以先复习一下上一篇文章:程序地址空间的初认识 本片我们深度学习一下程序地址

    2024年01月19日
    浏览(33)
  • 【Linux初阶】进程地址空间 | CUP与可执行程序的交互原理

     🌟hello,各位读者大大们你们好呀🌟 🍭🍭系列专栏:【Linux初阶】 ✒️✒️本篇内容:计算机空间初识(子进程变量修改实验),感性理解进程虚拟地址空间,进程地址空间基础(概念、区域划分与调整、程序对内存数据的修改、按需分配虚拟地址空间),解答为什么存

    2024年02月05日
    浏览(79)
  • Linux: 进程地址空间究竟是什么?进程地址空间存在意义何在?

     在C/C++中,我们常将内存分为: 代码区、常量区、全局区(静态区)、堆、栈 等等。相关内存区域划分如下:(X86, 32位平台) 如何验证C/C++中各区域的相对位置呢?  我们可以在每个区域中选择一个地址来验证C/C++中各区域的相对位置!!具体如下: 【源代码】: 【运行

    2024年04月08日
    浏览(64)
  • Linux之进程(四)(进程地址空间)

    目录 一、程序地址空间 二、进程地址空间 1、概念 2、写时拷贝 3、为什么要有进程地址空间 四、总结 我们先来看看下面这张图。这张图是我们在学习语言时就见到过的内存区域划分图。  下面我们在Linux下看一看内存区域是不是也是这么划分的。 可见在Linux下也是符合上面

    2024年02月04日
    浏览(29)
  • 【Linux】进程周边006之进程地址空间

      👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.程序地址空间 1.1验证地址空间的排布  1.2利用fork函数观察当子进程修改某个共

    2024年02月04日
    浏览(27)
  • 【Linux】—— 进程地址空间

    序言: 在上篇中,我们讲解了关于进程优先级的概念。本期,我将给大家介绍的是关于进程地址空间的话题。 目录 (一)程序地址空间回顾 (二)代码演示 (三)进程地址空间的引入 总结 我们在学习C/C++语言的时候,大家可能都见过这样的空间布局图: 一个程序有哪些

    2024年02月15日
    浏览(30)
  • Linux--进程地址空间

    1.线程地址空间 所谓进程地址空间(process address space),就是从进程的视角看到的地址空间,是进程运行时所用到的虚拟地址的集合。 简单地说,进程就是内核数据结构和代码和本身的代码和数据,进程本身不能访问物理地址,之时候就需要有一个中间媒介,就是地址空间,

    2024年02月11日
    浏览(29)
  • 【Linux】深挖进程地址空间

    作者简介:დ旧言~,目前大二,现在学习Java,c,c++,Python等 座右铭:松树千年终是朽,槿花一日自为荣。 目标:熟悉【Linux】进程地址空间 毒鸡汤:也许有一天,你发觉日子特别的艰难,那可能是这次的收获特别的巨大。 望小伙伴们点赞👍收藏✨加关注哟💕💕      

    2024年02月03日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包