Linux|[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)

本文基于CentOS,深入解释进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列。
目录
一、进程优先级
1.基本概念
2.查看系统进程
3. PRI && NI
4.其他概念
二、环境变量
1. 基本概念
2. 常见环境变量
3. 查看环境变量方法
4. 和环境变量相关的命令
【Linux|[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)】5. 环境变量的组织方式
6. 通过代码如何获取环境变量
7. 通过系统调用获取或设置环境变量
8. 环境变量通常是具有全局属性的
三、程序地址空间
1.程序地址空间
四、进程地址空间
五、Linux2.6内核进程调度队列
1.进程队列的数据结构
2. 优先级
3.活动队列
4.过期队列
5. active指针和expired指针
一、进程优先级 1.基本概念 cpu资源分配的先后顺序,就是指进程的优先权(priority)。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
2.查看系统进程 在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容:
Linux|[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)
文章图片

列表中:
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值
3. PRI && NIPRI即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,值越小进程的优先级别越高
NI是nice值,其表示进程可被执行的优先级的修正数值
PRI值越小越快被执行,加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行,调整进程优先级,在Linux下,就是调整进程nice值
nice其取值范围是-20至19,一共40个级别。
用top命令更改已存在进程的nice:进入top后按“r”–>输入进程PID–>输入nice值
4.其他概念 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级;
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰;
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行;
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
二、环境变量 1. 基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。
在编写C/C++代码的时候,在链接的时候,不知道链接的动态静态库在哪里,但依然链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
2. 常见环境变量 PATH : 指定命令的搜索路径
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
SHELL : 当前Shell,它的值通常是/bin/bash。
3. 查看环境变量方法

echo $NAME//NAME:环境变量名称
4. 和环境变量相关的命令 echo: 显示某个环境变量值
export: 设置一个新的环境变量
env: 显示所有环境变量
unset: 清除环境变量
set: 显示本地定义的shell变量和环境变量
5. 环境变量的组织方式 Linux|[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)
文章图片

每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串.
6. 通过代码如何获取环境变量 命令行第三个参数
#include int main(int argc, char *argv[], char *env[]) { int i = 0; for(; env[i]; i++){ printf("%s\n", env[i]); }return 0; }

通过第三方变量environ获取
#include int main(int argc, char *argv[]) { extern char **environ; int i = 0; for(; environ[i]; i++){ printf("%s\n", environ[i]); }return 0; }

libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。
7. 通过系统调用获取或设置环境变量 常用getenv和putenv函数来访问特定的环境变量。
#include #include int main() { printf("%s\n", getenv("PATH")); return 0; }

8. 环境变量通常是具有全局属性的 环境变量通常具有全局属性,可以被子进程继承下去
三、程序地址空间 1.程序地址空间 C语言的空间布局图
Linux|[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)
文章图片


#include #include #include int g_val = 0; int main() { pid_t id = fork(); if(id < 0){perror("fork"); return 0; }else if(id == 0){ //childprintf("child[%d]: %d : %p\n", getpid(), g_val, &g_val); }else{ //parent printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val); }sleep(1); return 0; }

输出:
Linux|[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)
文章图片

输出出来的变量值和地址是一模一样的。可是将代码稍加改动:
#include #include #include int g_val = 0; int main() { pid_t id = fork(); if(id < 0){perror("fork"); return 0; }else if(id == 0){ //child g_val=1; printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val); }else{ //parent sleep(3); printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val); }sleep(1); return 0; }

输出:
Linux|[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)
文章图片

父子进程,输出地址是一致的,但是变量内容不一样 。
这是因为:
我们在用C/C++所看到的地址,全部都是虚拟地址!物理地址,由OS统一管理。
OS必须负责将虚拟地址转化成物理地址。
四、进程地址空间 进程的地址空间由允许进程使用的全部线性地址组成。每个进程所看到的线性地址集合不同,一个进程的地址和另一个进程的地址间没有什么关系。内核可以动态修改进程的地址空间。
分页式内存管理可以将一段程序加载到不连续的物理空间上,但是从虚拟地址空间来看依旧是连续的,这样就可以解决内存使用率低的问题
序A和程序B分别被映射到了两块不同的物理内存,它们之间没有任何重叠,如果程序A访问的虚拟地址超出了 0X00A00000 这个范围,系统就会判断这是一个非法的访问,拒绝这个请求,并将这个错误报告给用户,通常的做法就是强制关闭程序。程序可以使用固定的内存地址。虚拟内存无论被映射到物理内存的哪一个区域,对于程序员来说都是透明的,我们不需要关心物理地址的变化,只需要按照从地址 0X00000000 到 0X00A00000 来编写程序、放置变量即可,程序不再需要重定位。
现代计算机都使用分页(Paging)的方式对虚拟地址空间和物理地址空间进行分割和映射,以减小换入换出的粒度,提高程序运行效率。
分页与虚拟地址空间
Linux|[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)
文章图片


五、Linux2.6内核进程调度队列 1.进程队列的数据结构 Linux2.6内核中进程队列的数据结构:
Linux|[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)
文章图片


一个CPU拥有一个runqueue,如果有多个CPU就要考虑进程个数的负载均衡问题。
2. 优先级 普通优先级:100~139(普通的优先级,与nice值的取值范围对应)
实时优先级:0~99
3.活动队列 时间片还没有结束的所有进程都按照优先级放在该队列;
nr_active: 总共有多少个运行状态的进程;
queue[140]: 一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进行排队调度,所以,数组下标就是优先级;
从该结构中,选择一个最合适的进程的过程:
(1) 从0下表开始遍历queue[140];
(2) 找到第一个非空队列,该队列必定为优先级最高的队列;
(3) 拿到选中队列的第一个进程,开始运行,调度完成;
(4) 遍历queue[140]时间复杂度是常数!但还是太低效了。
bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32个比特位表示队列是否为空,这样,便可以大大提高查找效率!
4.过期队列 过期队列和活动队列结构一模一样;
过期队列上放置的进程,都是时间片耗尽的进程;
当活动队列上的进程都被处理完毕之后,对过期队列的进程进行时间片重新计算.
5. active指针和expired指针 active指针永远指向活动队列;
expired指针永远指向过期队列;
活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期时一直都存在的, 在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了一批新的活动进程。


    推荐阅读