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

Posted TT在长大

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)相关的知识,希望对你有一定的参考价值。

本文基于CentOS,深入解释进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列。

目录

一、进程优先级

1.基本概念

2.查看系统进程

3. PRI && NI 

4.其他概念

二、环境变量

1. 基本概念

2. 常见环境变量

3. 查看环境变量方法

4. 和环境变量相关的命令

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命令则会类似输出以下几个内容:

列表中:

        UID : 代表执行者的身份
        PID : 代表这个进程的代号
        PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
        PRI :代表这个进程可被执行的优先级,其值越小越早被执行
        NI :代表这个进程的nice值

3. PRI && NI 

PRI即进程的优先级,或者通俗点说就是程序被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. 环境变量的组织方式

每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\\0’结尾的环境字符串.

6. 通过代码如何获取环境变量

命令行第三个参数

#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;
}

 通过第三方变量environ获取

#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;
}

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

7. 通过系统调用获取或设置环境变量

        常用getenv和putenv函数来访问特定的环境变量。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("%s\\n", getenv("PATH"));

    return 0;
}

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

        环境变量通常具有全局属性,可以被子进程继承下去

三、程序地址空间

1.程序地址空间

         C语言的空间布局图

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int g_val = 0;

int main()
{
    pid_t id = fork();

    if(id < 0){

        perror("fork");

        return 0;

    }

    else if(id == 0){ //child

        printf("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;
}

输出:

 输出出来的变量值和地址是一模一样的。可是将代码稍加改动:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

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;
}

输出:

父子进程,输出地址是一致的,但是变量内容不一样 。

这是因为:
我们在用C/C++所看到的地址,全部都是虚拟地址!物理地址,由OS统一管理。
OS必须负责将虚拟地址转化成物理地址。

四、进程地址空间

进程的地址空间由允许进程使用的全部线性地址组成。每个进程所看到的线性地址集合不同,一个进程的地址和另一个进程的地址间没有什么关系。内核可以动态修改进程的地址空间。

分页式内存管理可以将一段程序加载到不连续的物理空间上,但是从虚拟地址空间来看依旧是连续的,这样就可以解决内存使用率低的问题

序A和程序B分别被映射到了两块不同的物理内存,它们之间没有任何重叠,如果程序A访问的虚拟地址超出了 0X00A00000 这个范围,系统就会判断这是一个非法的访问,拒绝这个请求,并将这个错误报告给用户,通常的做法就是强制关闭程序。程序可以使用固定的内存地址。虚拟内存无论被映射到物理内存的哪一个区域,对于程序员来说都是透明的,我们不需要关心物理地址的变化,只需要按照从地址 0X00000000 到 0X00A00000 来编写程序、放置变量即可,程序不再需要重定位。

现代计算机都使用分页(Paging)的方式对虚拟地址空间和物理地址空间进行分割和映射,以减小换入换出的粒度,提高程序运行效率

分页与虚拟地址空间

五、Linux2.6内核进程调度队列

1.进程队列的数据结构

Linux2.6内核中进程队列的数据结构:

一个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指针的内容,就相当于有具有了一批新的活动进程。

以上是关于[OS-Linux]详解Linux的进程2(进程的优先级,环境变量,程序地址空间,进程地址空间,进程调度队列)的主要内容,如果未能解决你的问题,请参考以下文章

[OS-Linux]详解Linux的进程间通信1------管道

[OS-Linux]详解Linux的进程间通信1------管道

[OS-Linux]详解Linux的进程间通信1------管道

[OS-Linux]详解Linux的进程间通信2------system V共享内存(Shared Memory)

[OS-Linux]详解Linux的进程间通信2------system V共享内存(Shared Memory)

[OS-Linux]详解Linux的进程间通信2------system V共享内存(Shared Memory)