Linux进程概念--环境变量和进程地址空间

Posted 蓝乐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux进程概念--环境变量和进程地址空间相关的知识,希望对你有一定的参考价值。

一、环境变量

1、基本概念

·环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
·如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
·环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。

2、常见环境变量

·PATH

PATH指定的是命令的搜索路径。
我们之前生成可执行程序a.out后要执行这个可执行程序是通过./a.out这个指令来执行的,必须在文件名前加上./的修饰符表示a.out所在的路径。但是我们所使用的系统指令却不需要加上其所在的路径。
这是因为系统对于ls这样的指令回去PATH所指的路径寻找,而我们自己的类似a./out的指令并不在PATH变量所指的路径

这个路径中的:是分隔符,系统会搜寻每个路径去寻找要执行的命令。若我们像让当前路径下的命令可以直接被系统执行而不加路径的的话就需要将当前路径加到PATH中,可以用PATH=$PATH:+(要添加的路径)这条指令来添加。

环境变量既然是变量,那么其就是可以被赋值的,所以如果我们不小心将PATH路径覆盖了,也不用慌,因为每次我们登录时Linux系统都会对PATH进行赋值。因此只需要关闭当前终端再次登录PATH路径就会恢复。

·HOME

HOME变量指定的是用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)

·SHELL

即当前Shell(当前用户所使用的命令行解释器),它的值通常是/bin/bash。

3、查看环境变量的方法

查看环境变量的方法我们上面已经使用过来,就是echo $ + 环境变量这条指令。

4、和环境变量相关的命令

·echo

echo命令的作用是显示某个环境变量值

·export

export的作用是设置一个新的环境变量
在命令行中,我们可以定义两种变量,一种是本地变量,另一种是环境变量。本地变量只能够在当前shell命令行解释器下(bash)被访问,而不能被子进程继承;环境变量具有全局属性,可以被子进程继承(这个我们后面会提到)

·env

env的作用是显示所有环境变量

·unset

unset的作用是清楚环境变量

·set

set的作用是显示本地定义的shell变量和环境变量,也就是说set和env是包含关系,env只能查看环境变量;而set既可以查看环境变量,也可以查看本地变量。

5、环境变量的组织方式

在lib.c中定义了全局变量environ,这是一个二级指针,指向一个环境变量表(字符指针数组),字符指针数组中的指针指向每一个环境变量字符串。需要注意的是,,environ没有包含在任何头文件中,所以在使用时 要用extern声明。

6、获取环境变量

main函数中的第三个参数

在Linux系统下,main函数有三个隐含的参数,分别是int argc,char* argv[], char* env[]
其中前两个参数与我们执行可执行文件时命令行上的参数有关:

而第三个变量就是存储的环境变量,通过env这个数组就可以查看环境变量。

#include <iostream>
#include <stdio.h>
using namespace std;

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

  for(int i = 0; env[i]; i++)
  
    cout << env[i] << endl;
  
  return 0;


通过第三方变量environ获取

#include <iostream>
#include <stdio.h>
using namespace std;

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

  extern char** environ;
  for(int i = 0; environ[i]; i++)
  
    cout << environ[i] << endl;
  
  return 0;


通过系统调用获取环境变量

Linux系统提高了一个getenv函数接口可以获取特定的环境变量

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

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

  //获取PATH环境变量的值
  cout << getenv("PATH") << endl;
  return 0;


7、环境变量通常时具有全局属性的

前面我们提到环境变量可以被子进程继承而本地变量不行,通过对于main函数的三个参数我们可以直到环境变量通过env数组被传给bash的子进程,而本地变量却没有传给子进程,因此子进程无法使用本地变量而可以使用环境变量。

二、进程地址空间

1、进程地址空间布局图


我们用一个程序来验证一下图中的内容:

#include <iostream>
#include <stdio.h>
using namespace std;

int global_val = 10;
int uninit_global_val;

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

  printf("代码区:%p\\n",main);
  const char* pc = "hello world";
  printf("字符常量区:%p\\n",pc);
  printf("已初始化全局变量:%p\\n",&global_val);
  printf("未初始化全局变量:%p\\n",&uninit_global_val);
  int* p_heap1 = new int[10];
  int* p_heap2 = new int[10];
  //堆区向上增长
  printf("堆区p_heap1:%p\\n",p_heap1);
  printf("堆区p_heap2:%p\\n",p_heap2);
  //栈区向下增长
  printf("栈区pc:%p\\n",&pc);
  printf("栈区p_heap1:%p\\n",&p_heap1);
  printf("栈区p_heap2:%p\\n",&p_heap2);
  printf("命令行参数:%p\\n",argv);
  printf("环境变量:%p\\n",env);
  return 0;


2、虚拟地址&物理地址

我们上面所说的进程地址空间是虚拟地址还是物理地址呢?我们先来看一个程序:

#include <iostream>
#include <stdio.h>
#include <unistd.h>
using namespace std;

int global_val = 10;
int uninit_global_val;

int main()

  pid_t id = fork();
  if(id == 0)//child
  
    printf("child:%p\\n", &global_val);
  
  else if(id > 0)//father
  
    printf("father:%p\\n",&global_val);
  
  else
  
    //error
  
  return 0;



可以看到的是父子进程中global_val的地址是一模一样的。
而系统生成子进程是写时拷贝,将父进程的所有信息完完全全拷贝给子进程,这时我们让子进程修改变量的值,在查看两个变量的地址:

cat test.cpp 
#include <iostream>
#include <stdio.h>
#include <unistd.h>
using namespace std;

int global_val = 10;
int uninit_global_val;

/*int main(int argc, char* argv[], char* env[])

int main()

  pid_t id = fork();
  if(id == 0)//child
  
    global_val = 1;
    printf("child:%p,global_val:%d\\n", &global_val,global_val);
  
  else if(id > 0)//father
  
    printf("father:%p,global_val:%d\\n",&global_val,global_val);
  
  else
  
    //error
  
  return 0;



根据结果我们可以看到父子进程中变量有相同的地址,但是值却不同。如果说进程地址空间是物理内存的话,这可能吗?
可见,我们所说的进程地址空间实际上是虚拟地址,我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理OS必须负责将 虚拟地址 转化成 物理地址 。

3、进程地址空间

我们已经知道了虚拟地址和物理地址,那么二者是如何转换的呢?
实际上,系统为每个进程分配空间时都是在“画大饼”。准确来说,每个进程在看到自己能使用的空间其实是4GB(32位系统)。而进程所看到的这块空间一般是用不完的,并且进程的使用的空间会经过某种映射关系到物理内存上开辟(这个映射关系被称为页表)。
我们用一张图来理解一下分页和虚拟地址空间:

从图中可以很清楚的看到,父子进程的global_val变量,虽然虚拟地址相同,但是映射到物理内存上的地址是不同的。

以上是关于Linux进程概念--环境变量和进程地址空间的主要内容,如果未能解决你的问题,请参考以下文章

[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)

Linux进程概念二

Linux——进程概念进程创建僵尸进程孤儿进程环境变量程序地址空间详解

Linux——进程概念进程创建僵尸进程孤儿进程环境变量程序地址空间详解

Linux——进程概念进程创建僵尸进程孤儿进程环境变量程序地址空间详解

Linux-进程概念