进程篇: 进程概念的补充(了解环境变量和虚拟地址空间)

个人主页:小则又沐风

个人专栏:<数据结构>

<竞赛专栏>

<C语言>
<C++>

<Linux>

座右铭

路虽远,行则将至;事虽难,做则必成

目录

前言

命令行参数和环境变量

命令行参数

环境变量

查找环境变量

[export: 设置⼀个新的环境变量](#export: 设置⼀个新的环境变量)

查看所有的环境变量

getenv();

环境变量的继承

程序的地址空间

虚拟地址的管理


前言

在前面我们知道了怎么创建进程,并且知道了怎么拿到进程退出的信息和拿到这个信息的重要性

今天我们来结束进程的篇章,今天会补充一下进程的概念的知识(包括环境变量和虚拟地址空间)

然后讲解进程替换的内容,模拟实现一下xshell

命令行参数和环境变量

• 环境变量(environmentvariables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数

• 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪 ⾥,但是照样可以链接成功,⽣成可执⾏程序,原因就是有相关环境变量帮助编译器进⾏查找。

• 环境变量通常具有某些特殊⽤途,还有在系统当中通常具有全局特性

命令行参数

我们在日常使用指令的时候比如ls我们会又在后面有选项的,我们知道的是Linux的底层使用C语言实现的,那么这个东西是怎么实现的呢?

我们的main函数有参数吗?

有的有的

cpp 复制代码
#include<iostream>
using namespace std;
int main(int argc, char const *argv[])
{
    for(int i=0;i<argc;i++)
    {
        printf("argv[i] is %s\n",argv[i]);
    }
    return 0;
}

这就是我们main函数的参数,我们来看看是什么情况:

我们可以发现我们像使用ls的方法也可以使用我们自己的程序

那么这个main函数的参数就是用来表示选项的

那么我们就可以想到了我们命令行的参数就是传入到了这个argv的字符串的表中,然后将一个个的选项分隔开一个个来执行他的功能

所以我们也可以简单模拟实现一下这个功能

cpp 复制代码
#include <iostream>
#include <string.h>
using namespace std;
int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        // printf("%d\n",argc);
        printf("用法错误,这个程序需要两个选项\n");
        return 1;
    }
    if (strcmp(argv[1], "-a") == 0)
    {
        printf("这是a功能\n");
        printf("hhhh\n");
    }
    else if (strcmp(argv[1], "-b") == 0)
    {
        printf("这是b功能\n");
        printf("xxxxx\n");
    }
    if (strcmp(argv[2], "-a") == 0)
    {
        printf("这是a功能\n");
        printf("hhhh\n");
    }
    else if (strcmp(argv[2], "-b") == 0)
    {
        printf("这是b功能\n");
        printf("xxxxx\n");
    }
    return 0;
}

我们执行一下看看

这大概就是指令的参数的内部核心了吧

需要注意的是我们的第一个运行的程序也算是一个指令的选项的,也就是说我们不掺入参数的话,我们默认的argc是1

那么我们来解决一个问题,为什么我们自己编写的程序必须带./呢?

环境变量

需要知道的是我们在执行一个程序的时候必须找到这个程序.

那么我们就可以解释了这个./不就是代表的就是当前路径吗?

但是常用的指令为什么不需要带路径呢?

为什么我们的就是not found了

这就是因为我们的可执行程序又不是在这个路径下找的,在什么路径下找程序是规定了的

我们可以查看一下

那么系统就会在这些的路径下来查找指令了,如果我们把text的路径添加到这里的话我们的text也可以直接运行了

果然是这样的

那么我们来重启一下我们的机器

怎么又显示找不到了

下面我们来介绍一下环境变量是怎么生成的

环境变量是从我们的配置文件中生成的,从这些的配置文件中获得环境变量

刚才我们知识修改了环境变量并没有修改配置文件,当我们的机器重启之后当然我们刚才配置的环境就消失了

除了这个PATH还有什么环境变量呢??

我们来打印一下环境变量

cpp 复制代码
#include<iostream>
using namespace std;
int main(int argc, char *argv[], char *env[])
{
   for(int i=0;env[i];i++)
   {
      printf("%s\n",env[i]);
   }
    return 0;
}

在这里我们进一步了解一些main函数的参数,第三个参数是一个环境变量的表

我们看看打印出的环境变量是什么

bash 复制代码
SHELL=/bin/bash
COLORTERM=truecolor
TERM_PROGRAM_VERSION=1.108.2
PWD=/home/jiao/study/5.24
LOGNAME=jiao
XDG_SESSION_TYPE=tty
VSCODE_GIT_ASKPASS_NODE=/home/jiao/.vscode-server/cli/servers/Stable-c9d77990917f3102ada88be140d28b038d1dd7c7/server/node
HOME=/home/jiao
LANG=en_US.UTF-8
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=00:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.avif=01;35:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:*~=00;90:*#=00;90:*.bak=00;90:*.crdownload=00;90:*.dpkg-dist=00;90:*.dpkg-new=00;90:*.dpkg-old=00;90:*.dpkg-tmp=00;90:*.old=00;90:*.orig=00;90:*.part=00;90:*.rej=00;90:*.rpmnew=00;90:*.rpmorig=00;90:*.rpmsave=00;90:*.swp=00;90:*.tmp=00;90:*.ucf-dist=00;90:*.ucf-new=00;90:*.ucf-old=00;90:
SSL_CERT_DIR=/usr/lib/ssl/certs
GIT_ASKPASS=/home/jiao/.vscode-server/cli/servers/Stable-c9d77990917f3102ada88be140d28b038d1dd7c7/server/extensions/git/dist/askpass.sh
PROMPT_COMMAND=__vsc_prompt_cmd_original
SSH_CONNECTION=39.144.28.73 7013 10.2.0.7 22
VSCODE_GIT_ASKPASS_EXTRA_ARGS=
VSCODE_PYTHON_AUTOACTIVATE_GUARD=1
LESSCLOSE=/usr/bin/lesspipe %s %s
XDG_SESSION_CLASS=user
TERM=xterm-256color
LESSOPEN=| /usr/bin/lesspipe %s
USER=jiao
VSCODE_GIT_IPC_HANDLE=/run/user/1003/vscode-git-0081e76109.sock
GOPROXY=https://mirrors.tencent.com/go,direct
SHLVL=1
XDG_SESSION_ID=5078
XDG_RUNTIME_DIR=/run/user/1003
SSL_CERT_FILE=/usr/lib/ssl/cert.pem
SSH_CLIENT=39.144.28.73 7013 22
VSCODE_GIT_ASKPASS_MAIN=/home/jiao/.vscode-server/cli/servers/Stable-c9d77990917f3102ada88be140d28b038d1dd7c7/server/extensions/git/dist/askpass-main.js
XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
BROWSER=/home/jiao/.vscode-server/cli/servers/Stable-c9d77990917f3102ada88be140d28b038d1dd7c7/server/bin/helpers/browser.sh
PATH=/home/jiao/.vscode-server/data/User/globalStorage/github.copilot-chat/debugCommand:/home/jiao/.vscode-server/data/User/globalStorage/github.copilot-chat/copilotCli:/home/jiao/.vscode-server/cli/servers/Stable-c9d77990917f3102ada88be140d28b038d1dd7c7/server/bin/remote-cli:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1003/bus
TERM_PROGRAM=vscode
VSCODE_IPC_HOOK_CLI=/run/user/1003/vscode-ipc-0c32845d-e511-48b7-b310-062b475cc7ba.sock
_=./env
OLDPWD=/home/jiao

可以看到在这个环境变量表中我们发现了许多重要的变量

PWD=/home/jiao/study/5.24

HOME=/home/jiao

USER=jiao

OLDPWD=/home/jiao

LOGNAME=jiao

这些环境变量有没有联想到一些指令

HOME不是我们的家目录吗?

LOGNAME不是我们的登录用户吗?whoami

OLDPWD这个不是我们的cd -吗

所以在执行我们的程序的时候我们不仅仅需要一张命令行参数表还需要一张环境变量表,

在这个环境变量表中我们可以知道在哪里找程序什么的等基础的信息

那么我们现在来学习一下怎么查找环境变量

查找环境变量

echo $查找的环境变量

export: 设置⼀个新的环境变量

假设我们需要设置一下我们的PATH路径

这样就把我们的PATH路径新增了我们的当前路径了

查看所有的环境变量

env

上面的都是在命令行端获得环境变量那么我们怎么在代码上获得环境变量呢?

getenv();

cpp 复制代码
#include<iostream>
#include<stdlib.h>
using namespace std;
int main()
{

    printf("%s\n",getenv("PATH"));
    return 0;
}

环境变量的继承

子进程的环境变量是来自于哪里?

我们知道的是我们的进程都是来自于bash的,那么我们的程序的环境变量是来自于父进程bash吗?

我们来看看

cpp 复制代码
#include <iostream>
#include<stdlib.h>
using namespace std;

int main()
{
    char *P=getenv("MYENV");
    if(P!=NULL)
    {
        cout<<"MYENV="<<P<<endl;
    }

    return 0;
}

现在我们bash的环境变量并没有这个MYENV的变量那么我们一定不会再这个程序中输出数据

果不其然

那么我们设置一个环境变量呢?

果然这个进程也会有了这个环境变量了

那么我们可以确认的就是子进程的环境变量是来自于父进程的

那么我们子进程环境变量修改了话会影响父进程的环境变量吗?

不会的,这就像是我们之前说的当我们子进程需要修改子父进程的公共数据的时候就会发生写实拷贝

程序的地址空间

我们在学习C语言的时候一定我们见到过这个图片

但是在当时我们根本不了解这是什么东西,今天我们就来计息的了解一下这个图

在之前我们知道一些的知识是:

我们的数据进行了分类分别放在了这些一个个的区间中.

但是这些空间就是一个个真实的物理空间了吗?

我们来看这个代码:

还记得在之前我们在创建进程的时候遗留下的问题了吗?

为什么一个相同的变量可以拥有两个数据?

我们下面的知识就会解决这个问题

cpp 复制代码
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
    int id = fork();
    int g_value = 10;
    if (id == -1)
    {
        perror("fork error\n");
    }
    else if (id == 0)
    {
        // child
        printf("i am child my pid is %d the g_value is %d\n", getpid(), g_value);
    }
    else
    {
        // father
        g_value = 100;
         printf("i am father my pid is %d the g_value is %d\n", getpid(), g_value);
    }
    return 0;
}

我们运行起来是什么样子的结果呢?

我们竟然发现这两个的g_value的值不一样

那么我们来看看这个g_valus的地址呢?

cpp 复制代码
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
    int id = fork();
    int g_value = 10;
    if (id == -1)
    {
        perror("fork error\n");
    }
    else if (id == 0)
    {
        // child
        printf("i am child my pid is %d the g_value is %d my g_value is %p\n", getpid(), g_value, &g_value);
    }
    else
    {
        // father
        g_value = 100;
        printf("i am father my pid is %d the g_value is %d my g_value is %p\n", getpid(), g_value,&g_value);
    }
    return 0;
}

这太不可思议了,一个地址一样的变量居然有了两个值.

这到底是怎么回事

这是因为我们在这里得到的地址不是实际的物理地址,而是一个虚拟地址

那么什么是虚拟地址?

其实在上面的那个图中就是我们用户能够得到的地址,这些地址都是虚拟的地址

那么我们就会感到了不解了,这不就是给我们一个假的地址呗,那么我们怎么才能得到一个真的地址呢?

我们在得到了一个虚拟地址之后我们就可以拿着这个虚拟的地址去和页表这个表中的地址进行映射,得到一个物理地址,那么这样我们就可以得到真实的地址了.

那么怎么解释上述的情况呢?

这是因为我们的子进程的task_struct都是拷贝父进程的,那么其他的数据当然也是啊

那么每一个变量的虚拟地址空间也是一样的,但是只要这个变量需要发生写实拷贝的话,那么这个数据就会在物理空间上存在两份,一人一个,那么各自就可以拿着相同的虚拟地址拿到不同的物理地址了

虚拟地址的管理

在上面我们知道的是在我们的进程的结构体中存储的其实是一个虚拟的地址,那么这个表是怎么进行对一个个的空间进行控制的?

他是怎么分割一个个的空间的?

难道开辟一个个的空间吗?但是我们知道的是这个空间就需要4G的内存,一个进程就足够呛了的.别说同时多个进程了

那么这个虚拟地址空间是怎么进行空间资源的分配和管理的?

我们来看下面的例子:

你是一名小学生,你的同桌是一个十分美丽的小女孩,但是呢你是一个鼻涕都不停的小屁孩,你的同桌嫌弃你,就在一个课间的时候说:张三这是一个分界线,谁越界谁就是狗!

边说着在你两个的课桌的上面平均画上了一个线.

在这个时候小美做了什么事情???

就是这一个简单的限不就是把张三的活动的空间约束到了一半吗

假设总个课桌的长度是100

那么张三的空间就是

[0,,50)

小美就是50,100;

这就是实现了空间的分配了.

所以一个简单的生活例子我们知道了,要想对整个空间进行若干份的分配的话,我们只需要表示清楚各个部分的开始和结束就可以了

那么我们的操作系统也是这么做的,所以mm_struct横空出世

假设你是操作系统的设计者你怎么写呢?

不就是这样的吗?

struct mm_struct

{

int code_begin=0;//代码区

int code_end=100;//代码区

int data_begin=101;//数据区

int data_end =201;/数据区

....

}

ok 就是这样

但是这时候会出现一个小问题,我们的堆区是一块一块的,这是怎么弄得?

我们需要就申请不需要就自己释放,这个一个的mm_struct是做不到的把?

没错我们的mm_struct不能很好的进行对堆区的管理,所以有一个个小的mm_struct的结构体对一个个的区进行细致的管理.

也就是这个图中的vm_area_struct这个结构会对对样的区的内存进行管理,管理的方式和上述的一样,

那么我们的mm_struct的工作就是管理一个个的vm_area_struct了一般是用红黑树加双向链表进行管理的

相关推荐
艾莉丝努力练剑1 小时前
【Linux网络】Linux 网络编程:传输层协议TCP(五)
linux·运维·网络·计算机网络·udp
郝学胜-神的一滴1 小时前
[简化版 GAMES 101] 计算机图形学 11:频域·卷积·抗锯齿
c++·unity·图形渲染·opengl·three·unreal
无足鸟ICT1 小时前
【RHCA+】toilet命令(生成艺术字)
linux
kebidaixu1 小时前
设备树修改
linux
valan liya1 小时前
C++ 继承
开发语言·c++
晚风吹红霞1 小时前
进程调度深度解析:从优先级到O(1)调度算法
linux·运维
say_fall1 小时前
深入理解Linux内核进程调度:从基础概念到O(1)调度算法
linux·运维·服务器·算法·计算机组成
青梅橘子皮1 小时前
Linux---命令行参数和环境变量
linux·运维·服务器
艾莉丝努力练剑1 小时前
【Linux网络】Linux 网络编程:传输层TCP(四)
linux·运维·服务器·网络·tcp/ip·http