本篇文章,继续和大家分享与Linux相关的知识。本篇文章的主要内容会涉及到进程的相关的一些其他概念,程序计数器寄存器,进程的上下文以及环境变量等等。

其他概念

这里要补充的概念如下:

Linux-环境变量_环境变量

并行是指多个进程在同一时刻同时运行,而并发是指多个进程在一段时间间隔内同时运行。

通常,我们用的计算机都只搭载有一个CPU,在不考虑多核的情况,我们计算机上所运行的多个程序在CPU上,是交替运行的,也就是并发执行。而我们之所以感觉它们在同时运行,是因为计算机交替切换的速度太快了,我们感知不到。

之前,我们回答过一个问题,一个进程会不会在CPU上运行到结束。答案是不会,如果这个程序是死循环,那么其他程序就永远没法被投入到CPU中运行。那CPU怎么知道什么时候,进行进程切换。在每个进程中,都会存在一个时间片,如果进程在自己的时间片用完之前结束了,那它就结束了。如果时间片用完了,进程还执行完,系统就会进行进程切换,把相应的进程从CPU上剥离下来。


栈帧,大家都了解过吧!函数内部的变量,我们称之为临时变量,会随着函数的结束而销毁。返回值,也是函数的部分,也是临时变量。为什么函数返回值,会被外部拿到呢?我们在看汇编语言的时候,可以看到类似return a ->mov eax 10的语句。当函数返回的时候,函数的返回值,会先保存到寄存器中,在通过寄存器拿给外部的变量。

我们写的代码中有各种函数调用,需要跳转到不同的地方执行代码。那系统是如何得知我们的进程当前执行到哪行指令了?在CPU中存在寄存器程序计数器pc、eip:会记录当前进程正在执行指令的下一行指令的地址!也就是每执行一行代码,寄存器就会保存下一行代码的地址。

在CPU中除了程序计数器这样的寄存器外,还有很多的寄存器。比如:通用寄存器:eax、ebx、ecx、edx, 栈帧:ebp、esp、eip,状态寄存器:status。

这么多的寄存器有什么用呢?它们扮演者一个什么样的角色?这些寄存器用来提供效率,把进程高频数据放入寄存器中。也就是说,cpu内的寄存器里面保存的是,进程相关的,需要访问或者修改的数据。我们把,CPU寄存器里面保存的进程的临时数据,称之为进程的上下文

进程切换的核心工作:1.保存上下文 2.恢复上下文


进程在从CPU上离开的时候,要将自己的上下文数据保存好,甚至带走。这样未来,再次运行的时候,可以恢复上下文。

Linux-环境变量_内建命令_02

环境变量

我们先来见见环境变量,常见的环境变量有PATH,SHELL,USER,HOME。

Linux-环境变量_环境变量_03

什么是环境变量

这里我们直接给出概念,再慢慢理解。环境变量是系统提供的一组name=value形式的变量,不同的环境变量有不同的用户,通常具有全局属性。

Linux-环境变量_寄存器_04

查看环境变量

PATH

直接使用PATH,系统告诉你没有该指令

Linux-环境变量_子进程_05

查看某个环境变量的内容的正确方式是echo $+环境变量名

[common_108@iZf8zaj27gxmvq7veqrekfZ ~]$ echo $PATH

PATH的内容,很明显,是用分号作为分割符的几个路径。

Linux-环境变量_子进程_06

还记得我们之前说过吗?指令本质是一个个放在特定路径下的可执行程序。可为什么运行我们自己写的程序需要加点斜杠,而不能像运行ls指令那样不加点斜杠。这是因为Linux系统默认搜索的路径是PATH环境变量中的路径,而这些路径并没有包含我们程序所存在的路径。

Linux-环境变量_子进程_07

我们只需要把我们当前所处的路径加到PATH环境变量中,系统就能认识我们的指令了。

Linux-环境变量_寄存器_08

反之,如果我们把PATH中的内容,置空,哪系统基本什么指令也不认识了。

Linux-环境变量_寄存器_09

如果我们不小心像破坏了环境变量,我们重启xshell,PATH的内容就恢复了。

Linux-环境变量_环境变量_10

HOME

HOME环境变量的内容,是我们当前使用账号的家目录。每次启动xshell登录账号,系统就会帮我们自动填充该变量,并进入该变量的路径,这也就是为什么我们每次登录不同的账号,会在不同的家目录的原因。

此时,我们是common_108

Linux-环境变量_内建命令_11

当我们将账号换成root,HOME内容也会随着变化。

Linux-环境变量_子进程_12

SEHLL

SEHLL环境变量的内容,是我们所使用的外壳程序。

Linux-环境变量_子进程_13

更多的环境变

如果查看更多环境变量,可以使用env指令

[common_108@iZf8zaj27gxmvq7veqrekfZ test]$ env

Linux-环境变量_寄存器_14

HISTSIZE

我们往上翻,可以查找历史指令。系统究竟会记录多少条历史指令呢?这个由HISTSIZE这个环境变量来决定。

Linux-环境变量_子进程_15

SSH_CLIIENT和SSH_TTY

还记得我们第一次登录账号时用的指令吗?ssh指令,这两个环境变量和连接远端服务器有关。

Linux-环境变量_寄存器_16

USER

USER这个环境变量的内容记录者连接远端服务器的账号用户名。

Linux-环境变量_子进程_17

LS_COLORS

LS_COLORS这个环境变量和配色方案有关。

Linux-环境变量_内建命令_18

PWD

PWD这个环境变量记录了我们当前所处的路径

Linux-环境变量_寄存器_19

LOGNAME

LOGNAME这个环境变量记录着登录账号的名称,和USER没太大区别

Linux-环境变量_寄存器_20

OLDPWD

OLDPWD环境变量记录的是上一次我们所处的路径。我们使用cd -指令,回到哪,由该环境内容决定

Linux-环境变量_内建命令_21

在程序中获取环境变量

我们在程序中,如果想获取某个环境变量的内容,可以使用getenv函数

Linux-环境变量_环境变量_22

Linux-环境变量_寄存器_23

它获取到的内容,使用命令行解释器获取的是一样的

Linux-环境变量_寄存器_24

现在,我们获取利用getenv来获取USER这个环境变量。

Linux-环境变量_子进程_25

通过比较USER环境变量的内容,我们就完成了对权限的控制。

Linux-环境变量_环境变量_26

如果我们想env一样获取全部环境变量,该怎么做?

命令行参数

main主函数也是函数,它也是函数,是函数就可以进行传参。下面,这种带参数的main函数,大家应该见过吧。argc是一个整形,c就是count数量的意思,argv是一个指针数组,v就是vector向量的意思,可以理解为argv用来存放一个个的向量表,以NULL结尾。

Linux-环境变量_寄存器_27

编译运行,我们就可以看到如下的结果,./proc被作为参数获取了。

Linux-环境变量_寄存器_28

如果我们换一种方式运行程序,你可以看到如下结果:

Linux-环境变量_环境变量_29

我对程序做一下修改。

Linux-环境变量_子进程_30

编译运行一下,这不就是ls指令不同选项,呈现不同功能的原理吗?

Linux-环境变量_寄存器_31

main函数,它不是第一个运行的程序,第一个运行的程序是Startup,最后一个运行的程序是CRTStartup。为什么main函数可以传参?这是为了指令,工具,软件等提供命令行选项的支持!

Linux-环境变量_子进程_32

main函数除了argc和argv这两个参数,还有第三个参数env。

第三个参数env

这个参数用来接受一张环境变量的向量表,以NULL结尾,NULL在C语言中本质就是0,只是类型不同。我们可以通过如下方式打印环境变量的内容。

Linux-环境变量_环境变量_33

程序打印的内容和env指令的内容是一致的。

Linux-环境变量_子进程_34

带三个参数的main函数,每次都会接受两张向量表,一是argv,命令行参数表、二是env,环境变量表。

刚刚为什么,我说环境变量具有全局属性呢?这是因为我们所运行的程序都是子进程,bash本身在启动的时候,会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程交给自己的环境变量!这个怎么证明?

Linux-环境变量_寄存器_35

本地变量

我们可以通过创建本地变量中的方式来验证,本地变量只在创建它的进程中有效。

我们所创建的Hello,当前进程是认识的,可在环境变量中查找不到,这是什么原因?

Linux-环境变量_寄存器_36

当然,查不到啦!我们创建的Hello是本地变量。这里介绍一个新指令set.

set

set指令,可以查看当前进程中的所有变量。此时,我们再查就可以发现Hello了。

Linux-环境变量_子进程_37

编写如下程序:

Linux-环境变量_环境变量_38

编译运行,发现程序并不认识该变量。

Linux-环境变量_子进程_39

unset

unset指令用来删除本地和环境变量,我们先将Hello这个变量删除,

Linux-环境变量_子进程_40

export

再使用export来创建环境变量

Linux-环境变量_子进程_41

在创建好Hello这个环境变量后,我们可以使用env指令查到它。程序也能正常获取它内容。

Linux-环境变量_环境变量_42

bash会创建一个子进程来运行我们的程序。刚刚我们创建的本地变量Hello子进程不认识,当我们将Hello变成环境变量后,子进程就认识,这说明环境变量会被子进程继承。

现在,我们将Hello重新,改成本地变量。Hello确实是本地变量,可为什么echo会认识它,不是说子进程不会继承本地变量吗?

Linux-环境变量_寄存器_43

这是因为echo指令是内建指令,bash不会创建子进程而是自己亲自去执行完成。

指令分为两批,一批叫常规命令,通过创建子进程来完成,一批叫内建命令通过bash自己来完成。

内建命令

内建命令,是由bash自己来执行完成。这些命令,通常是bash自己实现的,或者系统调用提供的函数。常见的内建命令,有cd和echo等。


Linux-环境变量_寄存器_44

由于子进程会继承父进程的环境变量,我们还有一种方式去打印环境变量的内容。通过二级指针environ。

Linux-环境变量_内建命令_45

Linux-环境变量_寄存器_46

打印的内容也是和env指令打印的内容是一直的。

Linux-环境变量_内建命令_47

好了,到这里,我们本次的分享就到此结束了,不知道我有没有说明白,给予你一点点收获。关于更多和Linux相关的知识,会在后面的文章更新。如果你有所收获,别忘了给我点个赞,这是对我最好的回馈,当然你也可以在评论发表一下你的收获和心得,亦或者指出我的不足之处。如果喜欢我的分享,别忘了给我点关注噢。