【linux进程(六)】环境变量再理解&程序地址空间初认识

简介: 【linux进程(六)】环境变量再理解&程序地址空间初认识

1. 前言

如果你不知道什么是环境变量PATH

请先阅读下面的文章:

初识环境变量

本章重点:

本篇文章着重于完善上一篇文章遗留
的一些环境变量的问题,认识其他几个
常见的环境变量,以及在bash下查看
环境变量和添加环境变量的方式,以及
拓展main函数的第三个参数:env.
最后对程序地址空间的内容开个头!


2. 在bash中查看所有环境变量

在bash中查看所有环境变量:

使用指令: env

可以看见打印出密密麻麻的环境变量

这其中不需要我们全部记住,只需简单

的认识几个常见的环境变量即可!

  1. 环境变量PWD:

记录当前路径,你以为平时执行的
pwd指令是怎么知道当前路径的?
稍加思考就能窥探,pwd实际上调用
了PWD环境变量!

  1. 环境变量HOME

我们平时使用的指令:cd ~进入
家目录,哪儿是家目录?环境变量
HOME里面的内容就是家目录!

  1. 简述登录Xshell时,所要做的事

根据以上对环境变量的认识,我们发现
登录xshell时我们用root账户登录就
会跑到root的家目录下,这是因为在
启动Linux时,OS帮我们做了一些事情

  • 输入用户名,密码
  • 认证信息是否正确
  • 形成环境变量(PATH,PWD,等等)
  • 根据用户名初始化HOME环境变量
    HOME=root或HOME=XXX
  • cd $HOME(进入家目录)

3. 程序中获取环境变量的方式

我们知道,Linux是由C语言写的

所以这里的程序我们默认是C程序

使用函数: getenv()

使用方法如下:

char* p = NULL;
p = getenv("PATH");

p就能获取环境变量PATH的内容了

即函数的参数是环境变量的名字

返回值是环境变量的内容!


4. main函数的第三个参数解析

不卖关子,直接写出来:

int main(int argc,char* argv[],char* env[])

第三个参数为env,是不是很熟悉?
没错,第三个参数就是环境变量

它和第二个参数一样是指针数组

它指向环境变量表,环境变量表中的

内容和在bash中使用env打印的一样!

到目前为止了解到,系统在启动

程序时会给main函数提供两张表:

  • 命令行参数表
  • 环境变量表

通过下面的代码查看环境变量表:

#include<stdio.h>
int main(int argc,char* argv[],char* env[])
{
  for(int i=0;env[i]!=NULL;i++)
  {
    printf("[%d]: %s"\n,i,env[i]);
  }
  return 0;
}

5. 本地的与env中的环境变量

我们可以在bash中直接定义环境变量

使用指令: 环境变量名=内容

比如:

用户自己定义的环境变量是本地的

而env并不能查看本地的环境变量

所以使用env查找刚才定义的环境变量

时,实际上会找不到!

如果想要我们定义的环境变量
被放在系统的环境变量表中应该怎么做?

使用指令: export 环境变量名

还可以使用export定义环境变量:


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

根据上面的一些信息我们可以推论:

在修改/定义环境变量时,实际上
修改/定义的是bash进程内部的
环境变量信息,每一次重新登录都会
形成新的bash解释器,并且新形成
的bash解释器会自动从家目录下
bash_profile文件中读取信息形成
一份新的环境变量表!

所以为啥每次重新启动bash后
我们自己定义/修改的环境变量
就不见了?因为它并没有被保存在
配置文件bash_profile中!

查看配置文件bash_profile:

当我们在配置文件中加上自己想要

一直存在的环境变量时,此时bash再

次启动后,这个环境变量就会被放到

环境变量表中!

并且在创建子进程时
这张表会通过main函数的参数传递
给子进程,所以子进程有了和父进程
一样的环境变量表,而子进程创建孙子
进程时又会将子进程的表给孙子进程
所以说,环境变量具有全局性!

本地变量 VS 环境变量
1. 本地变量只在bash进程内部有效,不会被子进程继承
2. 环境变量通过让所有子进程继承的方式,实现自身的全局性

7. 内建命令与常规命令

我们知道一个事实,bash中的指令

可以直接使用,不用加./是因为它的

路径在环境变量PATH中,所以我们

将PATH置空后,这些命令就不能运行了!

我们会发现一个问题,PATH被置空后

确实有些命令是跑不了了.但是某些

命令还是能跑,比如pwd命令还能正常

输出,这是为啥呢?

Linux下的命令父分类:

  • 常规命令:bash创建子进程执行的
  • 内建命令:bash自己执行的(类似于一个函数)

很明显pwd是内建命令,echo也是

内建命令,难道你就没有一个疑惑吗?

为什么我们自己定义的环境变量在
环境变量表env中找不到,但是echo
命令却可以将它的内容打印出来?
echo程序也是bash的一个进程
为什么它能获取我们定义的环境变量?
答案是echo是内建命令,是父进程
自己内部执行的,所以它可以看见
父进程的变化,但是其他的子进程不能
接受父进程的本地变量,所以看不见!


8. 程序地址空间初认识

C/C++程序员认为,程序的内存分布是这样的:

堆是向上增长的,栈是向下增长的

这个我们在以前的学习中以及有所

了解了,那为什么这里还要提呢?

有两个目的:

  1. 再对以前的知识做复习
  2. 在原有知识的基础上拓展

这其中有两个地方需要注意:

在写C程序时定义的常量字符串
实际上在上面区域的正文代码区
因为它和正文代码区紧挨着,所以
常量区的字符串不允许修改!
第二点是,栈虽然说是向下增长的
但是栈中的数组,结构体等结构的
地址是向上增长的,比如说开辟数组
时,开辟十个空间,那么数组中第一个
元素在空间的最下面,也就是地址最低
处,然后依次往上放后面的元素


9. 深入探究地址空间与物理内存

现在写上这样一段代码来深入探究地址:

#include <stdio.h>    
#include <unistd.h>    
#include <sys/types.h>    
#include<stdlib.h>    
int main()    
{    
    int id=fork();    
    int tmp=10;    
    if(id==0)//子进程执行的代码    
    {    
        tmp=20;    
        while(1)    
        {    
            printf("子进程,tmp: %d,&tmp: %p\n",tmp,&tmp);    
            sleep(1);    
        }    
    }    
    if(id>1)//父进程执行的代码    
    {    
        while(1)    
        {    
            printf("父进程,tmp: %d,&tmp: %p\n",tmp,&tmp);    
            sleep(1);    
        }    
    }                                                                                                                                                                   
    return 0;    
}

我们发现,tmp的值在父子进程中

是不同的,可以我们暂时可以理解,

是因为写时拷贝, 但是相同的地址
为什么会有不同的值?
这就有一点

挑战我们之前学习的知识了!

以目前的知识储备是无法想明白这个问题的

但是我们可以得出一个小结论:

我们平时写的堆区,栈区,静态区
等等区,并不是真实的物理地址!
因为一个物理地址不可能有两个值
那么真实的物理地址到底是什么?
如果这个不是真实的物理地址那么
这个地址又是什么东西呢?我们打印
出来的地址又是什么东西?以上内容
将在下一篇文章讲解!


10. 总结

了解环境变量相关知识可以更好的

理解操作系统在启动时所做的工作,

以及可以更好的了解进程在运行的

时候需要的一些数据从何而来!

本篇文章不再做拓展,更多内容
将在下一篇文章中讲解


🔎 下期预告:程序地址空间 🔍


相关文章
|
2月前
|
安全 Linux Shell
Linux上执行内存中的脚本和程序
【9月更文挑战第3天】在 Linux 系统中,可以通过多种方式执行内存中的脚本和程序:一是使用 `eval` 命令直接执行内存中的脚本内容;二是利用管道将脚本内容传递给 `bash` 解释器执行;三是将编译好的程序复制到 `/dev/shm` 并执行。这些方法虽便捷,但也需谨慎操作以避免安全风险。
179 6
|
3月前
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
637 2
|
1月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
35 1
|
2月前
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
42 5
|
5月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
126 13
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
166 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
3月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。
|
4月前
|
存储 缓存 安全
【Linux】冯诺依曼体系结构与操作系统及其进程
【Linux】冯诺依曼体系结构与操作系统及其进程
171 1
|
4月前
|
小程序 Linux
【编程小实验】利用Linux fork()与文件I/O:父进程与子进程协同实现高效cp命令(前半文件与后半文件并行复制)
这个小程序是在文件IO的基础上去结合父子进程的一个使用,利用父子进程相互独立的特点实现对数据不同的操作
105 2