进程优先级&环境变量&程序地址空间
1 进程优先级
1.1 进程优先级的概念
cpu资源分配的先后顺序,就是指进程的优先权(priority)。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
1.2 查看系统进程和优先级
在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容:
我们很容易注意到其中的几个重要信息,有下:
- UID : 代表执行者的身份
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行
- NI :代表这个进程的nice值
关于PRI 和 NI
- PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小
进程的优先级别越高 - 那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值
- PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
- 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
- 所以,调整进程优先级,在Linux下就是调整进程的nice值
- nice其取值范围是-20至19,一共40个级别。
- 需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进
程的优先级变化。 - 可以理解nice值是进程优先级的修正数据
1.3 更改系统的优先级
用top命令更改已存在进程的nice
进入top后按“r”–>输入进程PID–>输入nice值
其他概念:
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高
效完成任务,更合理竞争相关资源,便具有了优先级 - 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
- 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为
并发
2 环境变量
2.1 基本概念
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但
是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。 - 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
2.2 常见环境变量和查看环境变量的方法
-
PATH : 指定命令的搜索路径
-
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
-
SHELL : 当前Shell,它的值通常是/bin/bash。
查看环境变量的方法: -
echo $NAME //NAME为环境变量的名字
将我们的程序所在路径加入环境变量PATH当中: -
export PATH = $PATH:hello 程序所在路径
2.3 和环境变量相关的命令
和环境变量相关的命令:
- echo:显示某个环境变量值
- export:设置一个新的环境变量
- env:显示所有环境变量
- unset:清除环境变量
- set:显示本地定义的shell变量和环境变量
2.4 环境变量的组织方式
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串
通过代码如何获取环境变量==>命令行第三个参数
#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;
}
通过系统调用获取或设置环境变量
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
常用getenv和putenv函数来访问特定的环境变量。
2.5 设置&获取环境变量和环境变量的特性
环境变量通常是具有全局属性的
环境变量通常具有全局属性,可以被子进程继承下去
3 进程地址空间(重点)
我们在学习知识的时候要学会问自己3个问题?1、是什么?2、为什么?3、怎么办?
做到知其然和知其所以然。
3.1 地址空间的引入
之前写过的C语言的博客,谈到过"程序地址空间",虽然这个说法是不对的,但是是为了为现在这个进程地址空间做铺垫的,
之前并没有内核空间。注意:代码区并不是从地址全零开始的。看下面的代码
创建子进程,在子进程中改变了全局变量的值。那么我们来看看运行结果:
我们发现g_val在父子进程的地址值是一样的,但是g_val的值却不一样,是不是很诡异?
问自己个问题:内存中同一个地址的值,被不同的进程读取,有可能是不同的值吗?进程地址空间是内存吗?
结论:
- 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量。
- 但是地址值是一样的,说明了该地址绝对不是物理地址。
- 在Linux地址下,这种地址叫虚拟地址。
- 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户是看不到的,有 OS 统一管理。
所以之前说的"程序地址空间"是不准确的,应该是进程地址空间。"
3.2 什么是地址空间
描述进程所占有资源的一张表,在操作系统内核中就是一个结构体,把地址空间划分成若干区域,来看下Linux内核的源码
这是Linux的进程控制块的结构体定义,在最后一行就是进程的地址空间,我们看下其定义,
其中
unsigned long total_vm, locked_vm, shared_vm, exec_vm;
unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
就划分了进程地址空间中的不同区域。
3.3 为什么要有地址空间
- if没有地址空间,进程读写的是实际的物理内存,那个一个进程去读写另一个的进程的数据,导致野指针问题,并且破坏了进程的独立性。
- 进程数据的地址可能不连续,导致访问难度增大,异常访问的概率变大。
3.4 地址空间怎么使用
- 虚拟地址空间和物理内存之间建立映射关系—页表(虚拟地址和物理地址的映射关系)
- 由用户去访问物理内存变成OS去访问,那么进程一些非法行为将被禁止,地址空间通过虚拟内存将空间连续化处理了,起到保护内存的作用。一旦进程越界访问到只读数据,程序会崩溃,会被OS干掉。
3.5 页表的作用和进程地址空间的管理
3.5.1 页表的作用
1 问大家一个问题:为什么代码是只读的,字符串是只读的,数据是可读可写的?
答:因为页表中有对应空间的访问权限,是可读的,可写的,还是可读可写的,就会限制进程对内存空间的访问。如果进程对只读数据进行写入,就会被OS终止,比如char * str = “chenzhihao”; *str = ‘C’;就会报错
2 因为有页表这样的映射关系存在,所以一个进程是不可能越界去访问到另一个进程的数据的,因为页表中就没有这种映射关系,那么进程内部的越界访问是有可能有的
3.5.2 程序地址空间的管理
1、进程按照地址空间 和 页表中的虚拟地址和实际物理内存地址的映射关系拿到在物理内存中的数据和代码,进程得以运行。
2、问题:每个进程都有一个进程地址空间,那么在系统中有很多的进程及其地址空间,那么要如何管理这些进程地址空间呢?
还是先描述再组织。
先描述:
在组织:
跟进程是强绑定的,在Linux源码里可以看出辣
mm_struct 结合页表完成映射
3.6 小结
什么是进程谈谈你的理解?
答:进程是加载进内存的程序,有进程常见的数据结构(struct task_struct(进程控制块) && struct mm_struct(进程地址空间)) 和 代码数据构成,从冯诺依曼体系机构来理解的话:一个程序从硬盘加载进内存,操作系统(OS),要给这个进程创建task_struct和 mm_struct ,二者用指针关联起来,然后通过页表和物理内存建立映射关系,执行代码和数据。