admin管理员组

文章数量:1597469

文章目录

  • 一.冯·诺依曼体系结构
    • 冯诺依曼结构能干什么?
  • 二.操作系统
    • 概念
    • 结构图(不完整)
    • 为什么要有操作系统?
    • 尝试理解操作系统
    • 管理
    • 结构图(完整)
    • 总结:
  • 三.进程
    • 进程是什么?
    • PCB
      • 为什么要有PCB?
    • Linux中的PCB
    • 进程的task_struct本身内部的属性有哪些?
      • 终止进程
      • 进程创建的代码方式(fork函数)---重操作,轻原理
        • 我们为什么要创建子进程?
        • 多进程
      • task_struct的内容
    • 查看进程(通过/proc查找)
  • 四.进程状态
    • R状态 S状态
    • 状态+中的+是什么意思?
    • T状态 t状态
    • D 状态
    • Z 状态(僵尸进程) 与X 状态
    • 孤儿进程
    • 纯理论:进程的阻塞和挂起,运行
      • 运行状态
      • 阻塞状态
      • 挂起状态
    • 进程切换
  • 五.进程优先级
    • 什么是优先级?
    • 为什么要有优先级?
    • 如何查看优先级呢?
    • top更改进程优先级
    • nice与renice
  • 六.Linux真正是如何调度的

一.冯·诺依曼体系结构



那么问题就来了,为什么要加一个储存器
直接输入设备–>CPU–>输出设备不好吗?

短板在输入设备和输出设备,所以加了个存储器

在计算机里面,离CPU越近,效率越高,造价越贵
存储金字塔

总结:硬件流动角度,数据层面中:(蓝色箭头)
1.CPU不和外设直接打交道,CPU之和内存打交道
2.外设(输入和输出)的数据,不是直接给CPU的,而是先要放入内存中的

冯诺依曼结构能干什么?

引例:“你给你的朋友用QQ发送你好!!”的过程

二.操作系统

概念

开机的操作就是启动操作系统
操作系统是一款软硬件资源管理的软件(比如插个U盘,电脑会显示,可以往U盘里写东西)
操作系统广义的认识:操作系统内核+操作系统的外壳周边程序
狭义:只是操作系统的内核

windows也可以没有图形化界面的

安卓的操作系统属于两层

结构图(不完整)

体系结构的层状划分结构如下
因为操作系统需要硬件的数据,那么每次更新硬件,操作系统获得数据的方式可能跟上一个配对的硬件的方式不一样,那么操作系统就得跟着更新,才能与硬件配对,获取数据
所以操作系统不直接找硬件要相关数据,而是通过驱动层,这样只需要更新驱动就行。(驱动层是独立的,操作系统是核心,更新驱动层肯定比直接更新操作系统方便、安全)

就比如鼠标,在插入电脑的时候,不能直接使用,是因为驱动层还没有下好。右下角会弹窗显示插入或者鼠标亮了(现在插进去响一声),才能使用。

为什么要有操作系统?

举个例子:
学校有椅子、桌子、粉笔、图书馆里的书、体育器材
角色有老师、保安、校长、教导主任、医生
图书馆里的书需要图书馆管理员管理,椅子桌子坏了找谁报修,学生生病找谁
有了角色的担任,管理起来就会变得方便
操作系统就是对软硬件进行管理
为用户提供提供一个良好(稳定的、安全的、高效的)运行环境
良好的反例是指:用电脑玩原神,10分钟一黑屏,5分钟一重启,就是因为某个硬件造成的。

尝试理解操作系统

管理

管理在生活中无非在做两件事:1.决策 2.执行
比如说不想上今天的早8,这就决策。今天去早8了,这叫执行
在学校层面:校长为管理者,学生是被管理者

随着被管理的学生的增多,同学的信息也变得非常多,校长很难管理
但是校长会C语言,写了一个关于学生属性的表

张三的属性就可以用结构体表达出来

学生有很多人,校长要在5W个学生中找到张三同学,为了节省时间,校长就加了给指针,形成了链表
校长对学生的管理工作(日常),变成了对链表的增删查改(计算机语言)

于是校长完成了对计算机的建模工作,也就是对学生的管理工作
校长想开除张三,就变成了删除张三的节点
综上所述,管理学生:先把学生的信息描述起来,然后再组织
结论:任何管理:先描述,再组织
引例:C++先提供描述的能力,STL标准库,有各种容器,提供组织的能力

那么如何管理硬件呢?
比如插入鼠标的一瞬间,进程管理会创建一个关于鼠标的结构体,把鼠标的驱动程序读取到的数据写入该进程(先描述),并以算法(链表等)的形式插入前面的节点后面。(再组织)

不仅仅是进程管理是这样,只要是与管理相关的都大差不差

结构图(完整)


谈谈在操作系统上层的部分
作为用户能否直接访问底层硬件吗?
故事:

隔壁的校长是接触不到张三的,万一张三被拐走了,辅导员找不到人了就是管理人员的失职。要找也是找张三的校长
所以任何访问硬件的操作,都得通过操作系统来做
我们(用户)用C语言中的fopen fclose就经过文件管理,再写进磁盘,绕不过操作系统
同理printf是从显示器上读取数据,scanf是从键盘上读取数据,也是绕不开操作系统的
那用户能否直接访问操作系统呢?
故事:


又比如:现在的你改一改操作系统,然后打起了原神,就因为改了操作系统直接蹦掉了。所以用户是不能直接接触操作系统的
上面的故事映射到计算机里面就是:银行中的柜台就是系统调用接口
这样就可以受管控的使用操作系统

故事进入第二阶段:
作为新时代的青年,准备材料去存钱开户什么的都知道。
但是有一天,来了一个老奶奶要存1000万,但是不会操作

银行经理就是外壳程序,帮助用户开发等操作

开发的时候,直接使用系统调用接口,是不具有跨平台性的,在Linux上能跑,windows可不一定能跑
例子:

总结:

三.进程

操作系统中,进程可以同时存在非常多

如何管理这么多进程呢?
先描述,再组织

进程是什么?

假设我们要运行一个名为a.out的可执行文件(程序 = 代码 + 数据)
根据冯诺依曼结构,任何可执行程序都需要进入内存,交给CPU处理
问题来了,下面图中的内存是进程吗?

可以这样想:如果文件很多,哪个先运行,哪个后运行,什么时候到我运行,堆和栈在哪?
所以:

操作系统想要管理进程:就得先描述,再组织。

PCB

PCB全称process control block
PCB在哪里?
在操作系统里,我们电脑或手机开机就是加载操作系统(操作系统也是软件)

注意的是:操作系统是对PCB的管理,而非对可执行程序管理

为什么要有PCB?

OS要进行管理,就得先描述,在组织

Linux中的PCB


通常进程排队指的是PCB(struct task_struct)排队,而不是数据

因为队列的首部的节点是a.out
所以CPU会把a.out的数据弄到CPU中去运行

总结:调度运行进程,本质就是让进程控制task_struct进行排队
举个通俗点的例子:
同学投简历,大厂会筛选简历,就是对简历(进程)的增删查改,就是对同学(数据)的管理
所以在Linux中进程的概念:进程 = 内核task_struct 结构体 + 程序的代码和数据

进程的task_struct本身内部的属性有哪些?

例子:
Makefile文件

myprocess:myprocess.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f myprocess

myprocess.c文件

#include <stdio.h>
#include <unistd.h>
                                                      
int main()
{
    while(1)
    {
        printf("I am a process!\n");
        sleep(1);
    }
    return 0;
}         


如何查看当前的进程呢?
ps axj
ps:查看当前的进程
a:是all的意思
xj:是显示进程的详细信息
axj的顺序可以乱
一个可执行文件和其进程的名字默认是一样的
查看myprocess文件的进程的命令:

ps axj | grep myprocess

会发现有两个关于myprocess的进程,是因为grep本身的进程也被查到了

显示的每一个信息是什么呢?
显示进程的第一行

ps axj | head -1


结合起来:

一个进程,如何知道自己的pid?
前面讲了,用户无法直接访问操作系统

就需要系统调用接口getpid


myprocess.c的代码

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
                                                      
int main()
{
    pid_t id = getpid();
    while(1)
    {
        printf("I am a process!,pid:%d\n",id);
        sleep(1);
    }
    return 0;
}           

发现进程中的pid与运行起来的pid一样

终止进程

ctrl + C 就是在用户层面终止进程

kill -9 pid可以直接杀掉进程

进程创建的代码方式(fork函数)—重操作,轻原理

发现还有一个getppid,第一个p是parent,第二个p是process,意思是获得当前进程的父进程的id

看一下父进程id是什么
myprocess.c的代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
                                                      
int main()
{
    pid_t id = getpid();
    pid_t parent = getppid();
    while(1)
    {
        printf("I am a process!,pid:%d,ppid:%d\n",id,parent);
        sleep(1);
    }
    return 0;
}           

跑起来会发现

其中18839是什么?
是bash
bash是父进程,是命令行解释器,在Linux权限篇中讲了bash是媒婆,派实习生(子进程)的的故事

创造子进程,因为用户是不能直接去触碰操作系统的,所以用系统调用接口fork。

myprocess.c的代码如下

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
	printf("process is running,only me!\n");
	sleep(3);
	fork();                                                                                     
	printf("hello world\n");
	sleep(5);
	return 0;
}

运行上面的可执行程序时,运行下面的指令,方便观察进程。-v 是去掉grep的进程

while :; do ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep;sleep 1;done

运行结果:

可以发现

我们为什么要创建子进程?

想让子进程和父进程执行不一样的代码
fork会返回两个值

成功后,子进程的 PID 将在父进程中返回,在子进程中返回 0。 失败时,父进程中返回 -1,不创建子进程。
验证一下fork
process的代码如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
                                                   
int main()
{
    printf("process is running,only me!\n");
    sleep(3);
    pid_t id = fork();
    if(id == -1)  //失败
    {
        return 0;
    }
    else if(id == 0)  //子进程
    {
        while(1)
        {
        	printf("id:%d,I am child process,pid:%d,ppid:%d\n",id,getpid(),getppid());
            sleep(1);
        }
    }
    else  //父进程
    {
        while(1)
        {
                printf("id:%d,I am parent process,pid:%d,ppid:%d\n",id,getpid(),getppid());
            	sleep(2);
        }
    }
    return 0;
}

会发现确实有两个返回值,并且子进程的pid将在父进程中返回


于是就出现了两个问题:
1.同一个id,怎么可能即是 == 0,又是 > 0?
2.fork为什么会有两个返回值,并且返回两次
父进程的代码从磁盘中带出来的,子进程只能copy父进程给的代码。
数据上有些独特

解答一下第二个问题

多进程

代码如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

void RunChild()
{
    while(1)
    {
        sleep(1);
        printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
}

int main()
{
    int i = 0;
    const int num = 5;  //创建5个子进程
    for(i = 0;i<num;i++)
    {
        pid_t id = fork();
        if(id == 0)  //子进程进入
        {
            RunChild();
        }
        sleep(1);  //父进程继续往下走
    }

    while(1)
    {
        sleep(1);
        printf("I am parent process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    return 0;
}

可以看到进程逐渐变多

task_struct的内容

标识符:ppid pid 用来和别的进程做区分
状态:任务状态、退出代码、退出信号等
优先级:想对于其它进程的优先级
程序计数器: 程序中即将被执行的下一条指令的地址
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等
其他信息

查看进程(通过/proc查找)

进程的信息也可以通过 /proc 系统文件夹查看

假设要获取进程的PID==1的程序
直接查看/proc/1文件就行
举个例子:

进入proc文件,会发现文件里面的目录都是进程,目录名称就是PID

其中,/proc/PID目录中值得注意的是这两个
第一个表示的是:进程的当前工作路径
第二个表示的是:进程的PCB中会记录自己对应的可执行程序的路径

把这个进程干掉,发现程序还在跑。(这个图片显示不出来,得自己实验)
因为把磁盘中的文件删掉了,但是进程的代码与数据已经在内存里了

然后发现cwd和exe没了

每个进程在启动的时候,会记录自己当前在哪个路径下启动
验证:下面是个简单的文件函数

#include <stdio.h> 

int main()
{
    FILE* fp = fopen("log.txt","w");
    (void)fp;//ignore warning
    return 0;
}

因为是在该目录下运行的进程(a.out),所以默认在该目录下创建log.txt文件

更改进程的所在路径
用到的函数时chdir

#include <stdio.h> 
#include <unistd.h>
#include <sys/types.h>

int main()
{
    chdir("/home/sjl");//更改的路径
    FILE* fp = fopen("log.txt","w");
    (void)fp;//ignore waring
    fclose(fp);
    while(1)//显示出进程在运行
    {
        printf("I am a process,pid:%d\n",getpid());
        sleep(1);
    }
    return 0;
}

进程的路径被更改

会发现在更改的路径下创建log.txt,而运行可执行程序的路径下时没有的

所以一旦把当前进程的工作目录改了,那么新建对应的一个文件,他就在改过进程的当前目录下

四.进程状态

大概就是下面这种形式

下面的状态在kernel源代码里定义:

static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

R状态 S状态

引例:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    while(1)
    {
        printf("I am a process,pid:%d\n",getpid());
    }
    return 0;
}

运行的时候老样子,用以下指令查看进程(可执行程序为a.out)

while :; do ps ajx | head -1 && ps ajx | grep a.out | grep -v grep;sleep 1;done

会发现是S+的状态,是sleep,休眠状态

然后把printf注释掉

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    while(1)
    {
        //printf("I am a process,pid:%d\n",getpid());
    }
    return 0;
}

再次运行代码,会发现是R+状态,R为进程运行状态

原因:打印的时候,是往屏幕上打印的,屏幕是外设,CPU运行比外设快很多,CPU需要等显示屏的资源就绪。所以CPU大部分处于S状态,运气好的时候遇到CPU正在执行printf就能查到在R状态
S(休眠状态)本质分为两种:
第一种:进程在等待“资源”就绪(就是上面的例子)
第二种:可中断睡眠(进程处于睡眠的状态(比如sleep函数),但依旧可以被ctrl + c中断)

状态+中的+是什么意思?

代表的是在前台运行还是后台运行
在进程后面加个&代表在后台运行

运行后你就会发现ctrl + c中止不了
用kill -9 PID终止进程

T状态 t状态

显示出Linux的信号的指令:-l是list

kill -l

其中9号信号是不是很眼熟,杀掉进程

上面的信号底层就是宏,跟C语言中的INT_MAX意义差不多
其中19号信号SIGSTOP就是暂停进程
验证:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    while(1)
    {
        sleep(1);
        printf("I am a process,pid:%d\n",getpid());
    }
    return 0;
}

S状态直接变为T状态

T状态:让进程暂停,等待被进一步唤醒(进程被暂停了,自动变为后台)
18号信号SIGCONT,continue的意思,让进程继续

调试就是进程暂停
遇到断点处,进程就暂停了

D 状态

D:Linux系统比较特有的一种状态(disk是磁盘)
因为配置的原因很难演示出来,所以拿图来讲解:
进程要往磁盘中存1个GB的数据

内存严重不足,操作系统马上被毙掉了,然后看见这个进程悠然自得的SLEEP,一怒之下,把他删了。磁盘找不到了,造成了数据的丢失

这不行啊,好比说内存是银行的系统,被删掉的进程是银行的转账记录,直接被干没了,造成了上百万的损失。
为了防止这类事情的发生,规定凡是进程要进行数据IO等待外设的时候,进程要将自己的状态设成D状态
D状态:不可被杀,被称为深度睡眠,也被称为不可中断睡眠(S就是浅度睡眠,可被杀和中断)
要唤醒D状态有两种方法:
1.进程自己醒来
2.重启,重启不行就断电

Z 状态(僵尸进程) 与X 状态

一个进程退出的时候,他会把自己退出的信息保留在PCB当中,如果没有人读取PCB中退出进程的消息,这个进程就一直不释放,一般会把代码和数据释放掉,但PCB内容与数据结构会一直存在,直到将来对该进程进行等待(释放),如果不等待就一直处于僵尸状态。如果等待了就会转化为X状态,进而将进程所有的资源全部释放掉
引例:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        //child
        int cnt = 5;
        while(cnt)//子进程运行5次后退出
        {
            printf("I am child,cnt:%d,pid:%d\n",cnt,getpid());
            sleep(1);
            cnt--;
        }
    }
    else 
    {
        //parent
        while(1)//父进程一直不退出
        {
            printf("I am a parent,running always!pid:%d\n",getpid());
            sleep(1);
        }
    }
    return 0;
}

子进程运行结束后就是Z状态(僵尸状态)


子进程运行完毕,但需要维持自己的退出信息,在自己的进程task_struct会记录自己的退出信息,未来让父进程来读取

因为X状态释放是一瞬间的,所以查不出来
平时子进程创建出来是要完成任务的,得知道完成任务怎么样(获取子进程退出的信息)
kill不掉僵尸进程的,已经死进程了,杀不了了。

孤儿进程

如果父进程先退出了,那么子进程就是孤儿进程
就是与僵尸进程相反,所以验证的代码也相反:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        //child
        while(1)
        {
            printf("I am child,pid:%d\n",getpid());
            sleep(1);
        }
    }
    else 
    {
        int cnt = 5;
        //parent
        while(cnt)
        {
            printf("I am a parent,cnt:%d,pid:%d\n",cnt,getpid());
            sleep(1);
            cnt--;
        }
    }
    return 0;
}

发现两个S的进程就剩1个S的进程,剩下的那个S进程就是孤儿进程

问题就来了:父进程都没了,谁去回收孤儿进程?
答:孤儿进程一般都会被1号进程(OS本身)进行领养的
孤儿进程为什么要被OS领养?
答:依旧要保证子进程正常被回收
孤儿进程是可以被kill的
以前我们启动的所有进程(运行C语言代码)。怎么从来没有关心过僵尸进程呢?内存泄漏?
答:直接在命令行中启动的进程,他的父进程是bash,bash会自动回收新进程的Z
父进程的一般核心工作都是回收子进程

纯理论:进程的阻塞和挂起,运行

大多数书上的进程状态

运行状态


进程在运行队列中,该进程的状态就是R状态。
R状态不是指直接在CPU上去运行,而是值我已经准备好了,可以随时被调度了
一个进程一旦持有CPU,会一直运行到这个进程结束吗?
不会,就比如里面写个死循环,CPU调度就不会走了,其他进程被卡死了。
现实就是打着原神,微信还能收到信息。
是因为当代的内核基于时间片轮转调度的。
假设时间片设置的2毫秒,2毫秒一到,当前进程就会在CPU上剥离下来,插到队列的尾部,然后下一个进程上前,进入CPU

这样就可以让多个进程以切换的方式进行调度,在一个时间段内同时得以推进代码。就叫做并发
Linux不是这样调度的,上面方法太简单了。只是调度算法的一种。
如果存在两个CPU,就有两个调度队列

根据不同的进程,放到不同队列,也可以放进两个队列
任何时刻,都同时有多个进程在同时运行,我们叫做并行

阻塞状态

问题:我们在写C语言的时候,用过一个接口scanf,如果我们不输入的话,进程处于什么状态?另外scanf在等待什么资源呢?
测试代码如下:

#include <stdio.h>

int main()
{
    int a = 0;
    scanf("%d",&a);
    printf("a = %d\n",a);
    return 0;
}

会发现是S状态,其实D也算阻塞状态,不过无法演示

他在等什么呢?在等键盘资源是否就绪,键盘上面有没有被用户按下的按键,按键数据交给进程
键盘上面没数据,进程是不OK,该进程并没有被运行的(R状态),所以一定不在运行队列中
操作系统要对所有的硬件做管理,那么操作系统如何对硬件做管理?
管理肯定离不开先描述,在组织

一个进程如何等待一个设备呢?
不只是CPU才有等待队列,设备中也有等待队列wait_queue

演示一下scanf等待键盘资源的总过程:


总结:阻塞和运行状态的变化,往往伴随着PCB被连入到不同的队列中
入队列的不是进程的代码和数据,而是进程的task_struct

挂起状态

我们在装系统的时候,会有一个swap分区,大多数情况swap分区是内存的1.5倍2倍,可以自己定义。
当OS中内存吃紧,且有进程处于阻塞状态(暂时不会被调度),那么就把该进程的代码和数据唤出到swap分区中。这样内存就会腾出空间
这种状态叫做阻塞挂起态

如果该设备又等待就绪了,就会把进程的代码和数据唤入到内存

这样可以更合理的使用内存资源
还有很多挂起,就绪挂起等。本质都是一样的。
频繁的唤入唤出会导致效率问题。牺牲效率换取空间
不建议把swap分区放大太,因为操作系统会过渡依赖swap分区,一压力大就唤出。要逼着操作系统合理的分配内存
swap分区的位置

ls /proc/swaps

进程切换

CPU内部的所有寄存器中的临时数据,叫做进程的上下文

寄存器本身是硬件,具有数据的存储能力,CPU的寄存器硬件只有一套
但CPU内部的数据可以有多套,有几个进程,就有几套和该进程对应的上下文数据

五.进程优先级

什么是优先级?

指定进程获取某种资源(比如CPU)的先后顺序
进程控制模块task_struct中的内部字段有一个或多个整型(比如int prio =)的优先级数字
Linux中优先级数字越小,优先级越高

为什么要有优先级?

进程访问的资源(CPU)是有限的
进程比硬件资源多,显示屏就一个,网页有很多
操作系统关于调度和优先级的原则:分时操作系统,保证了基本公平
如果进程因长时间不被调度,就造成了饥饿问题(比如学生打饭,教导主任差一下队,校长插一下队)

如何查看优先级呢?

引例:

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

int main()
{
	while(1)
    {
    	printf("I am a process,pid:%d\n",getpid());
    	sleep(1);
    }
    return 0;
}

查看当前终端的进程

ps -l

想查看全部终端的进程

ps -al

PRI:进程优先级
NI:进程优先级的修正数据(nice值),新的优先级 = 优先级 + nice

top更改进程优先级

top

先输入top

然后输入r

输入需要调整优先级的PID

输入100,然后按回车

会发现优先级是99,不是80+100

这是因为nice并不能任意调整,是有范围的(-20~19)
如果能随便调,把优先级调很高,就可能会一直运行一个进程,影响了调度的平衡
第二次改的时候不让改了,因为一个进程的优先级是不能频繁改的

这里切root用户更改,nice的值为6

发现不是99+6,而是80+6
因为每次调整优先级,都是从80开始的

nice与renice

这两也可以更改优先级
nice命令格式:nice -n [NI值] [命令]加不加n都行

renice命令格式:renice [NI值] [PID]

六.Linux真正是如何调度的

Linux系统中每一个CPU都有一个运行队列
运行队列里面有个queue[140],只看[100,139],对应的是优先级的[60,99]
如果有优先级为60的进程进入,就会连接到queue[100]的后面,运行就是从上向下运行

但是假设前面没有数据的位置有很多,遍历起来很费时间。就有了bitmap[5]
一次可以检测32个比特位:如果为0,直接跳过;不为0,说明其中有数据。
大大减少了遍历的时间。这种算法叫做大O(1)算法

nr_active表示有多少进程,这就不谈了
新的问题来了:如果在运行的时候,加入新进程怎么办?就比如运行到78了,插入个优先级为60的进程
首先我们要知道上面所说的是一个结构体,且这个结构体是存在类似sturct q array[2]中的

active与expired分别存的是array[0]与array[1]的地址
假设运行第一个队列的时候:第一个队列只出不进,第二个队列只进不出。
如果新增一个进程,插入到第二个队列中。
第一个队列的进程时间片到了,也是直接连接到第二个队列中

当第一个队列没进程的时候,将两个指针(active,expired)的内容做交换

本文标签: 进程状态优先级僵尸孤儿