Linux修炼之路之进程优先级,并行,环境变量

目录

一:进程优先级 

二:进程的切换与调度

三:环境变量

接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧 

一:进程优先级 

优先级就是获得某种资源的先后顺序,本质是因为目标资源比较少,而争抢的对象却特别多,而这里的优先级其实是特定的一些int变量来表示的

1.基本概念

当使用ps -al指令时,就会显示用户所启动的所有进程信息

这里的PRI :代表这个进程可被执行的优先级,其值越小越早被执行,在Linux中优先级是一个整形的数字,是task_struct结构体当中的一个字段成员

NI :代表这个进程的nice值 ,代表进程优先级的修正数据           UID : 代表执行者的身份

cpu资源分配的先后顺序,就是指进程的优先权(priority)。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能

2.PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值nice其取值范围是-20至19,一共40个级别,对于这里的PRI(old)=80

3.用top命令更改已存在进程的nice:

root用户下,使用top指令->r->输入修改进程的PID->输入调整值

4.物理上硬件资源是缺少的,而进程有很多,因此进程在系统的资源角度是具有竞争性的。为了良性竞争,即较均衡的让每个进程享受到系统资源,必须想办法组织起来让他们排队,所以所有系统当中队列的问题本质上都是因为进程要访问的目标资源不足的问题,是竞争性所带来的结果

5.如果调度器允许用户恶意的更改优先级,而导致其他进程长时间处于排队状态的话,就会导致其他进程的饥饿问题

6.对于进程根据优先级来展开的调度

是维护了两个队列,即指针数组,一个是runqueue的运行队列,一个是waitqueue的等待队列,当每个进程在一段时间内在cpu上执行后,就会去等待队列对应位置去排队,当runqueue队列中的所有进程执行完后,就交换runqueue和waitqueue的指针,继续执行进程

二:进程的切换与调度

1.独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰

2.并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行

当有两个物理cpu时,在操作系统层面上就得维护两个调度队列,两个物理cpu各自调度各自的,所以就直接决定了在系统当中的任意一个时刻一定会同时存在两个真正并行运行的进程

3.并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

这里我们先铺垫一下

为什么函数返回值能被外部拿到呢?---通过CPU寄存器提前保存下来

进程在运行的时候会产生很多的临时数据,都是保存在CPU的寄存器中,在进程调度层面关键的寄存器有pc指针,ir寄存器等,寄存器有很多,但我们统称为一套寄存器

系统如何得知我们当前进程执行到哪里了--通过程序计数器即pc指针,eip寄存器保存了当前进程正在执行指令的下一条指令的地址,而ir是指令寄存器,保存的是正在执行的指令

所以CPU寄存器内保存的是进程的相关临时数据(可被访问或修改)---进程的上下文

对于每个进程当被cpu调度执行时,都是有一个时间片的概念的,当时间片的时间一到,进程将会被强制从CPU上扒下来,再将下个进程继续调度执行,因此在Linux中进程是基于时间片进行轮转调度的

当进程在从CPU上离开的时候,要将自己的相关临时数据(上下文数据)保存好带走,为下次被调度执行做准备,防止下次调度执行时不知道进程执行到哪行代码了(保存在任务状态段的结构tss中)

进程在被切换的时候,要做保存上下文,恢复上下文的操作

每个进程都有自己对应的上下文数据,但寄存器只有一套,因此这些进程共享这一套寄存器

Linux的真实调度算法

再最初的进程调度是基于队列的FIFO的调度队列,然而这种方式在进行进程调度时会出现诸多问题,因此接下来介绍关于Linux的真实调度算法

对于queue这个结构体,其内部的成员为

 在其中task_struct* queue[140]是一个指针数组,在这140个位置中,前100个不用考虑是给固定进程的,而后40个位置是用来存放我们所启用的进程的,相同优先级的进程就会挂接在同一个位置,这就类似于哈希桶,因为真实进程的优先级是[60,99]40个数字,对应上面的这40个位置,在计算时就是pri-60+100

在上面的调度算法中,有两个指针struct queue* active和struct queue* expired,在刚开始时active=&array[0], expired=&array[1],这样CPU在调度时,就会调度执行active所指向的队列中的进程,在遍历这个队列时就会使用到bitmap[5]这个位图结构,其对应160个bit位,刚好可以覆盖140个位置,然而在队列中一个位置的往后遍历的话,效率照样不高,这样的话采用的是

for(int i=0;i<5;i++)
{
    if(bitmap[i]==0) continue;//一次就可以检测32个位置
    else
    {
       //从32个位置中确定是哪一个位置
    }
}

使用x&=(x-1) 来确定32个位置中有多少个位置有进程,在具体确定是哪一个

当调度器要均衡的调度进程,但当进程优先级很高时,当时间片结束切换时,若这个进程继续放入原先调度的队列的话,接下来就会接着调度它,其它优先级较低的进程得不到调度执行就会导致饥饿问题,且有新进程创建出来开始调度时,若插入在正在调度的队列的话,若优先级较高的话,就会导致调度不均衡,这样的话,就将新创建的进程,时间片结束的进程这些插入expired队列,这样当active队列的所有进程执行完时,就执行swap(&active,&expired),接着CPU从active队列中选择进程来执行,active队列都是一个存在竞争的情况,active队列中的进程只会越来越少

补充知识

1.所有的进程都要双链表连接

2.进程可以在调度队列中,阻塞队列等

那么一个链表节点是如何同时在这些队列中的

然而在进程的链表节点的结构中,只有连接字段,没有属性字段

 这样的话,知道其中一个字段的话,如何访问到结构体中其它的属性字段呢

假设结构体为

struct  A
{
    int a;
    char b;
    double c;
    float d;
}

 当知道c的地址时,要访问其它成员变量的话就要(struct A*)(&c-偏移量)->a/b/d,这样的话就要计算偏移量,将0地址进行强转,&((struct A*)0->c)这就是c的偏移量,这样就可以设计为一个宏函数进行计算  #define who(type,x) (type*)(&x-&((type*)0->x))

在调用时,可以who(struct task_struct,link)->pri/pid

三:环境变量

在main函数中是有参数的,其中argc是参数的个数,argv是参数的清单

同一个程序,可以根据命令行参数,根据选项的不同,表现出不同的功能

在命令行中输入的是字符串,首先是会被shell拿到的,shell按照空格打散,形成一张argv表和元素个数argc,在命令行中启动的程序,其父进程是shell,所以对于父子关系,对于数据(只读)子进程也是可以看见的

1.环境变量是系统提供的一组key=value形式的变量,不同的环境变量有不同的用户,通常具有全局属性

2.在Linux系统中对于指令的搜索会提供一个环境变量,即PATH(指令的搜索路径)

所以对于在Linux中的指令不用带./就可以直接运行是因为指令的路径是在PATH环境变量当中的,运行时会直接去PATH中去查找

而我们自己写的程序的路径是不在PATH环境变量当中的,因此直接去运行时,在PATH环境变量中就会找不到

因此当直接执行指令时,系统是如何得知这些指令在哪里的?

因为操作系统在执行指令的时候,shell会在所维护的PATH环境变量的路径中一个一个路径的去查找,如果找到了就停下来,去执行该路径下的指令程序,所以在执行时不用带路径,如果指令不在对应的PATH的路径下,就会报出找不到的信息

3.在PATH系统环境变量中添加路径:PATH=$PATH:对应路径

4.在首次登陆时,默认所处的目录是自己的家目录或工作目录,这是因为当我们登陆时,shell会识别账户,填充$HOME这样的环境变量,当你登陆的时候,默认就是相当于cd $HOME,shell就把你放在默认的路径下,即当我们登录时,OS会创建bash进程,bash进程会读取环境变量所对应的配置文件.profile等这些文件,再运行这些配置文件填充对应的PATH,HOME等环境变量,又bash也是一个进程,会将HOME环境变量设置到进程对应的proc文件中的cwd中,通过chdir方法进行设置。接着命令行上执行的命令都是bash的子进程,而子进程在刚创建时是没有任何属性填充的,是继承自父进程的,因此会继承bash的当前工作路径

正是因为有了这个环境变量的存在,当我们登陆时,xshell会给我们分配bash命令行解释器,这其实也是一个进程,在执行时就会cd $HOME,把自己变为/root这样的路径

5.shell当前的可执行程序:$SHELL

6.env可以查到当前进程以及bash进程从系统里继承下来的所有的环境变量

7.环境变量可以通过系统调用接口获取: getenv(环境变量名); 

8.指令带选项,本质上是同一个指令,但是因为不同的选项可以有不同的功能点去查看不同的内容

9.为了支持命令行参数,就必须在main函数中带参  int main(int argc,char* argv[],char*env[])

在命令行上解释的时候,输入的是字符串,bash会将字符串打散,以空格为间隔符,第一个为执行的命令,其余的为选项 

获取自己进程的所有系统环境变量 env[]

当程序变为进程启动时,一定有人调用main函数,将命令行参数表,环境变量表传给main函数,自身的进程没有环境变量,但启动起来变为进程后又具有了环境变量

这是因为在shell中./运行可执行程序后,会变为bash进程的子进程,bash本身在启动时,会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程的环境变量,因此环境变量可以被bash之后的进程全部看到,即环境变量具有全局属性

因此环境变量被继承的两种方式:通过main函数传参,通过环境变量直接继承

10.常规命令:通过创建子进程完成的

内建命令:bash不创建子进程,而是由自己亲自执行的,类似于bash调用了自己写的或系统调用的函数.因此命令行上所启动的指令不一定全都要创建子进程

本地变量(在命令行中直接定义的为变量):只在本bash内部有效,不会被继承

11.增加环境变量:export   如:export b=100;

  取消环境变量:unset

  查询系统中所有的变量:set

12.环境变量的意义

系统的配置信息,尤其是具有指导性的配置信息,它是系统配置起效的一种表现

进程具有独立性。环境变量可以用来进程间传递数据(只读数据)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值