当前位置: 首页 > news >正文

Linux·环境变量与进程地址空间

1. 命令行参数

        各位可能见过main函数也是有参数的,只是我们平时写的代码都比较简单,用不到main函数的参数,下面我们看一下main函数的参数是什么又是怎么用的

        我们看这样一段代码

                

        其编译运行后的效果是这样的

                

        我们将main函数后面的那两个参数叫命令行参数列表,argc 表示参数个数,argv[ ] 数组表示参数的清单。        

        我们对代码稍加改造

                        

        就可以达到这样的效果

        同一个程序可以根据命令行参数,根据选项的不同,表现出不同的功能,就比如 ls -a, ls -al 这种命令。

        main函数的命令行参数传递原理大概就是:我们输入的命令是一串字符串,这串字符串先由shell拿到,然后按空格打散形成一张表(argv[]),和元素个数(argc),argv中每个元素都是一个指针指向一个字符串这个效果我们在第一次的实验中也见到了。

2. 环境变量

        main函数的参数还有一个env[ ] 表这张表是该进程的环境变量表,我们先见一见这个表

                        

        env表的最后是空,因此循环结束条件这么写没问题。

        我们编译运行,出来的这24行东西就是这个 ./code 进程的环境变量。

        所有环境变量的格式都是key=value,也就是数字对应属性信息。这些环境变量中有部分是我们认识的,比如3号的SHELL=bin/bash shell的版本是bin/bash的,7号的当前用户,9号一大堆的ls配色方案,12号pwd

        环境变量表都是继承自父进程的,每个进程都有环境变量表。

2.1 几个环境变量

        下面我们感受几个环境变量的作用效果

        如果想查环境变量还可以使用指令 env

2.1.1 PATH

        我们知道,ls 虽然作为系统命令,但是说白了它也是个可执行程序,但是它跟我们自己写的可执行程序又有点不一样,自己写的程序运行时必须要在程序名前加上 ./ 但是 ls 命令却不用。

        这个问题其实我们之前解释过,因为想要执行一个程序,比如 ls 就要让系统知道这个程序的路径是什么,这样才能通过这个路径启动程序,因此自己写的程序要加一个 ./ 表示我要执行的程序就在当前目录下。但是 ls 不用指定路径是因为系统知道要去 /usr/bin 目录下找这个程序,如果我们把自己写的程序放到 /usr/bin 中后也可以不用 ./ 指定路径了,就像系统指令一样执行程序了。

        

        前面说 env指令 可以查所有环境变量,如果要查特定环境变量 echo $PATH 指令

        这样查出来的信息就是PATH指令特定的环境变量,其中每条路径都由分号 : 隔开。

        PATH环境变量的作用就是告诉shell应该去哪些路径下查指令,当shell要执行一个命令行命令的时候,它首先就会按顺序去这些由冒号 : 分割开的多个子路径下查找对应的命令。

        也就是说PATH是系统可执行文件的搜索路径集合。也就是说,我们现在有两种方案来达到不带路径就可以执行程序的效果。1.将程序拷贝到上述任意一条路径下,2.将自己的路径添加到PATH环境变量中去。

        添加的命令就是 PATH=$PATH : 新增路径

        $PATH就是老PATH作为变量放在那里,避免我们再将老PATH敲一遍

        环境变量是内存级别的变量,即使我们这样更改了之后在重启终端之后新增的环境变量更改都会消失。如果说非要保存对环境变量的更改,可以尝试到家目录下vim进这两个文件中更改。

        这个是系统的配置文件,每次启动一个终端,申请操作系统服务的时候,操作系统都会先从这些配置文件中提取到环境变量,如果在这两个文件中修改的话就相当于是在磁盘级别的修改,是可以保存修改的了。

2.1.2 HOME 与 工作目录

        我们可以echo查home环境变量的信息,效果就是展示当前用户的家目录

                

        当用户登录的时候系统要创建对应的bash,而准备bash的时候要读取相关环境变量的配置文件,在此时用户对应的家目录就被写进了HOME环境变量中,因此我们在登录进机器后就直接处在自己的家目录下。

        bash作为一个进程,一定有自己的cwd也就是当前工作目录,我们可以查看一下

        先查bash进程的pid再进入proc目录查看bash进程的cwd发现此时工作目录就是/proc/22574,再回到家目录下重新查看bash进程的当前工作目录,发现变更成了家目录。也就是说bash的当前工作目录是根据当前所在目录变化的,而由于子进程的环境变量又是从父进程继承过来的,因此一般情况下我们运行程序的时候它的工作目录就是它所在的目录。但实际上一个程序的工作目录是取决于,bash的当前工作做目录,而bash的工作目录又取决于当前所在的目录。也就是说如果我们在家目录下用绝对路径启动一个进程,那该进程的工作目录就是家目录。

2.1.3 SHLL

        这个环境变量会记录下来用户登录的时候启动的是哪一个shell,由此会把shell相关的一些可执行程序的路径和程序记录下来,就比如我们现在shell的版本是bash

2.3.4 PWD 与 代码中获取环境变量

        保存当前进程所在工作路径

                

        在程序外我们可以查看PWD环境变量得知当前进程的工作路径,那在代码中获知进程的工作路径也是有必要的。之前我们的获取方案是main函数参数获取环境变量 env[ i ] ,其实还有一种更简单的方案:使用系统调用 char* getenv(const char* )

        ​​​​​​​        

        我们使用一下这个系统调用

        ​​​​​​​                

        其效果就是这样

        可以看到其效果也验证了我们之前在home中所说的工作目录的问题,果然是在哪个目录下打开的程序,那它的工作路径就在哪里。

2.3.5 USER

        USER是当前用户的环境变量

        ​​​​​​​        

        可以看到我用atlanteep的身份和root的身份去运行同一份程序时运行的效果是不一样的,通过这个效果就可以根据环境变量写出控制权限的程序。

2.3.6 OLDPWD 与 cd -

        cd - 命令可以返回上次打开的目录

        这个功能的实现就是通过环境变OLDPWD实现的,这个环境变量会记录下来上次所在的路径

3. 本地变量

        除了环境变量之外在Linux中我们还可以设置本地变量,本地变量与环境变量一样,都是内存级别的变量,重启bash进程之后之前的修改就都没了。

        ​​​​​​​        

        查看本地变量使用 set 命令

        通过 set命令 我们不仅可以看见环境变量,还可以看到我们刚才自己定义的本地变量。

        我们还可以使用 explort命令 将本地变量变成环境变量

        事实上,我们启动机器之后,首先会从磁盘中把OS加载到内存中去,之后用户登录,操作系统就会给该用户分配一个bash命令行解释器进程,那么bash进程会维护3张表,argv[](命令行参数表) env[](环境变量表) 本地变量表

        我们explort就是将变量直接从本地变量表迁移到环境变量表中。

        如果想要移除某个环境变量使用 unset命令

        这样刚才那个a就没有了

        本地变量一般的作用是在运维的时候写一些自动化的脚本的。环境变量是可以被子进程继承下去的,但是本地变量不能被子进程继承。

        环境变量因为有这种继承的特性,因此我们称全局变量具有全局属性

        为什么要让环境变量具有全局属性呢?第一:环境变量是系统的配置信息,尤其是有指导性的一些功能,比如当前工作目录,它是系统配置起效的一种表现。第二:进程虽然具有独立性,但是可以通过环境变量来进行进程间传递数据。

3.1 一种新方案访问环境变量

        我们前面介绍了访问环境变量的两种方案,接下来我们再介绍第三种 environ全局变量 访问环境变量

        它指向环境变量表,因此其类型是 char** 的二级指针类型。

        因此我们可以通过这个指针来获取环境变量

        ​​​​​​​        ​​​​​​​        

        可以看到,通过全局变量environ也可以获取环境变量

4. 进程地址空间

        我们先看一个现象

        我们定义一个全局变量gval,之后从父进程中分出一个子进程,两个进程同时打印各自的信息,同时子进程会修改gval的值。

        因为进程间具有独立性,因此子进程改变全局变量的大小,但是父进程gval的值还是100,这些都符合我们的认知。但是观察gval的地址在子进程和父进程都是一样的,这是没道理的,同一块物理内存上是不可能存两份不同数据的。因此这说明之前我们在 c/c++ 中学到的 &取地址,取出的并不是真正的物理内存地址,这个取出来的地址实际上叫做 虚拟地址 或 线性地址 

        内存空间的地址(虚拟地址)分配如下图

        ​​​​​​​        ​​​​​​​        ​​​​​​​        

        由低地址到高地址分别是,正文代码、初始化全局数据,未初始化全局数据、堆、栈、命令行参数环境变量,但是堆和栈之间还有一块很大的空间是共享区。

        上图的空间分配方案应该叫进程地址空间,它是逻辑上存在的,而不是真实的物理内存的分配方案。

        这个进程地址空间存在的意义是为了让每一个进程都认为自己是独占机器物理内存的大小,进程之间彼此不知道对方的存在,从而实现一定程度上的隔离和便于管理。

        形象一点讲,这个进程虚拟地址空间是操作系统给进程话的饼,告诉进程:你那些数据我都如上图那样放进物理内存了,你要用随时可以来取用。但其实物理内存中根本就不是那么存放的。既然操作系统给进程画了饼,那肯定也会给进程一个兑换饼的凭证,使进程可以用这个凭证来从逻辑上存在的内存地址中向OS申请,取到对应数据。

        这个凭证具体来说是一个内核数据结构对象,针对Linux操作系统来讲就是struct mm_struct进程地址空间

        pcb中会存储这个凭证的指针,后面可以通过这个凭证向OS索要物理内存的使用权。

        mm_struct中描述的就是进程地址空间的逻辑,那进程地址空间又是由各种数据段组成的,其区域的划分就是用某区域的起始地址和结束地址,存放在 mm_struct 中用来给进程地址空间做变量存放地址的指导,注意还是那句话这里划分出来的区域也是虚拟的并不是给真正的物理内存划分。

        在mm_struct创建的时候其中具体哪一块空间要开多大都是在可执行程序中写好的,但是像栈区堆区这种运行中使用的地址空间就是在运行中申请的。

        struct mm_struct作为虚拟地址存放表,依靠页表将虚拟地址映射到物理内存的真实地址中。

        当我们创建一个子进程的时候,不仅仅要从父进程那里把PCB继承过来,还要把mm_struct虚拟地址空间继承过来,接着再把父进程的页表继承过来。

        也就是说在不修改变量的时候,子进程的虚拟地址空间、页表、甚至页表映射的物理内存与父进程都是完全一致的,也就是说它们的数据,代码全都是共享的。

        但是如果此时子进程(或父进程),对于某个数据进行了更改,此时操作系统就会通过该变量的虚拟地址找到真实物理地址,然后将物理地址中的内容拷贝一份再开一份新空间,将新数据放到物理内存中,然后把页表中的所映射的物理内存更新,但是虚拟地址不变。这就是为什么我们一开始看到父进程和子进程的全局变量地址相同但数据不同的原因,因为我们看到的地址只是虚拟地址,但实际上子进程的相同虚拟地址所映射的真实物理地址却与父进程不同了。这种操作叫做OS的写时拷贝机制

4.1 页表

        我们这里只是粗谈一下页表做一下铺垫,之后有章节详说

        页表的结构并没有我们刚才画的那么简单,并不是简单的提供虚拟地址映射物理内存的路径,它里面还有一些标志位,这次先说两个 rwx isexists

        ​​​​​​​        ​​​​​​​        

        rwx标志很简单,就是看这个变量有没有权限读写内存,如果没有权限读写却硬要写,那OS就直接杀进程。比如一个字符串常量,代码跑到了要修改这个常量的地方,要修改就要访问内存,直接查看页表,发现没有写权限,那就拒绝写入,杀进程。这个拒绝写入的事情是操作系统在做,因此编译器检查不出来错误,但是为了方便程序员自查,于是编译器提供const关键字来提示程序原一个变量在内存中是否有写权限。

        isexists标志表示一个变量是否真正加载到物理内存当中去了。比如一个黑猴有130G但是我电脑的内存只有4G那玩的时候不可能把整个黑猴都加载进内存,只能一部分一部分的加载,比如开局剧情跑完了,之后很长一段时间肯定都不会用到这段代码了,那就将这段代码踢出内存并在这个标志位标注,或者后面很久才会用到的代码也不会加载进内存。这种操作叫分批加载,或者分批挂起。

相关文章:

Linux·环境变量与进程地址空间

1. 命令行参数 各位可能见过main函数也是有参数的,只是我们平时写的代码都比较简单,用不到main函数的参数,下面我们看一下main函数的参数是什么又是怎么用的 我们看这样一段代码 其编译运行后的效果是这样的 我们将main函数后面的那两个参数叫…...

MYSQL 乐观锁

乐观锁是一种用于处理并发控制的策略,特别适用于读多写少的场景。在 MySQL 数据库中,乐观锁通常通过版本号或时间戳来实现。下面将详细介绍乐观锁的概念、实现方式以及在 MySQL 中的应用。 1. 乐观锁的概念 乐观锁的基本思想是:在对数据进行…...

SpringCloud入门(十二)全局过滤器和跨域

一、全局过滤器 全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。 区别在于GatewayFilter通过配置定义,处理逻辑是固定的,如果我们希望拦截请求,做自己的业务逻辑则没办法实现。而GlobalFilt…...

51单片机系列-按键检测原理

🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” 独立按键是检测低电平的。 下面我们来看一张对应的电路原理图: 在这张图当中,P1,P2,P3内部都上拉了电阻,但是P0没有&am…...

基于元神操作系统实现NTFS文件操作(五)

1. 背景 本文主要介绍$Root元文件的解析。先介绍元文件的构成及各个部分的结构,然后结合上一篇博文中读取到的元文件内容,对测试磁盘中目标分区的根目录进行展示。 2. $Root元文件解析 (1)$Root元文件的结构 $Root元文件由两部…...

AutoCAD学习

AutoCAD学习 最基本操作 命令用途说明空格键确认键也可以是重复刚才的命令回车键也是确认键鼠标右键也可以选择确认LINE、L直线命令绘制直线DLI线性尺寸标注DIMLINEAR鼠标滚轮滚动放大缩小视图界面鼠标中键按住移动视图DAL对齐线性标注DIMALIGNED F8 正交模式ORTHOMODE Tab 切换…...

go的一些知识点

一.package 1.新建项目 新建一个itying文件夹,在里面使用命令 就能生成一个go项目。生成一个go.mod 2.调用别的包的代码 按照下面的目录层级生成代码 //clac.go package calcfunc Add(x, y int) int {return x y } func Sub(x, y int) int {return x - y }…...

前端 vue3 对接科大讯飞的语音在线合成API

主要的功能就是将文本转为语音,可以播放。 看了看官方提供的demo,嗯....没看懂。最后还是去网上找的。 网上提供的案例,很多都是有局限性的,我找的那个他只能读取第一段数据,剩下的不读取。 科大讯飞的接口&#xf…...

缺省参数

一、概念 在声明或定义函数时为函数的参数指定一个默认值&#xff0c;调用时&#xff0c;如果对应参数没有传参&#xff0c;则使用其默认值&#xff0c;否则使用指定的实参 void TestFunc(int a 0) {cout<<a<<endl; }int main() {TestFunc(); // 没有传参&am…...

Stable Diffusion绘画 | 来训练属于自己的模型:炼丹启动

经过前面几轮辛苦的准备工作之后&#xff0c;现在开始进入终篇的炼丹环节。 在「上传素材」页面&#xff0c;点击「开始训练」&#xff1a; 可以在「查看进度-进度」中&#xff0c;查看模型训练的整体进度&#xff1a; 求助&#xff01;&#xff01;&#xff01;操作「开始训练…...

08_OpenCV文字图片绘制

import cv2 import numpy as npimg cv2.imread(image0.jpg,1) font cv2.FONT_HERSHEY_SIMPLEXcv2.rectangle(img,(500,400),(200,100),(0,255,0),20) # 1 dst 2 文字内容 3 坐标 4 5 字体大小 6 color 7 粗细 8 line type cv2.putText(img,flower,(200,50),font,1,(0,0,250)…...

【笔记】选择题笔记+数据结构笔记

文章目录 2014 41方法一先序遍历方法二 连通分量是极大连通子图 一个连通图的生成树是一个极小连通子图 无向图的邻接表中&#xff0c;第i个顶点的度为第i个链表中的结点数 邻接表和邻接矩阵对不同的操作各有优势。 最短路径算法: 单源最短路径 已知图G(V,E)&#xff0c;我们…...

浅谈汽车智能座舱如何实现多通道音频

一、引言 随着汽车智能座舱的功能迭代发展&#xff0c;传统的 4 通道、6 通道、8 通道等音响系统难以在满足驾驶场景的需求&#xff0c;未来对于智能座舱音频质量和通道数会越来越高。接下来本文将浅析目前智能座舱如何实现音频功放&#xff0c;以及如何实现多路音频功放方案。…...

系统架构设计师教程 第13章 13.1层次式体系结构概述 笔记

13.1 层次式体系结构概述 分层式体系结构是一种最常见的架构设计方法&#xff0c;能有效地使设计简化&#xff0c;使设计的系统机构清晰&#xff0c;便于提高复用能力和产品维护能力。 层次式体系结构设计是将系统组成一个层次结构&#xff0c;每一层为上层服务&#xff0c;并…...

cnn突破一(先搞定三层反馈神经网络bpnet,c#实现)

惦记cnn很久了&#xff0c;一直搞机器视觉&#xff0c;走不出来&#xff0c;现在megauging已经实现&#xff0c;说明书也写了不少&#xff0c;该突破的突破了&#xff0c;该改进的也改进了&#xff0c;一个心病治好了&#xff0c;有空把人工智能在机器视觉上的延伸&#xff0c;…...

如何创建一个docker,给它命名,且下次重新打开它

1.创建一个新的docker并同时命名 docker run -it --name one ubuntu:18.04 /bin/bash 这时候我们已经创建了一个docker,并且命名为"one" 2.关闭当前docker exit 3.这时docker已经终止了&#xff0c;我们需要使用它要重新启动 docker start one 4.现在可以重新打…...

【D3.js in Action 3 精译_025】3.4 让 D3 数据适应屏幕(中)—— 线性比例尺的用法

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…...

Python的多线程与多进程:并发编程基础与实战

随着计算机硬件的不断发展,现代计算机通常配备多核处理器,使得在程序中同时处理多个任务成为可能。并发编程是提升程序性能、充分利用多核处理器能力的重要技术之一。在Python中,并发编程的实现主要包括多线程、多进程以及异步编程(如asyncio)。然而,由于Python的全局解释…...

HarmonyOS Next应用开发——响应式布局之媒体查询

响应式布局之媒体查询 媒体查询作为响应式设计的核心&#xff0c;在移动设备上应用十分广泛。媒体查询可根据不同设备类型或同设备不同状态修改应用的样式&#xff0c;常用于多屏幕的应用适配。媒体查询常用于下面两种场景&#xff1a; 针对设备和应用的属性信息&#xff08;…...

240 搜索二维矩阵 II

解题思路&#xff1a; \qquad 解这道题最重要的是如何利用从左到右、从上到下为升序的性质&#xff0c;快速找到目标元素。 \qquad 如果从左上角开始查找&#xff0c;如果当前matrix[i][[j] < target&#xff0c;可以向右、向下扩展元素都是升序&#xff0c;但选择哪个方向…...

jenkins微服务

如果vim进去某个文件里&#xff0c;可以按键盘的向下键查阅其它部分 记得每天备份虚拟机的项目 一.在linux安装jenkins 1.上传文件 我们采用安装包的方式安装。 先用SShclient在/usr/local/下创建jenkins文件夹&#xff0c;然后向其中导入两个包 2.安装jenkins 再在控制…...

【Kotlin基于selenium实现自动化测试】初识selenium以及搭建项目基本骨架(1)

导读大纲 1.1 Java: Selenium 首选语言1.2 配置一个强大的开发环境 1.1 Java: Selenium 首选语言 Java 是开发人员和测试人员进行自动化 Web 测试的首选 Java 和 Selenium 之间的协同作用受到各种因素的驱动,从而提高它们的有效性 为什么Java经常被认为是Selenium的首选语言 广…...

汽车追尾为什么是后车的责任?

简单点说&#xff1a;因为人后面没有长眼睛。 结论 在汽车追尾事故中&#xff0c;通常情况下后车被认为是责任方的原因在于交通法规对驾驶安全标准的约定和实践中的责任识别原则。虽然追尾事故常见地被归责于后车&#xff0c;但具体判断并不是绝对的&#xff0c;仍需综合多种…...

[运维]4.bookinfo无法部署的问题

为了拉取镜像&#xff0c;搭建了阿里云镜像仓库&#xff0c;教程见&#xff1a;K8S中基于NFS-Subdir-External-Provisioner存储组件实现的StorageClass-CSDN博客 但是bookinfo的ratings和productpage无法运行&#xff0c;部署后显示crashLoopBackOff [rootmaster ~]# kubectl…...

ACT调试pycharm报错

在运行ACT 代码时&#xff0c;根据官方readme使用命令行需要在wandb选择的时候输入3 但是&#xff0c;使用pycharm运行的时候会报错 wandb.errors.UsageError: api_key not configured (no-tty). call wandb.login(key[your_api_key]) 网上搜索都是说要注册什么key&#xf…...

记一次控件提升后,运行却不显示的Bug

.h文件 #ifndef VOLUMETOOLBTN_H #define VOLUMETOOLBTN_H#include <QToolButton> #include <memory>class VolumeToolBtn : public QToolButton { Q_OBJECTpublic:explicit VolumeToolBtn(QWidget *parent nullptr);~VolumeToolBtn() override;void initUi(); p…...

关于深度学习torch的环境配置问题

已经下好了torch在虚拟环境中&#xff0c;结果在ipynb文件中无法运行 后来在终端直接用python语句编译 发现没有问题 在编辑测试py文件 发现runcode有问题 原来是插件默认base环境 具体操作参考VS Code插件Code Runner使用python虚拟环境_coderunner怎么在虚拟环境中使用-CSD…...

Linux工具的使用——yum和vim的理解和使用

目录 linux工具的使用1.linux软件包管理器yum1.1yum的背景了解关于yum的拓展 1.2yum的使用 2.Linux编辑器-vim使用2.1vim的基本概念2.2vim的基本操作2.3命令模式命令集2.3.1关于光标的命令&#xff1a;2.3.2关于复制粘贴的命令2.3.3关于删除的命令2.3.4关于文本编辑的命令 2.4插…...

websockets库使用(基于Python)

主要参考资料&#xff1a; 【Python】websockets库的介绍及用法: https://blog.csdn.net/qq_53871375/article/details/135920231 python模块websockets&#xff0c;浏览器与服务器之间的双向通信: https://blog.csdn.net/randy521520/article/details/134752051 目录 websocke…...

Electron 主进程与渲染进程、预加载preload.js

在 Electron 中&#xff0c;主要控制两类进程&#xff1a; 主进程 、 渲染进程 。 Electron 应⽤的结构如下图&#xff1a; 如果需要更深入的了解electron进程&#xff0c;可以访问官网 流程模型 文档。 主进程 每个 Electron 应用都有一个单一的主进程&#xff0c;作为应用…...