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

在这里插入图片描述

文章目录

  • 1、前言
  • 2、环境变量
    • 2.1 main函数第三个参数 -- 环境参数表
    • 2.2 本地环境变量和env中的环境变量
    • 2.3 配置文件与环境变量的全局性
    • 2.4 内建命令与常规命令
    • 2.5 环境变量相关的命令
  • 3、程序地址空间

1、前言

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

2、环境变量

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

看完上一篇文章的同学,肯定知道了如何查看环境变量,命令行输入 env:
我们查看一下:
在这里插入图片描述

我们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;
}

在这里插入图片描述

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

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

我们可以在bash中直接定义环境变量:环境变量名 = 内容
我们来试一下:
在这里插入图片描述

用户定义的环境变量是本地的,env是查看不了本地环境变量的。
在这里插入图片描述

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

export 环境变量名

在这里插入图片描述

还可以直接使用export定义环境变量:
在这里插入图片描述

当我们把自己定义的环境变量放入了bash的环境变量后,我们在main函数所打印的环境变量中能不能找到呢,我们试一下:
在这里插入图片描述

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

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

问题:
我们要是重新登录Xshell的话,我们刚导出的环境变量还在吗?
在不在我们测试一下就出来了:
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
我们再介绍一个获取环境变量的系统调用函数,const char ** environ
在这里插入图片描述
我们发现environ的类型是一个二级指针,这里不卖关子了,它其实指的是环境变量表首元素:
在这里插入图片描述
我们写一段代码将其内容打印出来:

#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;
}

在这里插入图片描述

问题: 大家有没有想过,如果我们本地环境变量,子进程会将我们添加的本地环境变量也继承下来吗?
我们做一下实验:
在这里插入图片描述
显然是没有的(这里细心的人会发现echo为什么就继承下来了,其实echo不是子进程,别急下一个话题我们就会讲到)。如果我们将本地变量导出到系统环境变量中,子进程就会将其继承下来。
总结:
1、迄今为止我们学到了三种获取环境变量的方式:

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

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

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

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

2.4 内建命令与常规命令

通过上一篇的学习,我们知道了,bash自己的指令可以直接使用,不用加 ./,因为这些指令在系统默认路径PATH下,现在我们将PATH置空,这些指令就运行不了了!
在这里插入图片描述

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

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

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

2.5 环境变量相关的命令

  1. echo: 显示某个环境变量值
  2. export: 导出一个新的环境变量
  3. env: 显示所有环境变量
  4. unset: 清除环境变量
  5. set: 显示本地定义的shell变量和环境变量
    在这里插入图片描述

3、程序地址空间

C/C++程序员认为,程序内存分布是这样子的:
在这里插入图片描述
我们写一段代码验证一下:

#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;
}

在这里插入图片描述

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/630162.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 编程需要什么样的开发环境?

C 编程需要什么样的开发环境&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#…

网页设计-用户体验

Use Cases (用例) 用例是用户如何在网站上执行任务的书面描述&#xff0c;从用户的角度描述了系统响应请求时的行为。每个用例都是用户实现目标的一系列简单的步骤。简言之&#xff0c;用例是一种用于描述系统如何满足用户需求的方法。 用例的好处 1. 明确需求&#xff1a; Use…

Python办公自动化 – 可以解析的文件格式和可以调用的API实现办公自动化

Python办公自动化 – 可以解析的文件格式和可以调用的API实现办公自动化 以下是往期的文章目录&#xff0c;需要可以查看哦。 Python办公自动化 – Excel和Word的操作运用 Python办公自动化 – Python发送电子邮件和Outlook的集成 Python办公自动化 – 对PDF文档和PPT文档的处…

Unity与Android交互通信系列(4)

上篇文章我们实现了模块化调用&#xff0c;运用了模块化设计思想和简化了调用流程&#xff0c;本篇文章讲述UnityPlayerActivity类的继承和使用。 在一些深度交互场合&#xff0c;比如Activity切换、程序启动预处理等&#xff0c;这时可能会需要继承Application和UnityPlayerAc…

Windows系统字体尺寸学习

调用GetTextMetrics来获得字体尺寸信息, 函数返回设备描述表中当前选定的字体信息&#xff1b; 返回值到TEXTMETRIC类型的结构中&#xff1b; 返回字段值的单位取决于当前设备描述表映射方式&#xff1b;默认映射方式是MM_TEXT&#xff0c;值的单位是像素&#xff1b; 前7个字…

多模型图像特征可视化

特征图可视化是指将网络中某一层的特征图可视化出来&#xff0c;以便观察网络在不同层次上学到的特征。卷积可视化可以帮助深度学习研究者更好地理解卷积的概念和原理&#xff0c;从而更好地设计和优化卷积神经网络。通过可视化&#xff0c;研究者可以更清晰地看到卷积运算中的…

[SS]语义分割——基础知识

语义分割前言 一、定义 1、概念 语义分割&#xff08;Semantic Segmentation&#xff09;是计算机视觉中的一项任务&#xff0c;目标是将图像中的每个像素按其语义类别进行分类。与传统的目标检测不同&#xff0c;语义分割对图像中的每个像素都进行分类&#xff0c;而不是只…

Vue 如何把computed里的逻辑提取出来

借用一下百度的ai 项目使用&#xff1a; vue 文件引入 <sidebar-itemv-for"route in routes":key"route.menuCode":item"route":base-path"route.path"click"onColor"/>import { handleroutes } from "./handle…

牛客-寻找第K大、LeetCode215. 数组中的第K个最大元素【中等】

文章目录 前言牛客-寻找第K大、LeetCode215. 数组中的第K个最大元素【中等】题目及类型思路思路1&#xff1a;大顶堆思路2&#xff1a;快排二分随机基准点 前言 博主所有博客文件目录索引&#xff1a;博客目录索引(持续更新) 牛客-寻找第K大、LeetCode215. 数组中的第K个最大元…

C#: CRC8,CRC16,CRC32 校验代码

说明&#xff1a;CRC即循环冗余校验码&#xff08;Cyclic Redundancy Check&#xff09;&#xff1a;是数据通信领域中最常用的一种查错校验码&#xff0c;其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查&#xff08;CRC&#xff09;是一种数据传输检错功能&…

统计学-R语言-5.1

文章目录 前言随机性和规律性概率变量的分布离散型--二项、泊松、几何二项分布几何分布泊松分布 连续型--均匀、正态均匀分布正态分布 其它统计分布--χ2分布、t分布、F分布χ2分布t分布F分布 练习 前言 从本篇文章开始介绍有关概率与分布的介绍。 随机性和规律性 当不能预测…

嵌入式-Stm32-江科大基于标准库的GPIO的八种模式

文章目录 一&#xff1a;GPIO输入输出原理二&#xff1a;GPIO基本结构三&#xff1a;GPIO位结构四&#xff1a;GPIO的八种模式道友&#xff1a;相信别人&#xff0c;更要一百倍地相信自己。 &#xff08;推荐先看文章&#xff1a;《 嵌入式-32单片机-GPIO推挽输出和开漏输出》…

vue-cli解决跨域

在vue.config.js中 找到devServer 在devServer中创建proxy代理 proxy:{ path&#xff08;路径中包含这个path就会导航到target的目标接口&#xff09;&#xff1a;{ target:"目标接口" } } 例&#xff1a; 1 同源策略只针对于浏览器&#xff0c;代理服务器到后端接…

操作系统课程设计-Linux 进程控制

目录 前言 1 实验题目 2 实验目的 3 实验内容 3.1 进程的创建 3.1.1 步骤 3.1.2 关键代码 3.2 子进程执行新任务 3.2.1 步骤 3.2.2 关键代码 4 实验结果与分析 4.1 进程的创建 4.2 子进程执行新任务 5 代码 5.1 进程的创建 5.2 子进程执行新任务 前言 本实验为课…

Electron+React项目打包踩坑记录

首先&#xff0c;如何打包 写下本文的时间是 2024/01/16&#xff0c;搜索了网络上 ElectronReact 的打包方式&#xff0c;中间行不通&#xff0c;本文采用的方式是记录本文时 Electron 快速入门(https://www.electronjs.org/zh/docs/latest/tutorial/quick-start)记录的打包方式…

Stream API 函数式编程 - 告别for循环,代码竟能写的如此优雅?

目录 一、Stream API 函数式编程 1.1、Stream 简介 a&#xff09;为什么引入 Stream&#xff1f;Stream 的出现就是为了让关于集合的操作更加简单&#xff1a; b&#xff09;Stream 的特性&#xff1a; c&#xff09;对stream的操作分为为两类&#xff0c;中间操作 和 结束…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例4-3 textarea

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>textarea</title> </head><body> <h2>多行文本框:</h2> <!--textarea&#xff08;文本域&#xff09;cols(列) rows(行)--> …

Template Engine-06-模板引擎 Handlebars 入门介绍

拓展阅读 java 表达式引擎 logstash 日志加工处理-08-表达式执行引擎 AviatorScriptMVELOGNLSpELJEXLJUELJanino QLExpress 阿里表达式引擎系统学习 什么是 Handlebars&#xff1f; Handlebars 是一种简单的模板语言。 它使用模板和输入对象生成 HTML 或其他文本格式。Ha…

go语言(一)----声明常量

package mainimport ("fmt""time" )func main() {fmt.Print("hello go!")time.Sleep(1 * time.Second)}运行后&#xff0c;结果如下&#xff1a; 1、golang表达式中&#xff0c;加&#xff1b;和不加&#xff1b;都可以 2、函数的{和函数名一…

【Qt之模型视图】1. 模型和视图架构

1. 模型/视图架构是什么及有什么用 MVC&#xff08;Model-View-Control&#xff09;是一种源自Smalltalk的设计模式&#xff0c;通常用于构建用户界面。 MVC由三种类型的对象组成。模型是应用对象&#xff0c;用来表示数据&#xff1b;视图是模型的用户界面&#xff0c;用来显…