目录
一、进程优先级
1.优先级与权限
2.查看进程优先级
3.PRI与NI
4.修改进程的优先级
5.进程优先级的注意事项
二、进程的其他概念
1.竞争性
2.独立性
3.并行和并发
三、环境变量
1.什么是环境变量
2.环境变量的分类
3.查看环境变量内容
(1)env命令
(2)echo命令
4.常见的环境变量
5.修改环境变量
(1)临时修改
(2)永久修改(用户级环境变量)
6.本地变量与环境变量
(1)定义本地变量
(2)set查找本地变量
(3)创建环境变量
(4)unset清除环境变量
7.获取环境变量
(1)getenv函数
(2)main()函数的参数
(3)environ
优先级:cpu资源的分配顺序,也就是优先执行哪一个进程,就是进程的优先级。
如果只是谁的优先级高,谁就先执行,那么优先级低的进程会不会一直得不到执行呢?
答案是不会的:对于Linux操作系统而言,一个进程的优先级,一般是会随着时间的推移渐渐增长的,所以那些优先级低的进程也会达到高优先级,从而得到处理。
权限:强调的是一个进程能不能被操作,也就是说某一个操作能不能被执行。
ps -l:采取详细的格式来显示进程状况
ps -al:显示所有终端机下执行的程序,除了阶段作业领导者之外 ,并显示他们的详细的格式。
PRI值比较好理解的,即进程的优先级(priority),它的大小决定了程序被CPU执行的先后顺序,此值越小则进程的优先级别越高。PRI有一个初始值,这个初始值被确定就不可更改了,所以下面我们引入了nice值。
NI本名叫做nice值,它是一个修正值可正可负,取值范围是-20至19,一共40个级别,表示进程可被执行的优先级的修正数值。
PRI的值=PRI初始值+NI值,也就是说最终我们看到的PRI是这两个值的和,随着NI的改变PRI的值会发生改变但是进程PRI的初始值是不会改变的。
修改进程优先级的方式有很多,但是本质上还是改变nice值来达到改变优先级的目的。
这里只介绍一种方法——top命令
我们先运行一个进程,通过ps -la查看当前,此时进程的pid为14506,PRI为20,NI为0,让我们改变它的优先级。
具体步骤如下:
(1)用root用户输入top命令(如果是普通用户需要用sudo进行提权),进入Linux的任务管理器。
(2)按下r,输入想要改变优先级的进程pid
(3)输入进程pid完成后(我这里输入的pid是12127),然后输入新的NI,比如将NI设为-19
进程的优先级由80变为61(80+0变为80+(-19))。
但是NI值的修改一定是在一定范围内,如果我们让PRI值过大或过小,都会使操作系统无法正常工作。所以为了避免影响操作系统的正常工作,我们NI值的改变也只能在-20至19之间。如果我们输入大于19的数字,操作系统也只能将NI改为19;如果我们输入小于-20的数字,操作系统也只能将NI改为-20,坚决不能越界。
(1)我们一般不要修改进程的优先级,把这些事情交给最专业的操作系统做就足够了,对进程优先级的不恰当修改可能会给操作系统的管理造成巨大的问题。
(2)一般进程的优先级初始化PRI都是80,NI都是0,我们只可以通过修改NI的方式达到修改PRI的目的。
(3)进程优先级是可以提升的。这里的提升不是说用户去提升它,而是操作系统对驻留在内存中的作业的优先级提升,一般随时间线性化增长。
(4)进程的优先级数值越大,优先级越低。
(5)进程PRI的取值范围,由于NICE的取值范围是[-20,19],所以PRI的取值范围为[60,99]。
对于进程除了上面的基本概念、进程状态和优先级等概念,还有一些我们需要了解的概念。
竞争性: 系统进程数目少则几十多则上百,而CPU只有固定的几个(单核就是一个CPU,多核就是多个CPU),所以进程之间是具有竞争性,都在为了高效完成任务,更合理竞争相关资源,因此出现了优先级。
独立性: 多进程运行,各种资源会为进程服务,但是各个进程之间互不影响叫做进程的独立性。
每个进程有自己的PCB和代码,互相不影响,即使时父子进程也一样。
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
一般而言,在我们研究操作系统时都认为只有一个CPU,而一个CPU只能在同一时间运行一个进程(运行进程和进程处于运行状态不是一回事)。而有的电脑有两个CPU,所以这样的电脑最多在同一时间每一个CPU运行一个进程,也就是两个进程。这种多个CPU同时运行不同进程的现象叫做并行。
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
现在的CPU处理进程时使用时间片轮转的方式,而不是我们想象中的处理完一个再处理另一个,而是定义一个很短的时间片,每当CPU处理进程的时间达到一个时间片,这个进程就必须从CPU上剥离下来。如果在时间片的时间内就处理完毕,那就直接剥离即可。
这个进程的处理进度的临时数据储存在CPU的寄存器内,而进程被剥离时这些临时数据被转移保存在进程的PCB中(实际运行中不是完全保存在PCB上,但在这里我们可以这样理解),然后进程PCB再次进入等待队列或者运行队列。CPU又会处理下一个进程,等到这个进程再次被CPU处理时,这些临时数据又会被加载到寄存器,CPU继续执行后面未完成的指令直到该进程执行完毕。(寄存器和寄存器中的数据不是一回事,处理任何进程时,该进程的执行数据都被寄存器保存,而寄存器的数据可以被来回拷贝转移)
这样不断地进程切换就实现了我们在计算机中看到的多个进程的同时运行的情况。
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,是操作系统为了实现部分功能在内存中预先加载的一批全局变量。
比如我们编写的C/C++程序在链接的时候,就是通过环境变量找到我们的动态库(头文件)的。虽然我们不知道我们链接的动静态库在哪里,但是程序照样可以找到它们成功并生成可执行程序,原因就是有相关的环境变量帮助编译器查找。
环境变量通常具有某些特殊用途,而且在系统当中通常具有全局特性。
环境变量分为系统级环境变量和用户级环境变量。
存在的所有环境变量,任何用户在每次打开计算机时操作系统都会将环境变量加载到内存上。而用户级的环境变量只会在某一个用户登陆时将他对应的环境变量加载进内存,系统级的环境变量不论是哪一个用户登录机器,系统级环境变量都会加载到内存。
可以查看所有的环境变量
上面就是Linux的所有环境变量
env命令也可以用grep查找特定的环境变量,比如说:env | grep PATH
所有的含有PATH的环境变量内容都显示出来了
格式:echo+$+环境变量名
使用echo指令可以查看环境变量的内容
比如说,我想查看PATH这个环境变量的内容,输入:echo $PATH
可以看到这就是PATH环境变量下存储的内容,这里一般存储的是可执行程序的路径。
(1)PATH:PATH存储着可执行程序的搜索路径
我们在前面学习过许许多多的指令,同时Linux系统的也是由C语言写成的。所以系统的指令归根结底也是一个C语言程序,输入的这些指令到最后都会在计算机中执行相应程序的代码。
然而,如果我们运行一个名字叫做test的可执行程序经常是输入:./test,而不是像指令那样直接输入pwd等程序名,也就是说我们运行自己的程序时是需要绝对或相对路径的,而指令的程序就不需要。这个现象就与环境变量有关。
在不指定路径的情况下,在命令行输入一个命令,操作系统会在PATH所存储的路径中依次去查找,查找到就执行该可执行程序,查找不到就会报错。这就解释了为什么在当前目录下生成的可执行文件需要加上路径才能执行,因为我们需要告诉编译器我是在执行指定目录下可执行程序。
(2)HOME :当前用户的家目录(即登录到LInux上时,默认的目录)
我们输入cd ~时,这个~就对应了HOME环境变量
(3)SHELL:当前的shell解释器,通常是/bin/bash
bash是一个系统进程
(4)LANG:Linux系统的语言、地区、字符集
(5)HOSTNAME:服务器的主机名。
(6)HISTSIZE:保存历史命令的数目。
虽然系统最多可以保存3000条已经执行的命令,但是我们用上键查看已经执行的指令时最多也只能上溯大约十多条。
(7)USER:当前登录用户的用户名。
比如cd命令等就会检查当前的HOME环境变量,检测你是谁,是否有权限去操作文件或文件夹
(8)PWD:当前所在目录。
我们的pwd指令也是通过读取这个PWD环境变量实现的
(9)LD_LIBRARY_PATH:C/C++语言动态链接库文件搜索的目录,它不是Linux缺省的环境变量,但对C/C++程序员来说非常重要
看到上面的环境变量你就能理解我们之前学习的指令其实很多都是通过环境变量实现的,而且这些环境变量的使用场景有很多,我们可以在学习Linux的过程中逐渐体会。
格式:export+环境变量名称=$环境变量名称:新的路径
$环境变量名称:为了保留之前的环境变量,类似于在之前环境变量的基础上添加,而不是直接修改
临时修改的特性:只在该系统使用时有效,重启后失效。
我们如果将某一个可执行程序所在的目录也放入到环境变量中,也可以实现无路径执行这个程序。我们可以试一试。
因为这里的修改只是对当前内存加载好的环境变量内容进行修改
在家目录输入:vim ./.bash_profile,然后到对应位置改变对应数据即可。
然后输入:source ~/.bash_profile,让主机重新读取环境变量,由于我们已经改变了存储环境变量的文件,所以每次将环境变量加载到内存中时,都会加载修改后的内容,也就实现了永久修改。
永久改变环境变量可能会导致操作系统使用出现的巨大问题,所以能不修改就不要修改。
在Linux中我们可以定义本地变量,同样也可以用echo查看。比如说,我定义一个myval变量,值为12345,然后用echo查看也可以查到。但是本地变量只在当前的进程内(也就是bash内)有效,在其他进程内都无效。所以我们查看环境变量时也找不到myval。
就像env指令可以查找所有的环境变量,set指令可以查找所有的环境变量和当前进程(bash)内的本地变量。
我们可以查找到原先定义的myval本地变量,但env是查不到的。
我们可以把本地变量升级为环境变量
格式:export+环境变量名
在Linux定义的本地变量,可以转化为环境变量,只要在环境变量的名字前加上export就可以了,此时环境变量也可以查到myval。
也可以直接创建环境变量
格式:export+环境变量名=环境变量内容
环境变量也可以直接用export和内容定义。
格式:unset+环境变量名
使用unset就可以清除环境变量,unset myval,发现我们已经查找不到myval了
首先我们要认识到,环境变量是全局的,它可以被其他进程继承。就像我们C/C++程序中的全局变量,你在任何一个函数里使用它都是可以的。因为不同进程是相互独立的,不能凭空互相干扰,如果没有这种继承性,子进程也就不可能获取环境变量。
char *getenv(const char *name)
头文件:stdlib.h
这个函数可以通过系统调用获取到对应名称的环境变量内容的首地址。
上面是我们写的一段代码,运行后就可以看到内容了
看到这里你很可能会诧异,原来main函数也有参数?为什么我从来也没给main函数写过参数?这些问题在今天就可以解答了。
main函数的声明为:int main(int argc, char *argv[ ], char *envp[ ]) ;
首先,操作系统加载进内存的环境变量不管是名称还是内容都是字符串,所以两个指针数组每一个元素指向的地址都储存着一块内容,在最后以NULL结尾。
也就是说main函数的这些参数是将操作系统导入内存的环境变量和各种命令行制成一个表,并作为参数导入到main函数内,用户使用这两个数组就可以访问环境变量等内容。
为什么我们不把这些参数写上去?
一方面我们的编程中不常用到那么多的环境变量,而且C语言中就已经有了可以获取环境变量的函数了,很多时候我们就不给mian函数写参数了。
我们在最早学习C语言时就规定了main函数的书写规范,返回类型为int,返回值为0
返回值修改成功返回0 ,失败返回非0值
我们也可以不用传递main函数的参数同时达到访问环境变量的目的,不过使用environ需要用extern声明。environ是一个二级指针,指向charenvp[]的第一个元素,我们看下面的代码也可以实现env指令。
看看结果,确实是一致的