进程优先级
概念:CPU
分配资源的先后顺序,就是计算机中优先级的提现。优先级高的进程优先受到CPU
的调度。以此CPU
可以根据优先级更好的进行资源的分配。
为什么会有优先级?
优先级出现的本质原因是计算机中资源是有限的,每个进程都需要受到
CPU
的调度,操作系统需要根据计算机的运行状态对一些进程进行优先调用,所以优先级应运而生。
优先级信息存储再在哪?
优先级信息存储在
PCB
中。
查看系统进程信息
格式:ps -al
功能:显示系统当前进程的部分信息
接下来我们一一对这些信息进行介绍:
(
1
)
(1)
(1)UID
全称:User Identification 用户身份证明
UID
表明了当前进程执行者
的编号。我们知道进程=程序+内核对应的数据结构。在Linux
中,我们查看某个进程的拥有者或者所属组时,其显示的都是具体的用户名。但是在Linux
中其实这些用户名对应了一个个的编号。拿现实世界举例,UID
就是身份证编号,其具有唯一性,而用户名就是姓名。
(
2
)
(2)
(2)PID
全称:Process Identification 进程表示符
PID
是每个进程独有的编号,用于区别计算机中的不同进程。
( 3 ) (3) (3)PPID
PPID
为当前进程父进程的编号。
(
4
)
(4)
(4)NI
NI
是计算机中的进程修正数据,计算机中可以通过修改NI
来间接修改优先级数据PRI
。
(
5
)
(5)
(5)PRI
PRI
表明了进程优先级,PRI
越小,其优先级越高。
优先级的更改
对于优先级的修改有多种方式,这里我们只讲一种:
(
1
)
(1)
(1)top命令修改进程优先级
前面我们介绍了优先级修正数据NI
,我们一般都是使用NI
来间接修改当前进程的优先级。
公式:
PIR(new)=PIR(old)+NI
PIR(old)
默认为80
,所以每次我们只需要修改NI
的数值,就可以直接修改当前进程的优先级。这也是为什么NI
叫优先级修正数据的一个原因。
注:NI
的数值也可以称作nice
值。
nice值取值范围:-19-20
接下来我们通过top
命令对当前进程的PRI优先级进行一次修改:
修改过程如下:
(1)输入top
命令
(2)输入进程PID
(3)输入nice
值
但是,为什么nice
需要设置一个范围呢?
首先,我们需要知道,操作系统的存在是为了计算机资源能够受到更好的分配。所以说操作系统设置nice
值一定是符合当前计算机运行状态的。
如果扩大nice
值范围,就会出现一个问题。
一旦多个进程的优先级非常高,这时就可能导致一些优先级低的进程长时间不被CPU
进行调度,这样就可能出现进程"饥饿问题"
。
而操作系统需要较为均衡的让每个进程享受到CPU
资源,所以人为不能进行过大的干预。
事实上,在计算机中存在40
个运行队列,正好对应nice
值具有40
个数值,因此CPU可以从高优先级的队列开始调度,依次顺下。
扩充一些概念:
竞争性:系统的进程很多,进程优先级就是不同进程之间竞争性的体现。
独立性:进程与进程之间是独立的,互不影响的。
并行:多个进程同时在不同的CPU
上运行,也就是多进程同时运行
并发:在单CPU
中,在一段时间内,通过进程切换操作,使得多个进程都得以被CPU
调度达到推进。
环境变量
前面我们提了一个概念:在Linux
中,一切皆文件。
命令,程序,工具的本质都是一个可以执行的文件
我们平常写的test
可执行程序也是一个文件,那么这个文件是否可以看作是一个命令呢?
答案是对的,可以将其看作是一个命令。
问题又来了,那为什么我们需要自己写的test
命令,需要./test
才能运行呢,而系统的命令ls
,可以直接使用。
为什么系统的命令不需要带路径呢?
原因:因为环境变量
接下来我们正式开始介绍部分环境变量:
那么环境变量的本质是什么?
环境变量也是一种变量:=变量名+变量内容
定义变量不仅仅是语言的任务,语言上面定义变量本质是在内存中开辟空间(有编号/名字),环境变量本质是操作系统在内存/磁盘中开辟的空间,用来保存系统相关的数据,环境变量和语言中定义的变量其实没有太多的区别,只不过环境变量存储的是系统的一些数据,而语言定义的变量存储的一般是一些具体的数值,两者都是变量名+变量内容,只不过环境变量是操作系统自动替我们执行的
。
(
1
)
(1)
(1)PATH
执行系统的命令其实也需要带路径,但是这一工作操作系统替我们干了。在环境变量PATH
中存储了很多组不同的路径,操作系统会从前往后进行路径的匹配,当匹配成功时就执行该命令。所以说我们在使用系统命令的时候不用带路径,因为操作系统承担了这一工作。
像如gcc
,gdb
可以直接使用,可以直接连接库文件…都是因为环境变量的存在,所以可以直接进行检索。
(
2
)
(2)
(2)HOME
每个用户的HOME
环境变量都是不一样的
格式:echo $环境变量名
功能:查看环境变量
注:
安装软件到系统中,其实本质就是将软件拷贝到系统环境变量中的特定路径下。
格式:export 环境变量名= 路径
功能:环境变量内原有路径被清空,并被赋予进新的路径
通过export
指令我们可以使得自己的命令也不需要带路径,并且还可以验证系统命令会自动到PAHT
环境变量中匹配路径
生成一个proc
可执行程序,源代码如下
int main()
{
cout<<"验证成功"<<endl;
}
将该可执行程序的路径通过export
导入PATH
环境变量
注:导入的环境变量都是单次登录有效的,因为没有修改配置文件。
格式:export 环境变量名= $环境变量名:路径
功能:在环境变量原有路径的基础上追加一条新的路径
环境变量的全局属性
2.对于环境变量,我们需要知道环境变量具有全局性。
这里的全局性和继承该如何理解呢?
子进程会继承父进程的环境变量。
接着我们通过一棵多叉树进行解释。
我们知道在Linux
中,都是bash
为我们和操作系统进行交互,用户在登陆的时候,bash
会自动从~/.bashrc
和~/.bash_profile
这两个配置文件中把环境变量读入了自己的环境变量列表中,父进程会将环境变量参数传递给运行的子进程,这样子进程就继承了父进程的环境变量,这就使得每一个子进程都间接或直接继承了bash
的环境变量,所以说环境变量具有全局性,就像这棵多叉树一样,影响了整个用户系统。
所以我们使用gcc,g++
这些命令的时候都够不带选项,直接链接一些库,还是因为其继承了bash
的环境变量,所以系统会自动替我们查找。
关于子进程会继承父进程的环境变量,在文章的末尾我们会进行代码上的验证。
本地变量
格式:env
功能:查看当前系统的环境变量
系统上还存在一种变量,只在本次登录有效,称为本地变量
我们自己创建的变量基本都是本地变量。
注意:本地变量不具有继承属性。
格式:set
功能:显示本地变量和环境变量
格式:export 本地变量
功能:将本地变量转成环境变量
注:但是这个本地变量因为没有写入配置文件里,即使导入变成了环境变量,关掉xshell
以后一样会被清除。
格式:unset 本地变量/环境变量
功能:取消本地定义的本地变量或者环境变量
命令行参数
在了解什么是命令行参数之前,我们需要重新看向main
函数。
在main
函数内其实也是可以传递参数的,这里我们先介绍其中的两个参数。
参数一:char *argv[]
作用:argv是一个char类型的动态指针数组。argv的长度由argc决定。
参数二:int argc
作用:决定argv的长度
实际上,我们在命令行上敲的命令和后缀其实都是传递给了argv
这个指针数组
如ls
,ls -a
,以及我们自己写的可执行程序./proc
,./proc -a
等等,如果我们的命令不带后缀选项,那么argv
数组就只有源程序一个元素,argc
个数就是我们传递的参数个数,也就是argv
的元素个数。
对此我们进行验证:
int main(int argc,char*argv[])
{
for(int i=0;argv[i];i++)
cout<<argv[i]<<endl;
}
为了更好的理解,我们通过一个图像来进一步理解argv
这个字符指针数组。
为什么要使用命令行参数呢?
因为命令行参数的存在,我们可以通过if else
语句进行筛选,从而实现一个命令的不同子功能,我们都知道一个命令具有很多的后缀选项,如mkdir -p
,ls -a
等等,其实这些命令选项底层的实现使用的就是命令行参数。
因此我们可以通过代码来定制我们的命令后缀选项。
int main(int argc,char*argv[])
{
if(argc!=2)
{
cout<<"你需要传递一个参数进来"<<endl;
return 0 ;
}
if(strcmp(argv[1],"a")==0)
cout<<"参数传递正确"<<endl;
else
cout<<"参数传递错误"<<endl;
}
获取环境变量
(
1
)
(1)
(1)env数组
前面我们通过main
函数的前两个参数引出了命令行参数
的概念,接着我们介绍main
函数的第三个参数。
main
函数除了可以传递命令行参数以外,还可以传递环境变量。
参数三:char* env[]
功能:接收环境变量参数
当父进程创建子进程的时候,子进程在运行代码的时候,父进程会将环境变量参数传递给子进程的env
数组
这个想必也非常好理解,因此通过第三个参数我们可以打印出进程当前的环境变量。
#include<iostream>
using namespace std;
E>int main(char *env[])
{
for(int i=0;env[i];i++)
cout<<env[i]<<endl;
}
我们依旧是通过一个图来加深对env
数组的印理解
为何命令行参数中argv数组的元素个数等于argc个,而环境变量没有具体的个数参数。
首先,我们需要知道二者的区别,命令行参数是用户填入的,而环境变量是系统填入的,并不需要人为控制,因此具体的个数没有太大的意义。
(
2
)
(2)
(2)environ指针
environ
是一个系统提供的第三方变量指针,其是一个二级指针,这个二级指针指向了env
数组的首元素,在编译的时候系统会自动让其指向环境变量的位置,因此可以通过environ
指针来获取环境变量。
代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
extern char** environ;
for(int i = 0;environ[i];i++)
{
printf("%s\n",environ[i]);
}
return 0;
}
(
3
)
(3)
(3)env命令
格式:env
功能:获取环境变量
( 4 ) (4) (4)getenv函数
头文件:#include<cstdlib>
格式:getenv(环境变量名)
功能:getenv函数会返回对应环境变量的字符串。
内置命令和外部命令
首先,我们需要知道在命令行上启动的进程,父进程大都是是bash
,除此之外,系统内还有一些内置命令如export
等等都是由父进程自己执行的。内置命令的执行速度一般快于外部命令。因为其不用创建子进程等操作,所以执行速度更快。
常见的内置命令有:cd,export…
关于环境变量与继承(重要)
在fork
中我们谈到了继承关系,需要注意只有fork
创建的子进程才会继承父进程的代码和数据,以父进程的PCB
,进程地址空间和页表(在下一个博客会讲到)为模板构建自己的PCB
,进程地址空间和页表。普通的父进程和子进程之间不具有这种继承属性。
而对于环境变量的继承性是所有父子进程之间都具有这种继承的属性,这里大家要分辨清楚,不要搞混了。