https://bqlp6h3avk.feishu.cn/docx/Jd7SdNmB2o4amuxNTIEc6Q33nNb
这是上节课链接,亲爱的大家就看一下呗,我求求大家了!
一.环境变量
基本概念:
环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
常见环境变量:
PATH : 指定命令的搜索路径
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
SHELL : 当前Shell,它的值通常是/bin/bash
**USER:**当前用户
补充知识:
为什么有些指令可以直接执行,不需要带路径,而我们的二进制程序需要带路径才能执行,其实就是指令它有被PATH记录
环境变量是系统提供的一组name = value形式的变量
查看环境变量:echo $环境变量
环境变量相关的命令
-
echo: 显示某个环境变量值(要带$)
-
export: 设置一个新的环境变量,用name=value生成的是本地变量
-
env: 显示环境变量
-
unset: 清除环境变量
-
set: 显示本地定义的shell变量、本地变量、env的内容
环境变量的组织方式

命令行参数:int main(int argc,char* agrv(参数表)char* env(环境变量表))
-
agrv第一个就是可执行文件(./mycmd)后面每一个选项用空格分隔然后用**(-字符)**,这些选项就会被存到参数表里面
-
参数表的意义就是为指令、工具、软件提供命令行选项

通过代码如何获取环境变量
- 命令行第三个参数
cpp
include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
for(int i = 0; env[i]; i++)
{
printf("%s\n", env[i]);
}
}
- 通过第三方变量environ获取
cpp
#include <stdio.h>
int main(int argc, char *argv[])
{
extern char* *environ;
for (int i = 0; environ[i]; i++)
{
printf("%s\n", environ[i]);
}
}
environ 是 char **,因为它是一个数组指针,你一个*相当于找到数组的某个位置,再加一颗星才是找到某个位置的内容!
- 系统调用获取
语法:char *getenv(const char *name)
cpp
#include<stdio.h>
#include<stdlib.h>
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
1.1 环境变量的全局性和本地变量
-
我们所运行的进程都是子进程,bash在启动的时候,会从操作系统的配置文件中读取环境变量信息,子进程会继承父类拥有的环境变量,因此如果想让子进程拥有某个环境变量可以修改其父亲的环境变量
-
本地变量只会在当前bash内部有效,无法继承
-
如果用export 本地变量,那么该本地变量就会变成环境变量
补充知识:
重新了解指令
-
**常规指令:**通过子进程来完成
-
**内建指令:**亲自完成,不创建子进程
二.程序地址空间
1:C中地址空间

2:虚拟地址
cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
pid_t id = fork();
if (id < 0)
{
perror("fork");
}
else if (id == 0)
{
g_val = 100;
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
else
{
sleep(3);
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
sleep(1);
}
发现它们虽然值不一样,但是地址确是一样的,但是同一个物理地址变量内容一定是一样的可知
-
这个地址是虚拟地址,并非是物理地址,因此C/C++所看到的地址其实都是虚拟地址
-
物理地址,用户一概看不到,由OS统一管理。
3:进程地址空间

理解为什么值不一样但是地址是一样的(宏观了解)
-
进程地址在开始创建数据的时候,它会将其写入页表,而这个页表的内容是一个键值对(虚拟地址,物理地址)
-
子进程因为继承父进程,因此它的页表其实就是父亲的页表,然后当子进程重写父进程的数据会触发写时拷贝,此时物理内存会重新开辟一个空间给子进程重写父进程的数据,因此物理地址就会发生改变,但是虚拟地址却不会改变,因此才会出现同一个地址却是不同的值。
谈细节
-
地址空间是什么(内核的一个数据结构对象,被操作系统管理)
-
**什么叫地址空间:**地址总线排列形成的范围,比如32位的范围【0,2^32)(4GB)因此32位最大支持4GB内存条
-
**理解地址空间的区域:**本质是将范围进行划分
-
**总结:**地址空间,本质是一个描述进程可视化范围的大小,然后对其范围进行各种划分,然后进程有一个指向该地址空间的指针,而这个地址空间就变成了进程的地址空间
-
-
-
为什么需要地址空间
-
让进程以统一的视角看待内存(告诉你这块存什么那块存什么)
-
增加一个转换的过程,因为如果我们查找的内容不在地址空间,那么可以抛出问题,避免影响物理内存
-
将进程管理和内存管理进行解耦合(进程不需要关心物理内存是怎么分配的他只需要通过地址空间和页表即可对内存的使用)
-
-
页表
-
页表作用:
-
将虚拟地址转成对应的物理地址
-
区分该进程空间区域是只读还是可以写的
-
标明代码和数据是否加载到内存
-
因为可以只写虚拟地址然后不给物理地址,当访问虚拟地址的时候去找标明数字然后根据它来区分是直接访问还是触发缺页中断(操作系统会补全这部分物理地址)
-
其实写时拷贝就是触发了缺页中断
-
-
-
**页表存放:**CPU中的cr3寄存器(该寄存器存放的是物理地址)
-
-
在了解进程
-
进程 = 内核数据结构(task_struct、mm_struct、页表)
-
进程切换:
- 切换进程的PCB(task_struct)它匹配的地址空间(mm_struct)切换,又因为页表属于cr3寄存器,而寄存器存放的是进程的上下文,因此在切换PCB的时候把上下文也切换了因此页表切换
-
关于今天的内容就到这里,本章理论知识很大,而且很多也是重点,而且本章通过地址空间又再一次了解了进程,所以这就是我之前说的有些内容不清楚很正常,因为要往深聊需要更多知识辅助才行!