【Linux第五课-进程概念下】环境变量、程序地址空间

ops/2024/10/9 4:08:28/

目录

  • 环境变量
      • main参数 --- 命令行参数
      • 环境变量
      • 环境变量特性 --- 命令行操作
        • main函数的参数获取环境变量
        • environ获取环境变量
        • getenv()获取环境变量
        • unset移除本地变量或环境变量
        • set显示本地变量
      • 代码获取和设置环境变量
    • 本地变量
  • 程序地址空间
    • 什么是进程地址空间
    • 为什么有地址空间+页表
    • 内存申请

环境变量

是什么:环境变量是由系统提供的一组全局变量,每一个环境变量都有其不同的系统级用途。
为什么有:在不同的场景下,在执行某些任务或工作时,是需要知道更多的其他属性。eg:创建文件时,它就知道你是谁给你一定的权限。
每个用户都有属于自己的.bash_profle

main参数 — 命令行参数

main函数可以带参数:int argc 和 char * argv[]
char*数组argv指向一个一个的字符串
argc是指针数组里面元素个数

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

在这里插入图片描述

argv必须以NULL结尾

为何这么做?
可以通过不同的选项,对于同一个程序可以使用内部不同的子功能

就像 ls -l
ls -a
ls -n

使用参数写一个计算器

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//./myprocess -[add|sub|mul|div]
int main(int argc, char* argv[])
{if(argc != 4){printf("./myprocess -{add|sub|mul|div} x y\n");return 1;}int x = atoi(argv[2]);int y = atoi(argv[3]);if(strcmp("-add", argv[1]) == 0){printf("%d + %d = %d\n", x, y, x+y);}else if(strcmp("-sub", argv[1]) == 0){printf("%d - %d = %d\n", x, y, x-y);}else if(strcmp("-mul", argv[1]) == 0){printf("%d * %d = %d\n", x, y, x*y);}else{printf("%d / %d = %d\n", x, y, x/y);}return 0;
}

在这里插入图片描述

命令行参数是选项指令的基础

环境变量

不是一个,而是一堆,彼此之间没有关系
是系统内置的,具有特殊用途的变量

定义变量的本质,其实是开辟空间
操作系统/bash是C语言写的程序,他也能在运行中开辟空间
系统的环境变量,本质就是系统自己开辟的空间,给他名字和内容即可

执行pwd命令实际上是读取环境变量PWD
想要自己写写的程序和操作系统自带的命令一样直接使用,而不是./myprocess
需要将myprocess所在的路径加入到PATH 环境变量里面
在这里插入图片描述

环境变量特性 — 命令行操作

1、export
导入某个环境变量,env里面就有了
在这里插入图片描述

2、env
在这里插入图片描述

3、echo

main函数的参数获取环境变量

main函数除了有argc和argv两个参数以外,还有一个参数env
env是一个char*的数组用来存放父进程给的环境变量


上面就是自己打印的环境变量

int main(int argc, char* argv[], char* env[])
{printf("-----------\n");for(int i = 0; env[i];i++){printf("%s\n",env[i]);}return 0;
}

环境变量具有全局属性:环境变量会被所有的子进程即孙子进程继承,因此改变子进程的并不影响

environ获取环境变量

environ是一个二级指针
在这里插入图片描述
第一次用需要声明
在这里插入图片描述
在这里插入图片描述

getenv()获取环境变量

上面获取环境变量的方式太笼统,可以使用getenv()
环境变量可以使用USER限制谁可以访问该程序

头文件:#include<stdlib.h>

int main(int argc, char* argv[], char* env[])
{const char* username=getenv("USER");if(strcmp("hui", username)==0){printf("this is my process\n");}else{printf("你没有权限\n");}return 0}
unset移除本地变量或环境变量
unset 变量名

在这里插入图片描述

set显示本地变量

环境变量是bash从磁盘里面读来的

代码获取和设置环境变量

想设置自己的环境变量在启动中就有,可以配置家目录下的.bash_profile
在这里插入图片描述
在这里插入图片描述
关掉重启,环境变量里面就有MYVAL
在这里插入图片描述

本地变量

本地变量不在环境变量中,可以set将shell里面的全部变量显示出来(环境变量和本地变量)

在这里插入图片描述
在这里插入图片描述
本地变量只能在bash内部有效,不能被子进程继承下去
在这里插入图片描述
myenv是导进去的环境变量可以被子进程继承,而hello是本地变量不是环境变量不能被子进程继承
在这里插入图片描述

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main()
{const char* myenv = getenv("myenv");if(myenv == NULL) printf("getenv() get null\n");else printf("myenv: %s\n", myenv);return 0;
}

在这里插入图片描述

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main()
{//const char* myenv = getenv("myenv");if(myenv == NULL) printf("getenv() get null\n");else printf("myenv: %s\n", myenv);return 0;
}

程序地址空间

对于地址空间用代码的初步感受
在这里插入图片描述
在这里插入图片描述

堆栈相对而生
在这里插入图片描述

命令行参数和环境变量的严重,现有命令行参数表,再有环境变量这张表

argv+i:打印的是命令行参数那张表
在这里插入图片描述

argv[i]:环境变量字符串的地址
在这里插入图片描述

无论是表还是表指向的项目,都在栈上部

未初始化数据和以已初始化数据是全局变量
static变量是全局变量
在这里插入图片描述

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int g_unval;
int g_val = 100;int main(int argc, char* argv[], char* env[])
{printf("code addr:%p\n", main);printf("init data addr:%p\n", &g_val);printf("uninit data addr:%p\n", &g_unval);char* heap = (char*)malloc(20);char* heap1 = (char*)malloc(20);char* heap2 = (char*)malloc(20);char* heap3 = (char*)malloc(20);printf("heap addr:%p\n", heap);printf("heap1 addr:%p\n", heap1);printf("heap2 addr:%p\n", heap2);printf("heap3 addr:%p\n", heap3);static int i = 99;printf("static data addr:%p\n",&i);printf("stack addr:%p\n", &heap);printf("stack1 addr:%p\n", &heap1);printf("stack2 addr:%p\n", &heap2);printf("stack3 addr:%p\n", &heap3);for(int i = 0; argv[i]; i++){printf("argv[%d]:%p\n", i, argv[i]);//printf("&argv[%d]:%p\n", i, argv+i);}for(int i = 0; env[i]; i++){printf("env[%d]:%p\n", i, env[i]);//printf("&env[%d]:%p\n", i, env+i);}return 0;
}

问题?
上面那张图是内存吗?不是,叫作进程地址空间
验证

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int g_unval;
int g_val = 100;int main()
{pid_t id = fork();if(id == 0){//子进程int cnt = 0;while(1){printf("child, pid:%d, ppid:%d, g_val:%d, &g_val:%p\n",getpid(), getppid(), g_val, &g_val);cnt++;if(cnt == 5){printf("child: g_val 100->200\n");g_val = 200;}sleep(1);}}else{//父进程while(1){printf("father, pid:%d, ppid:%d, g_val:%d, &g_val:%p\n",getpid(), getppid(), g_val, &g_val);sleep(2);}}return 0;
}

在这里插入图片描述

这个地址绝对不是物理空间,这个地址叫作:虚拟地址/线性地址
语言中我们写代码所用到的所有地址都不是物理地址!

打出来的值不一样是因为,子进程的映射表已经改变,映射到不一样的物理地址

什么是进程地址空间

每一个进程,都存在一个进程地址空间,32[0, 4GB]
地址空间是一个内核数据结构,里面有各种类型的区域划分

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

地址空间,不具备对代码和数据的保存能力,在物理空间上存放
将地址空间上的地址(虚拟地址/线性)转换到物理空间中
给我们的进程提供一张映射表 — 页表

为什么有地址空间+页表

1、将物理内存从无序变成有序,让进程以统一的视角,看待内存
2、将进程管理内存管理进程解耦合
3、当访问内存的时候,如果请求合法就会映射,请求不合法就不会映射,这就是地址空间和页表的作用
进程=内核数据结构+自己的代码和数据

内存申请

操作系统一定要为效率和资源使用率负责
1、申请的内存,你会直接使用吗? 不一定
2、申请内存,本质是在哪里申请? 本质申请是在进程地址空间中申请

new或malloc申请地址空间的时候,只是在虚拟地址上申请的,并没有在内存空间上申请。当第一次使用申请的空间的时候,会去访问页表,但页表里面找不到发生缺页中断,再去物理内存上申请。

优点
充分保证内存的使用率,不会空转
提升new或malloc的速度


http://www.ppmy.cn/ops/122975.html

相关文章

C++学习笔记----8、掌握类与对象(六)---- 操作符重载(1)

经常在对象上执行如相加&#xff0c;比较&#xff0c;文件传输等操作。例如&#xff0c;spreadsheet只有在可以在上面执行自述运算才有用&#xff0c;比如对整行的单元格求和。所有这些都可以通过重载操作符来完成。 许多人发现操作符重载的语法复杂而令人迷惑。至少一开始是这…

Dev-C++ 安装与使用(dev c++官网)(已解决)

1.Dev-C的安装 ①打开Dev-C的官网(https://sourceforge.net/projects/orwelldevcpp/ )&#xff1b;点击Download(下载)&#xff0c;等待5秒后开始下载。 ②点开下载好的EXE文件&#xff0c;等待加载完成(如图)。 右键&#xff0c;以管理员身份 运行安装包。 选择English(英语),…

银河麒麟,apt 安装软件报错640Unknown Status

今天把银行麒麟的机器恢复出厂了&#xff0c;然后apt install 安装极其不稳定&#xff0c;故障现象如下图所示&#xff1a; 错误提示里面有&#xff1a; 640 Unknown Status [IP: 106.116.184.122 80] E: 无法下载 http://archive.kylinos.cn/kylin/KYLIN-ALL/pool/universe/f…

论文速读:基于渐进式转移的无监督域自适应舰船检测

这篇文章的标题是《Unsupervised Domain Adaptation Based on Progressive Transfer for Ship Detection: From Optical to SAR Images》基于渐进式转移的无监督域自适应舰船检测:从光学图像到SAR图像&#xff0c;作者是Yu Shi等人。文章发表在IEEE Transactions on Geoscience…

【Android】获取备案所需的公钥以及签名MD5值

目录 重要前提 获取签名MD5值 获取公钥 重要前提 生成jks文件以及gradle配置应用该文件。具体步骤请参考我这篇文章&#xff1a;【Android】配置Gradle打包apk的环境_generate signed bundle or apk-CSDN博客 你只需要从头看到该文章的配置build.gradle&#xff08;app&…

【答疑解惑】图文深入详解undo和redo的区别及其底层逻辑

题记&#xff1a;最近有些人问我&#xff0c;undo和redo到底是什么关系&#xff0c;他们中不乏已经入行3-4年的同学&#xff0c;今天咱们就来深入探讨下到底什么是undo和redo&#xff0c;他们分别做什么&#xff0c;底层逻辑原理是什么等等。 1. undo 1.1 undo的存储结构 Un…

游戏中的对象池技术探索(一)

前言 对象池技术在游戏开发中的应用非常普遍&#xff0c;它是一种高效管理对象实例的技术&#xff0c;能够避免频繁和重复创建对象所带来的性能开销。本篇文章我们就来探索一下如何在游戏开发中设计通用对象池&#xff0c;使之易于使用和扩展。 代码 代码目录结构 ObjectPool …

Pikachu-unsafe upfileupload-getimagesize

什么是getimagesize()&#xff1f; getimagesize()是PHP中用于获取图像的大小和格式的函数。它可以返回一个包含图像的宽度、高度、类型和MIME类型的数组。 由于返回的这个类型可以被伪造&#xff0c;如果用这个函数来获取图片类型&#xff0c;从而判断是否时图片的话&#xff…