【Linux笔记】Linux环境变量与地址空间

【Linux笔记】Linux环境变量与地址空间

  • 一、命令行参数
    • 1.1、main函数的参数
    • 1.2、main函数的第三个参数
  • 二、环境变量的概念与内容
    • 2.1、环境变量的概念
    • 2.2、环境变量的分类
    • 2.3、环境变量的组织形式
    • 2.4、常见的环境变量
  • 三、设置环境变量
    • 3.1、通过命令获取或设置环境变量
    • 3.2、通过代码获取获取环境变量
    • 3.3、通过系统调用获取或修改环境变量
  • 四、进程地址空间的概念
    • 4.1、C/C++程序员认为的空间排布
    • 4.2、虚拟地址与物理地址
    • 4.3、地址空间与物理内存之间转换
    • 4.4、理解为什么要有地址空间

一、命令行参数

1.1、main函数的参数

我们以前在写C/C++程序的main函数的时候,应该都是不带参数的:

int main() {
	printf("hello world!!\n");
	return 0;
}

这可能会让人形成一种误解,为main函数式不能带参数的。

但事实上,main函数是可以带参数的,如果是学过java的朋友可能很清楚这一点:

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

	return 0;
}

从参数的形式可以看出,一个是int类型的变量一个是一个指针数组,可这是用来干嘛的啊?
其实这个argv里存的每一个字符指针都指向一个命令行参数,而argc这是这些参数的个数,我们可以在程序中打印出这些参数:
在这里插入图片描述
如上图,其实我们在运行我们自己所写的程序时候也可以传入一些参数,而系统会将我们呢传递的这些参数以空格为分隔,将它们转化成字符串放入argv数组中,然后我们就可以在程序中打印出这些字符串了,而第一个字符串是程序的名字,这是怎样都会存在的。
注意: 打印这些字符串需要c99才支持,所以我们需要再makefile文件中指定编译标准为c99。

但这又有什么用呢?难道只是为了打印这么简单吗?

其实我们类比我们平时在shell里面敲的各种指令就很好理解了。
例如ls指令,我们不单单可以只执行ls,也可以加入各种选项让它以不同的方式运行比如ls -l表示显示出文件的详细信息,ls -a表示显示出所有文件包括隐藏文件。

而我们平时写的各种C/C++程序运行起来后都是一个进程,shell中的各种指令运行起来也都是进程,所以我们也可以通过命令行参数的形式来是我们自己写的程序以不同的方式运行。

我们也可以将其类比成函数传参,传入不同的参数函数就会计算出不同的结果,只不过这些参数是程序外传进来的:
在这里插入图片描述

1.2、main函数的第三个参数

其实main函数还有第三个参数env,这第三个参数就与我们今天要讲的环境变量有关了,它的本质也是一个字符指针数组,其中每个指针执行的都是一个环境变量,我们也可以先将它打印出来看看鲜:
在这里插入图片描述

二、环境变量的概念与内容

2.1、环境变量的概念

大家可能都或多或少的听说过 “环境变量” 这个词,当我们在安装一些软件或是语言例如java、python时都有要求过配置环境变量。

其本质目的就是为了操作系统能直接找到对应的软件或者指令,也可以理解成是在操作系统环境中的一个变量,所以操作系统能直接的找到它。

这时候我们再看看对于环境变量比较学术的概念就会好理解一会点了:

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。
环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息。例如Windows和DOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。用户通过设置环境变量,来更好的运行进程。

2.2、环境变量的分类

环境变量可以按作用范围和生存周期分为两类:
按作用范围分类:

环境变量: 相当于全局变量,存在于所有的Shell中,且具有继承性。
本地变量: 相当于局部变量,只存在于当前的Shell中,本地变量包含环境变量,但非环境变量不具有继承性。

按生存周期分类:

永久性环境变量: 需要修改配置文件,变量永久保存。
暂时性环境变量: 使用export定义,关闭Shell后失效。

2.3、环境变量的组织形式

我们所创建的所有进程其实都对应有一张环境变量表,这一张表其实就是一个字符指针数组(也可以理解成是一个字符串数组),其结构大致如下:
在这里插入图片描述
环境变量表中的每一个指针都指向一个字符串,字符串的内容其实就是一些描述信息,最后以NULL结尾。

2.4、常见的环境变量

我们可以使用一下指令来查看一些常见的环境变量:

echo $环境变量名

1、PATH
PATH变量指定我们输入的各种指令的搜索路径:
在这里插入图片描述

2、HOME
该变量指定用户的主工作目录,即用户登录到Linux系统时,默认的目录:
在这里插入图片描述
以为上面用的是普通用户,所以还会多带个home,如果使用的是root用户,就不会带home了:
在这里插入图片描述
其中的原因也很容易理解,因为root是超级管理员,他当然能访问的范围就是最广的啦,而普通用户的权限较小,所以要放在/home工作目录中进行管理。

3、HISTSIZE
该变量查看Linux系统保存历史命令的数目:
在这里插入图片描述
其显示结果说明当前Linux系统中最多保存1000条历史命令。

4、LOGNAME
该变量指定显示当前的用户名:
在这里插入图片描述
5、HOSTNAME
该变量显示当前主机名称:
在这里插入图片描述
6、SHELL
该变量显示用户当前使用的解析器:
在这里插入图片描述

三、设置环境变量

3.1、通过命令获取或设置环境变量

我们可以通过一些命令来获取或设置环境变量。
1、echo
该命令能显示某一个环境变量的值。
这个命令我们上面已经演示过了:
在这里插入图片描述
2、env
该命令可以显示所有的环境变量:
在这里插入图片描述
3、set
set命令显示本地定义的shell变量和环境变量:
在这里插入图片描述
4、export
export命令可以创建一个新的环境变量:
在这里插入图片描述
使用export创建的环境变量值是一个暂时性的环境变量,如果关闭shell再重启就会失效。

5、unset
unset 命令功能是清除指定的环境变量:
在这里插入图片描述
6、readonly
该命令用于设置只读的环境变量:
在这里插入图片描述
只读的环境变量就是只能读取而不能修改,消除的办法只有重启shell。

3.2、通过代码获取获取环境变量

我们可以通过main函数的第三个参数——环境变量地址指针数组 来获取环境变量:
在这里插入图片描述
其打印的结果几乎和我们在命令行使用env打印出来的一样:
在这里插入图片描述

我们也可以通过第三方变量environ获取环境变量:
在这里插入图片描述
变量environ是系统定义的一个全局的二级指针,它也是指向一个字符指针数组,也可以理解为指向一个环境变量表。

3.3、通过系统调用获取或修改环境变量

我们也可以通过getenv这个系统调用来访问特定的环境变量。
getenv可以获取一个环境变量的地址,我们可以使用一个字符指针来接收:
在这里插入图片描述

四、进程地址空间的概念

4.1、C/C++程序员认为的空间排布

在学习C/C++的时候,我们都会认为内存的划分为这几个区域:栈区、堆区、全局/静态区、代码区、字符常量区……
在这里插入图片描述

这其实只是在语言层面的理解,如果要放到系统层面还是不是这样呢?我们知道一个系统的内存是很大的,要管理的东西也很多,难道就只有上面这些吗?还有这是物理内存吗?

如果要放到系统层面来理解,这里就要给出一个结论:这不是物理内存,这是虚拟内存或进程地址空间。

4.2、虚拟地址与物理地址

在解释虚拟地址与物理地址之前,我们先来看看一个我们以前无法理解的现象。
这是代码:
在这里插入图片描述
这是运行结果:
在这里插入图片描述
我们会惊讶的发现两个进程访问了相同的地址,但是访问到的值竟然是不同的。这要是在以前我们可能会觉得这是不是操作系统出bug了!

其实操作系统并没有出bug,恰恰相反在这一点上操作系统做得很好。

我们知道,父子进程的代码是共享的,而数据是个自有一份的,并且观察以上的结果我们可以推断出变量的值不一样,说明进程中的变量绝对不是同一个变量。既然不是同一个变量,那访问的物理地址也绝不会是同一个。

所以我们曾经在C/C++中用学习到和看到的地址全都是虚拟地址,而物理地址,用户是一概看不到的。

但是程序的代码和数据是一定要存在物理内存上的,因为运行程序之前一定要现将代码和数据加载到物理内存中。

所以操作系统要做的就是将虚拟地址转化成物理地址

4.3、地址空间与物理内存之间转换

其实虚拟地址和物理地址之间的转换主要是通过他们中间的一个“页表”来完成的:
在这里插入图片描述
图中父子进程的g_val的虚拟地址是一样的,但它们通过各自的页表映射到了不同的物理地址。
所以他们实际访问的物理地址是不同的,所以也就能访问到不同的值了。

而为什么说是虚拟地址呢?
其实在Linux中地址空间其实是Linux内核中的一种数据结构,在 Linux 中,OS 除了会为每个进程创建对应的 PCB(即 struct task_struct 结构体),还会创建对应的进程地址空间,即内核中的 struct mm_struct 结构体。
而在这个struct mm_struct 结构体中是通过顶一个各种start和end变量来划分进程地址空间的:

struct mm_struct {
    // ...
    
    unsigned long code_start;   // 代码区起始虚拟地址,比如 0x10000000h
    unsigned long code_end;     // 代码区结束虚拟地址,比如 0x00001111h
    
    unsigned long init_start;   // 已初始化数据区
    unsigned long init_end;
    
    unsigned long uninit_start; // 未初始化数据区
    unsigned long uninit_end;
    
    unsigned long heap_start;   // 堆区
    unsigned long heap_end;
    
    // ...
};

4.4、理解为什么要有地址空间

直接让进程去访问物理内存不就好了吗?为什么还要存在地址空间呢?
其实操作系统这样做也是出于安全着想,在早期的操作系统中是没有进程地址空间的,导致物理内存暴露,恶意程序可以直接通过物理地址,进行内存数据的读取,甚至篡改。

后来随着操作系统的发展迭代,有了进程地址空间(虚拟地址),由操作系统完成虚拟地址和物理地址之间的转化。

这样那些恶意程序就没法直接得到物理地址,我们的操作系统也能更好的受到保护。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#中基于.NET6的动态编译技术

前几天要解决动态计算问题,尝试着使用了不同的方法。问题是给定一个包含计算的字符串,在程序运行中得到计算结果,当时考虑了动态编译,在网上查了一些资料完成了这项功能,可是基于不同的.NET平台使用的编程代码相差比较…

【Git】推送Github失败:remote: Permission to xxx/*.git denied to xxx

在github上&#xff0c;创建了token&#xff0c;推送代码报没权限 #设置token git remote set-url origin <your.token>github.com/<your.name>/hello-git.git#推送代码 #git push -u origin main remote: Permission to xxx/hello-git.git denied to xxx. fatal:…

vscode文件跳转(vue项目)

在 .vue 文件中&#xff0c;点击组件名打开 方式1&#xff1a; 在 vue 组件名上&#xff0c;桉住ctrl 鼠标左键 // 重新打开一个tab 方式2&#xff1a; 在 vue 组件名上&#xff0c;桉住ctrl shift 鼠标左键 // 在右侧拆分&#xff0c;并打开一个tab .vue文件的跳转 按住 …

DevChat:VSCode中基于大模型的AI智能编程助手

#AI编程助手哪家好&#xff1f;DevChat“真”好用# 文章目录 1. 前言2. 安装2.1 注册新用户2.2 在VSCode中安装DevChat插件2.3 设置Access Key 3. 实战使用3.1 代码编写3.2 项目创建3.3 代码讲解 4. 总结 1. 前言 DevChat是由Merico公司精心打造的AI智能编程助手。它利用了最先…

Figma切图,轻松上手!

对于UI设计师来说&#xff0c;在设计网页或移动应用界面时&#xff0c;不仅需要考虑视觉效果和用户体验&#xff0c;还需要考虑实际开发过程中的实现。例如&#xff0c;与开发人员合作&#xff0c;将设计草案中的图片、图标、插图等元素转换为网页或移动应用程序的代码&#xf…

PCF8574芯片介绍及驱动方法

文章目录 前言一、PCF8574芯片介绍二、PCF8574读写地址确定三、PCF8574读写模式传输数据四、PCF8574准双向I/O口五、PCF8574驱动程序编写总结 前言 本篇文章带大家学习PCF8574芯片&#xff0c;了解PCF8574芯片有什么作用&#xff0c;以及学习PCF8574的控制方法。 一、PCF8574…

【kafka】Java客户端代码demo:自动异步提交、手动同步提交及提交颗粒度、动态负载均衡

一&#xff0c;代码及配置项介绍 kafka版本为3.6&#xff0c;部署在3台linux上。 maven依赖如下&#xff1a; <!-- kafka --><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka_2.13</artifactId><version>3.6.0…

Dapp开发流程以及应用

随着区块链技术的发展和普及&#xff0c;Dapp&#xff08;去中心化应用&#xff09;逐渐成为了区块链领域中备受关注的话题。Dapp是一种运行在区块链网络上的应用程序&#xff0c;具有去中心化、透明、安全、自治等特点&#xff0c;能够为人们提供更加便捷、高效、安全的应用体…

Stable Diffusion webui 源码调试(一)

Stable Diffusion webui 源码调试&#xff08;一&#xff09; 个人模型主页&#xff1a;LibLibai stable-diffusion-webui 版本&#xff1a;v1.4.1 内容更新随机&#xff0c;看心情调试代码~ 调试txt2img的参数和工作流 文件 /work/stable-diffusion-webui/modules/txt2img…

Rust和isahc库编写代码示例

Rust和isahc库编写的图像爬虫程序的代码&#xff1a; rust use isahc::{Client, Response}; fn main() { let client Client::new() .with_proxy("") .finish(); let url ""; let response client.get(url) .send() …

rtklib进行PPK解算

使用RTKLIB_bin-rtklib_2.4.3&#xff0c; 打开RTKPOST。 配置相关文件的路径&#xff0c;如果没有广播星历&#xff0c;则到武汉大学IGS数据中心 下载。 打开Options&#xff0c;进行配置。 点击执行 解算中 查看成果

使用OkHttp库爬取百度云视频详细步骤

目录 摘要 一、OkHttp库简介 二、爬虫基本概念 三、使用OkHttp库爬取百度云视频 1、发送HTTP请求 2、处理响应 3、下载文件 四、可能遇到的问题及解决方案 五、注意事项 总结与建议 摘要 本文将详细介绍如何使用OkHttp库爬取百度云视频。文章首先简要介绍OkHttp库和…

stm32f407栈溢出导致跑程序异常

栈溢出&#xff0c;固件下载后&#xff0c;会运行异常。如下代码&#xff1a; 代码运行异常&#xff0c;进入debug&#xff0c;发现有hard fault的错&#xff1a; 因为栈已经溢出&#xff0c;一般MCU的栈地址都是向下增长的&#xff0c;stm32也是一样&#xff0c;stm32在启动文…

《QT从基础到进阶·十六》QT实现客户端和服务端的简单交互

QT版本&#xff1a;5.15.2 VS版本&#xff1a;2019 客户端程序主要包含三块&#xff1a;连接服务器&#xff0c;发送消息&#xff0c;关闭客户端 服务端程序主要包含三块&#xff1a;打开消息监听&#xff0c;接收消息并反馈&#xff0c;关闭服务端 1、先打开服务端监听功能 …

Java 设计模式——访问者模式

目录 1.概述2.结构3.案例实现3.1.抽象访问者类3.2.抽象元素类3.3.具体元素类3.4.具体访问者类3.5.对象结构类3.6.测试 4.优缺点5.使用场景6.扩展6.1.分派6.2.动态分配6.3.静态分配6.4.双分派 1.概述 访问者模式 (Visitor Pattern) 是一种行为型设计模式&#xff0c;它用于将数…

jQuery中显示与隐藏

在我们jQuery当中&#xff0c;有多个显示隐藏的方法&#xff0c;本篇介绍一下hide()、show()、toggle() 在我们JS当中&#xff0c;或是CSS当中&#xff0c;我们常用到display:none或block; 在我们jQuery当中&#xff0c;我们该如何实现显示隐藏 在我们jQuery当中&#xff0c;我…

定义无向加权图,并使用Pytorch_geometric实现图卷积

首先定义无向边并定义边的权重 import torch import torch.nn as nn from torch_geometric.nn import GCNConv import torch.nn.functional as F from torch_geometric.data import Dataa torch.LongTensor([0, 0, 1, 1, 2, 2, 3, 4]) b torch.LongTensor([0, 1, 2, 3, 1, 5,…

amazon产品采集数据

导入需要的库&#xff1a;requests&#xff0c;BeautifulSoup&#xff0c;re&#xff0c;chardet requests用于发送HTTP请求&#xff1b;BeautifulSoup用于解析HTML&#xff1b;re用于正则表达式&#xff1b;chardet用于识别网页编码。 定义函数&#xff0c;接受URL参数&#…

c语言初学者用vs还是vscode?

c语言初学者用vs还是vscode? 看是科班还是自学&#xff0c;一般学校会有要求的编译软件&#xff0c;在这两者之间&#xff0c;用VS的居多&#xff0c;一个可能的原因是VS不用自己装环境。 最近很多小伙伴找我&#xff0c;说想要一些 c语言的资料&#xff0c;然后我根据自己从…

Spring的循环依赖问题

文章目录 1.什么是循环依赖2.代码演示3.分析问题4.问题解决5.Spring循环依赖6. 疑问点6.1 为什么需要三级缓存6.2 没有三级缓存能解决吗&#xff1f;6.3 三级缓存分别什么作用 1.什么是循环依赖 上图是循环依赖的三种情况&#xff0c;虽然方式有点不一样&#xff0c;但是循环依…
最新文章