linux篇【5】:环境变量,程序地址空间

目录

一.环境变量

1.环境变量概念

2.常见环境变量

3.查看环境变量方法

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

(1)echo: 显示某个环境变量值

 (2)export :导出环境变量

(3)env: 显示所有环境变量

(4)unset :删除环境变量

(5)set:查看本地定义的shell变量(本地变量)和环境变量

5.PATH-搜索可执行程序的环境变量

查看环境变量指令:echo $PATH​编辑

如何让自己的程序不带路径也可以执行?:

二.介绍常见的环境变量

HOSTNAME——显示主机名

SHELL

HISTSIZE

HOME

三.环境变量和局部(普通)变量

残留问题:本地变量不会被子进程继承下去,为什么本地变量的子进程echo能使用本地变量local_val呢?——内建命令

四.环境变量的C、C++获取方式

1.问题1:main函数可以带参数吗?最多可以带多少? -可以,实际是3个

(1)先说main函数的前两个参数

(2)argv[]中放什么呢?

2.实现一个命令行版的计算器

给命令行参数传程序名和选项意义是什么?

3.命令行可以带第三个参数!: char *env[](环境变量)

C语言获取环境变量的第一种方法

c语言中函数无参,也是可以传参的,只不过实参没用上

4.通过代码获取环境变量的三种方式

①C语言获取环境变量的第一种方法

②通过第三方变量environ获取 

③getenv——获取环境变量的接口

五.程序地址空间

1.初步认识

2.感知地址空间的存在!

3.让每一个进程都认为自己是独占系统中的所有资源

4.虚拟地址空间=进程地址空间 mm_struct

​编辑

什么叫做区域

5.写时拷贝

为什么要写时拷贝?创建子进程的时候,就把数据分开,不行吗? ?

fork有两个返回值,pid_ t id ,同一个变量,怎么会有不同的值?

6.为什么要有虚拟地址空间?


一.环境变量

1.环境变量概念

  ●环境变量(environment variables):系统当中用做特殊用途的系统变量。
●如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
●环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
子进程默认会复制拥有与父进程相同的环境变量

2.常见环境变量

PATH : 指定命令的搜索路径
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
SHELL : 当前Shell,它的值通常是/bin/bash。

——详情见大标题二

3.查看环境变量方法

echo $NAME                 NAME:你的环境变量名称 
指令 env——显示所有环境变量

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

1. echo: 显示某个环境变量值
2. export: 设置一个新的环境变量(export aaaa)
3. env: 显示所有环境变量
4. unset: 清除环境变量(rm只是普通的文件操作指令,无法删除环境变量)
5. set: 显示本地定义的shell变量(本地变量)和环境变量
 set | grep aaaa

(1)echo: 显示某个环境变量值

[zsh@ecs-78471 ~]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zsh/.local/bin:/home/zsh/bin

 (2)export :导出环境变量

(3)env: 显示所有环境变量

[zsh@ecs-78471 ~]$ env
XDG_SESSION_ID=3849
HOSTNAME=ecs-78471
TERM=xterm
SHELL=/bin/bash
HISTSIZE=10000
SSH_CLIENT=218.6.151.253 54910 22
SSH_TTY=/dev/pts/0
USER=zsh
…………………………………………

(4)unset :删除环境变量

(5)set:查看本地定义的shell变量(本地变量)和环境变量

[zsh@ecs-78471 ~]$ aaaa=100            aaaa是本地变量
[zsh@ecs-78471 ~]$ env | grep aaaa     环境变量查不到aaaa
[zsh@ecs-78471 ~]$ export bbbb=50      bbbb是本地变量    
[zsh@ecs-78471 ~]$ env | grep bbbb     
bbbb=50                                环境变量查的到bbbb    
[zsh@ecs-78471 ~]$ set | grep aaaa     
aaaa=100                               set既可以查到本地变量aaaa
[zsh@ecs-78471 ~]$ set | grep bbbb
bbbb=50                                set也可以查到环境变量bbbb

5.PATH-搜索可执行程序的环境变量

一个问题?基本指令也是程序,为什么我们的代码程序运行要带路径,而系统的指令不用带路径? ?比如使用指令ls,pwd时直接使用即可,使用自己的myproc 可执行程序时(gcc -o mproc.c myproc) 需要./myproc.c

答:系统中是存在相关的环境变量,保存了程序的搜索路径的! 比如:执行 ls 这个可执行程序时,系统会在PATH中一个一个搜索,在特定路径(系统所有命令都在usr/bin路径下)下可以找到ls,就可以执行

系统中搜索可执行程序的环境变量叫做 PATH !

查看环境变量指令:echo $PATH

如何让自己的程序不带路径也可以执行?:

——①把自己的程序拷贝进环境变量中,就可以直接myproc执行程序了,拷贝的过程就是安装软件,但是不建议这样做,本身我们的软件就没什么意义,会污染系统,删除=卸载

 ——②把myproc自己的文件加入环境变量PATH中,export PATH=$PATH:路径(相当于把PATH中的路径改成PATH和myproc的路径)

错误示范如果直接 export PATH=路径 ,会覆盖环境变量PATH的原有路径:这里pwd还能用,ls,top什么的就不能用了

二.介绍常见的环境变量

[zsh@ecs-78471 ~]$ env
XDG_SESSION_ID=3849
HOSTNAME=ecs-78471
TERM=xterm
SHELL=/bin/bash                            SHELL:显示shell所在路径
HISTSIZE=10000                             HISTSIZE:历史能够记录自己敲过的命令条数
SSH_CLIENT=218.6.151.253 54910 22          SSH_CLIENT:ip地址
SSH_TTY=/dev/pts/0
USER=zsh                                   USER:用户名
LD_LIBRARY_PATH=:/home/zsh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
       下面是ls的配色方案:
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=01;05;37;41:su=37;41:sg=30;43:ca=30;41: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:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=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:*.jpg=01;35:*.jpeg=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:*.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:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/zsh                   MAIL:系统中的邮箱路径
        PATH:命令的搜索路径:
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zsh/.local/bin:/home/zsh/bin
PWD=/home/zsh                              PWD:当前用户所处路径
LANG=en_US.UTF-8                           LANG:支持的编码格式,现在支持的是UTF8
HISTCONTROL=ignoredups
SHLVL=1
HOME=/home/zsh                             HOME:代表不同用户的家目录
LOGNAME=zsh                                LOGNAME:登录的人是谁
SSH_CONNECTION=218.6.151.253 54910 192.168.0.183 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/1002
HISTTIMEFORMAT=%F %T zsh 
_=/usr/bin/env

HOSTNAME——显示主机名

echo $HOSTNAME——显示主机名

SHELL

[zsh@ecs-78471 ~]$ echo $SHELL        显示shell所在路径
/bin/bash

HISTSIZE

[zsh@ecs-78471 ~]$ echo $HISTSIZE     历史能够记录自己敲过的命令条数
10000
[zsh@ecs-78471 ~]$ history    可以查看历史记录的命令
……
[zsh@ecs-78471 ~]$ history | wc -l    查看历史记录命令的总条数
1440

HOME

[zsh@ecs-78471 ~]$ echo $HOME
/home/zsh

三.环境变量和局部(普通)变量

环境变量:系统当中用做特殊用途的系统变量。

env:查看所有的环境变量

命令行变量分两种:
1.普通变量(在env查不到)
2.环境变量(全局)(在env能查到)

环境变量具有全局属性:环境变量是会被子进程继承下去的! !
所谓得本地变量,本质就是在bash内部定义的变量,不会被子进程继承下去!

 

定义 qi_104是本地变量,用set能找到qi_104这个本地变量,env就找不到本地变量,用myenv显示环境变量是空,当export qi_ 104 把本地变量导成环境变量后,myenv显示环境变量就是 qi_ 104

残留问题:本地变量不会被子进程继承下去,为什么本地变量的子进程echo能使用本地变量local_val呢?——内建命令

Linux下大部分命令都是通过子进程的方式执行的!
但是,还有一部分命令,不通过子进程的方式执行,而是由bash自己执行(调用自己的对应的函数来完成特定的功能),我们把这种命令叫做内建命令!

四.环境变量的C、C++获取方式

——————————————————————————————先讲讲main函数参数

1.问题1:main函数可以带参数吗?最多可以带多少? -可以,实际是3个

(1)先说main函数的前两个参数

main函数的前两个参数分别是下图所示:这两个参数我们称为:命令行参数

(2)argv[]中放什么呢?

我们给main函数传递的前两个参数 argc,char* argv[] 称为 命令行参数传递的是命令行中输入的程序名和选项!比如命令行输入了./myproc -a -b -c,argc对应就是4,argv[ ] 中传入了这4个字符串,argv[0] = "./myproc",argv[1] = "-a",argv[2] = "-b",argv[3] = "-c",argv[4] = "NULL",指针数组以NULL结尾。

命令行参数传程序名和选项意义是什么?我们通过实现一个命令行版的计算器来理解:

2.实现一个命令行版的计算器

我们要实现的功能:

执行 ./myproc -a 10 20 要实现 10+20=30;

执行 ./myproc -s 10 20 要实现 10-20=-10

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{
	if (argc != 4)
	{
		如果用户输入不对,打印使用手册,-a加法,-s(subtract)减法,-m乘法,-d除法:
		printf("Usage: %s [-a|-s|-m|-d] one_data two_data\n", argv[0]);
		return 0;
	}
	int x = atoi(argv[2]);    atoi:把字符串转为整数
	int y = atoi(argv[3]);
	if (strcmp("-a", argv[1]) == 0)    如果输入-a,就是加法
	{
		printf("%d+%d=%d\n", x, y, x + y);
	}
	else if (strcmp("-s", argv[1]) == 0)    如果输入-s,就是减法
	{
		printf("%d-%d=%d\n", x, y, x - y);
	}
	else if (strcmp("-m", argv[1]) == 0)    如果输入-m,就是乘法
	{
		printf("%d*%d=%d\n", x, y, x * y);
	}
	else if (strcmp("-d", argv[1]) == 0 && y != 0)    如果输入-d,就是除法
	{
		printf("%d/%d=%d\n", x, y, x / y);
	}
	else
	{
        输入错误说明不会用,还是打印使用手册
		printf("Usage: %s [-a|-s|-m|-d] one_data two_data\n", argv[0]);
	}

	return 0;
}

命令行参数传程序名和选项意义是什么?

答:同一个程序,通过传递不同的参数,让同一个程序有不同的执行逻辑 / 执行结果。

这就解释了指令中那么多选项的由来和起作用的方式!!Linux系统中,会根据不通的选项,让不同的命令,可以有不同的表现!

这就解释了我们平时输入的指令传入了哪里

3.命令行可以带第三个参数!: char *env[](环境变量)

指针数组env[]和argv[]差不多,只不过传的是所有的指向环境变量的地址,指针数组也是以NULL结尾的

 每个进程是会被传入环境变量参数的! ! 环境变量传给env[] :

C语言获取环境变量的第一种方法

    1 #include<stdio.h>
    2 int main(int argc,char* argv[],char* env[])
    3 {存环境变量的指针数组以NULL结尾,所以到NULL时for循环结束:
    4   for(int i=0;env[i];i++) 
    5   {
    6     printf("env[%d]:%s\n",i,env[i]);                                                                                
    7   }
    8   return 0;
    9 }
[zsh@ecs-78471 10_18_复习环境变量]$ ./test
env[0]:XDG_SESSION_ID=3849
env[1]:HOSTNAME=ecs-78471
env[2]:TERM=xterm
env[3]:SHELL=/bin/bash
env[4]:HISTSIZE=10000
env[5]:SSH_CLIENT=218.6.151.253 54910 22
env[6]:SSH_TTY=/dev/pts/0
env[7]:USER=zsh
env[8]:LD_LIBRARY_PATH=:/home/zsh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
env[9]: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=01;05;37;41:su=37;41:sg=30;43:ca=30;41: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:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=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:*.jpg=01;35:*.jpeg=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:*.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:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
env[10]:MAIL=/var/spool/mail/zsh
env[11]:PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zsh/.local/bin:/home/zsh/bin
env[12]:PWD=/home/zsh/linux/104new/10_18_复习环境变量
env[13]:LANG=en_US.UTF-8
env[14]:bbbb=50
env[15]:HISTCONTROL=ignoredups
env[16]:SHLVL=1
env[17]:HOME=/home/zsh
env[18]:LOGNAME=zsh
env[19]:SSH_CONNECTION=218.6.151.253 54910 192.168.0.183 22
env[20]:LESSOPEN=||/usr/bin/lesspipe.sh %s
env[21]:XDG_RUNTIME_DIR=/run/user/1002
env[22]:HISTTIMEFORMAT=%F %T zsh 
env[23]:_=./test
env[24]:OLDPWD=/home/zsh/linux/104new

c语言中函数无参,也是可以传参的,只不过实参没用上

 ​​​​​​​

 但是如果是int fun(void) 就不可以传参。

这就解释了我们可以直接给main()函数传参的原因

#include <stdio.h>
int main(int argc, char* argv[], char* env[])
{
	int i = 0;

	for (; env[i]; i++) {   
		printf("%s\n", env[i]);
	}
	return 0;
}

——————————————————————————————————————————

4.通过代码获取环境变量的三种方式

①C语言获取环境变量的第一种方法

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

②通过第三方变量environ获取 

c语言给我们提供了一个全局变量environ

getenv——获取环境变量的接口

通过环境变量名直接获得环境变量的内容

a.我为什么要获取环境变量? ?        万一以后要用(一定有特殊用途!)
b:环境变量是谁给我的呢??        目前谈不清,但是我们可以观察到!
环境变量为什么具有全局性!

五.程序地址空间

1.初步认识

程序地址空间,不是内存!
程序地址空间=进程地址空间,是操作系统上的概念!

堆,栈相对而生,

堆区向地址增大方向增长
栈区向地址减少方向增长

我们一般在C函数中定义的变量,通常在栈上保存,那么先定义的一定是地址比较高的!
如何理解static变量——函数内定义的变量用static修饰,本质是编译器会把该变量编译进全局数据区!

2.感知地址空间的存在!

函数内定义的变量用static修饰,本质是编译器会把该变量编译进全局数据区! 父子进程共享全局变量。

fork创建子进程,父子进程同时运行,若在子进程中第5秒改了全局变量g_val的值,会发现

父子进程读取同一个变量(因为地址一样!),但是子进程修改的全局变量后,父子进程读取到的
内容却不一样! ! ! !
结论:——我们在C/C++中使用的地址,绝对不是物理地址
如果是物理地址,这种现象不可能产生

3.让每一个进程都认为自己是独占系统中的所有资源

每一个进程在启动的时候,都会让操作系统给他创建一个地址空间,该地址空间就是进程地址空间

每一个进程 都会有一个自已的进程地址空间! !
操作系统要不要管理这些进程地址空间呢??
先描述,在组织
进程地址空间,其实是内核的一个数据结构,struct mm_ struct (稍后看! )
举例子:

①我们往银行存10亿,但是银行可能没10亿,但银行给你画的饼就是10个亿。

②美国一个富翁有3个私生子,富翁给每个私生子画的大饼:说自己有10个亿以后都是你的财产,富翁: OS
三个私生子:进程
富翁给三个私生子画的大饼:进程地址空间
让每一个进程都认为自己是独占系统中的所有资源的! !

4.虚拟地址空间=进程地址空间 mm_struct

所谓的进程/虚拟地址空间:其实就是OS通过软件的方式,给进程提供一个软件视角,认为自己会独占系统的所有资源(内存),每个进程会维护一个mm_struct,虚拟地址和物理内存通过页表建立映射关系,(上学时,一个班级中,老师点名需要一张名单,这个名单就是虚拟地址空间)

linux下:逻辑地址=虚拟地址=线性地址
 

什么叫做区域

每个区域范围,都是可以有对应的编号的,在虚拟地址空间 mm_struct 中有存储各个数据区的起始地址和结束地址,也就是区域

5.写时拷贝

写时拷贝正好可以回答fork 现象:三.2 子进程修改的全局变量后,父子进程读取到的内容却不一样
(下面这两张图是一样的)

因为进程具有独立性! !做到互不影响。当子进程修改g_val时,为了做到子进程不影响父进程,会发生写时拷贝:g_val会再拷贝一份,子进程中的映射关系会改变,指向新的g_val,但是g_val的虚拟地址(相对地址)还是原来的地址,和父进程的g_val虚拟地址(相对地址)一样,但是他们的物理地址不一样,100改成200时,只会改变新的g_val(写时拷贝本身就是有OS的内存管理模块完成的!所以我们感知不到

为什么要写时拷贝?创建子进程的时候,就把数据分开,不行吗? ?

答:1.父进程的数据,子进程不一定全用,即便使用,也不一定全部写入——会有浪费空间的嫌疑
2. 最理想的情况,只有会被父子修改的数据,进行分离拷贝。不需要修改的共享即可——但是从技术角度实现复杂,不可能实现
3.如果fork的时候,就无脑拷贝数据给子进程,会增加fork的成本(内存和时间)

所以最终采用写时拷贝:
①写时拷贝只会拷父子修改的,变相的,就是拷贝数据的最小成本
②拷贝的成本依旧存在
写时拷贝本质是延迟拷贝策略!只有真正使用的时候,才给你!
你想要,但是不立马使用的空间,先不给你,那么也就意味着可以先给别人!
变相的提高内存的使用率!

fork有两个返回值,pid_ t id ,同一个变量,怎么会有不同的值?

pid_ t id是属于父进程栈空间中定义的变量,fork内部,return 会被执行两次,return 的本质,就是通过寄存器将返回值写入到接受返回值的变量中! !
当id=fork()的时候,谁先返回,谁就要发生写时拷贝,所以,同一个变量,会有不同的内容值,本质是因为大家的虚拟地址是一样的,但是大家对应的物理地址是不一样的! !

6.为什么要有虚拟地址空间?

访问内存添加了一层软硬件层,可以对转化过程进行审核,非法的访问,就可以直接拦截了

1.保护内存!
2.进程管理-Linux内存管理
通过地址空间,进行功能模块的解耦! !
3.让进程或者程序可以以一种统一的视角看待内存!
方便以统一的方式来编译和加载所有的可执行程序
简化进程本身的设计与实现!
 

猜你喜欢

转载自blog.csdn.net/zhang_si_hang/article/details/126566601
今日推荐