Linux进程概念(中):进程优先级和环境变量

发布于:2025-09-14 ⋅ 阅读:(18) ⋅ 点赞:(0)


一、进程优先级

优先级是什么?

优先级就是获取某种资源的先后顺序。
进程优先级就是进程获取CPU资源分配的先后顺序,进程获取CPU资源分配的先后顺序,就是指进程的优先权(priority),优先权高的进程有优先执行的权力。

为什么需要优先级?

主要原因是因为资源是有限的。
需要进程优先级的主要原因就是CPU资源是有限的

竞争性:因为CPU资源是有限的,而进程是多个的,所以进程之间是具有竞争属性的

操作系统为了高效完成任务,让进程良性竞争相关资源,,于是便有了优先级。

如果一个进程长时间得不到CPU资源,该进程的代码会长时间得不到推进,造成该进程的饥饿问题。存在该问题的操作系统是低效的。

怎么控制进程优先级?

查看进程优先级

在Linux或者Unix操作系统中,用ps -l命令会类似输出以下几个内容:

ps -l

在这里插入图片描述
我们很容易注意到其中的几个重要信息,如下:

  • UID:代表执行者的身份
  • PID:代表这个进程的id
  • PPID:代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
  • PRI:代表这个进程可被执行的优先级,其值越小越早被执行
  • NI:代表这个进程的nice值
PRI and NI
  • PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
  • 那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值
  • PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:
    P R I ( n e w ) = P R I ( o l d ) + n i c e PRI(new)=PRI(old)+nice PRI(new)=PRI(old)+nice
  • 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即优先级会变高,则其越快被执行
  • 因此,我们调整进程优先级,在Linux下,就是调整进程nice值
  • 但是,如果用户随意修改进程优先级,是不安全的。
  • 所以nice其取值范围是-20至19,一共40个级别

注意:在Linux操作系统当中,PRI(old)默认为80,即PRI = 80 + NI

PRI vs NI
  • 需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化
  • 可以理解nice值是进程优先级修正的修正数据
用top命令修改进程的nice

top命令就相当于Windows中的任务管理器,它能够动态实时的显示系统当中进程的资源占用情况。
在这里插入图片描述
修改步骤:

  1. 进入top后按‘r’
  2. 输入需要修改进程的PID
  3. 输入nice值
用renice命令修改进程的nice值

使用renice命令,后面跟上更改后的nice值和进程的PID即可
在这里插入图片描述
注意: 若是想使用renice命令将NI值调为负值,也需要使用sudo命令提升权限。


二、环境变量

基本概念

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数

例如,我们在编写C/C++代码的时候,在链接时,从来不知道我们所链接的动静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。

环境变量通常具有某些特殊用途,并且在系统当中通常具有全局特性

常见环境变量

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

查看环境变量方法

可以通过echo命令来查看环境变量,如下:

echo$NAME //NAME为需要查看的环境变量名称

在这里插入图片描述

测试PATH

我们知道,我们平时使用的命令本质也就是一个一个的可执行程序,然而我们使用命令却不带./就可以执行,而我们自己生成的可执行程序必须要在前面加上./才能执行。
在这里插入图片描述
./的作用是什么?没错,就是告诉系统可执行程序的位置。

因为 Linux 默认不会在当前目录下寻找可执行程序,而 ./ 明确地告诉系统:“我要运行的程序就在当前目录下”

而命令无需./,系统是如何知道它的位置呢?
就是通过环境变量PATH来找到命令的,查看环境变量PATH我们可以看到如下内容:
在这里插入图片描述
可以看到环境变量PATH当中有多条路径,这些路径由冒号隔开,当你使用ls命令时,系统就会查看环境变量PATH,然后默认从左到右依次在各个路径当中进行查找。

那可不可以让我们自己的可执行程序也不用带路径就可以执行呢?

当然是可以滴!

方式一:将可执行程序拷贝到环境变量PATH的某一路径下。
sudo cp proc /usr/bin

在这里插入图片描述

方式二:将可执行程序所在的目录导入环境变量PATH中

用export命令,可以将某个目录导入到环境变量PATH中:

export PATH=$PATH:/home/zhh/process_code

在这里插入图片描述
这样,我们无需./就可以执行程序了:
在这里插入图片描述

测试HOME

任何一个用户在运行系统登录时都有自己的主工作目录(家目录),环境变量HOME当中即保存的该用户的主工作目录。

普通用户:
在这里插入图片描述
超级用户root:
在这里插入图片描述

测试SHELL

我们在Linux操作系统当中所敲的各种命令,实际上需要由命令行解释器进行解释,而在Linux当中有许多种命令行解释器(例如bash、sh),我们可以通过查看环境变量SHELL来知道自己当前所用的命令行解释器的种类。
在这里插入图片描述

而该命令行解释器实际上也是系统当中的一条命令,当这个命令运行起来变成进程后就可以为我们进行命令行解释。

和环境变量相关的一些命令

  1. echo:显示某个环境变量值
  2. export:设置一个新的环境变量
  3. env:显示所有环境变量
  4. unset:清除环境变量
  5. set:显示本地定义的shell变量和环境变量

环境变量的组织方式

在这里插入图片描述
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串

通过代码获取环境变量

方式一:命令行第三个参数
命令行参数

你可能不知道,main函数其实是有参数的:
在这里插入图片描述
因为我们平时几乎用不上它们,所以一般情况下都没有写出来。

其实,这三个参数称为命令行参数
既然argv是一个字符数组指针,我们不妨打印看看内容:

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

在这里插入图片描述
原来,argv当中的第一个字符指针存储的是可执行程序的位置,其余字符指针存储的是所给的若干选项,最后一个字符指针为空。
原理很好理解,先初始化指针数组的每个指针为空,然后读取命令行字符串,遇上空格就换下一个字符指针读取,直到字符串读取完毕

在这里插入图片描述
而main函数的第一个参数argc代表的就是字符指针数组当中的有效元素个数。

argc和argv结合使用就可以根据你所给选项给出不同的提示语:

#include <stdio.h>                                                                                                                         
#include <string.h>
int main(int argc, char *argv[], char* envp[])
{
	if(argc > 1)
	{
		if(strcmp(argv[1], "-a") == 0)
		{
			 printf("you used -a option...\n");
		}
		else if(strcmp(argv[1], "-b") == 0)
		{
			printf("you used -b option...\n");
		}
		else
		{
			printf("you used unrecognizable option...\n");
		}
	}
	else
	{
		printf("you did not use any option...\n");
	}
	return 0;
}

运行结果:
在这里插入图片描述

其实,这就是我们平时使用的命名的选项的实现逻辑!

main函数的第三个参数envp接收的实际上就是环境变量表,我们可以通过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;
}

运行结果同样是和env指令是一样的,可以显示所有环境变量

通过第三方变量environ获取

libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。

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

运行结果也同样是和env指令是一样的

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

在系统调用接口中,常用getenv和putenv函数来访问特定的环境变量。

  • 本文只讲解getenv
    getenv函数可以根据所给环境变量名,在环境变量表当中进行搜索,并返回一个指向相应值的字符串指针。

需要获取什么环境变量,就用什么环境变量做函数参数
以PATH为例:

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
	 printf("%s\n", getenv("PATH"));
	 return 0;
}

在这里插入图片描述

环境变量通常是具有全局属性的

我们所运行的进程都是子进程,bash自身在启动时,会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程给我们的环境变量。也就是说,所有进程会共用一张环境变量表。

本地变量 and 内建命令

本地变量

简单来说:
本地变量(Local Variable)是只会在当前 Shell 进程内部有效的变量。它不会被其子进程继承。

创建本地变量:

# 创建一个本地变量 MY_LOCAL_VAR
$ MY_LOCAL_VAR="I am a local variable"

# 创建一个环境变量 MY_ENV_VAR
$ export MY_ENV_VAR="I am an environment variable"

# 使用 `echo` 查看,两者看起来一样,都能被当前 Shell 访问
$ echo $MY_LOCAL_VAR
I am a local variable
$ echo $MY_ENV_VAR
I am an environment variable

如果想将本地变量导入环境变量,可以用export直接导入:

$ export MY_LOCAL_VAR

现在我们启动一个子 Shell(比如运行一个新的 bash 进程)来查看区别:

# 启动一个子 Shell
$ bash

# 现在你在一个新的子进程里了
# 尝试打印环境变量(从父进程继承来的)
$ echo $MY_ENV_VAR
I am an environment variable  # 可以看到!

# 尝试打印本地变量(父进程私有的)
$ echo $MY_LOCAL_VAR
                                  # 输出为空!看不到!

# 退出子 Shell,回到父 Shell
$ exit

可以看到:子进程继承了父进程的环境变量,但无法访问父进程的本地变量。

总结

  • 本地变量:像是你的私人笔记,只有你自己能看。你用完了,笔记就扔了。别人(子进程)看不到。
  • 环境变量:像是公告板上的通知,不仅你能看,你叫来帮忙的人(子进程)也能看到公告板上的内容。

内建命令

过去我们发现,我们在使用一些命令的时候,bash都会创建子进程来完成任务,比如之前的grep等等。

我们来思考一下,上面的:

$ echo $MY_LOCAL_VAR
I am a local variable

使用了echo命令,按道理bash应该创建一个子进程呀!那这个本地变量为什么能够显示出来呢?

其实并不是所有的命令bash都会创建子进程的,因此命令分为两批:

  • 常规命令:通过创建子进程完成任务
  • 内建命令:不创建子进程,而是由bash亲自执行,类似于bash调用了自己写的或者系统提供的函数

下篇预告:进程地址空间
有错误欢迎指出,万分感谢
创作不易,三连支持一下吧~
不见不散!