LINUX进程调度算法的分析

合集下载

理解Linux的进程调度和优先级

理解Linux的进程调度和优先级

理解Linux的进程调度和优先级Linux是一种广泛使用的开源操作系统,它采用了多任务处理的方式,能够同时运行多个进程。

进程调度是Linux操作系统中非常重要的一部分,它决定了进程之间如何共享CPU资源,使得操作系统能够高效地运行。

进程调度是操作系统的核心功能之一,它负责按照一定的调度算法从就绪队列中选择一个进程并将其分配给CPU执行。

Linux操作系统采用了时间片轮转法作为默认的调度算法,即每个进程被分配一个时间片,当时间片用完之后,如果进程还未执行完毕,就会被挂起,将执行机会分配给下一个就绪进程。

这样可以保证多个进程之间的平等性,防止某个进程长时间占用CPU资源。

除了时间片轮转法,Linux操作系统还支持其他的调度算法,如优先级调度、实时调度等。

优先级调度是一种根据进程的优先级来进行调度的方法,优先级高的进程会优先获得CPU执行权。

在Linux中,每个进程都有一个静态优先级和一个动态优先级,静态优先级由进程创建时指定,而动态优先级会根据进程执行情况进行动态调整。

实时调度是一种对实时任务进行优先调度的方法,可以保证实时任务的响应时间。

Linux操作系统中,进程的优先级范围是-20到19,数值越小表示优先级越高。

根据进程的优先级,Linux操作系统按照先运行高优先级进程的原则来进行调度。

当有多个具有相同优先级的进程时,Linux会采用时间片轮转法来进行调度。

通过灵活调整进程的优先级,可以合理分配CPU资源,提高系统的整体性能和响应速度。

除了优先级调度算法外,Linux操作系统还支持实时调度算法。

实时任务对响应时间要求非常高,需要保证任务在规定的时间内完成。

为了实现实时调度,Linux操作系统引入了实时进程和实时调度策略。

实时进程是一类对时间敏感的进程,它们可以获得更高的执行优先级和更快的响应时间。

实时进程的调度策略有FIFO(先进先出)调度、循环调度等。

理解Linux的进程调度和优先级对于系统管理员和开发人员来说非常重要。

linux的任务调度机制

linux的任务调度机制

linux的任务调度机制摘要:1.Linux任务调度机制简介2.Linux任务调度器的工作原理3.调度策略和队列4.进程优先级和调度算法5.总结正文:Linux任务调度机制是操作系统中负责分配处理器时间片给各个进程的核心组件。

它依据特定的策略和算法,确保公平、高效地管理进程的执行。

本文将详细介绍Linux任务调度机制的各个方面。

1.Linux任务调度机制简介Linux采用基于优先级的抢占式调度算法,以确保处理器资源得到充分利用。

调度器通过周期性地在就绪队列中选择一个或多个进程,将它们分配给处理器执行。

调度器主要依据进程的优先级和当前的负载情况来决定哪个进程获得处理器资源。

2.Linux任务调度器的工作原理Linux任务调度器的核心组件是调度实体(scheduler entity),它包括进程队列、调度策略和调度算法。

调度实体根据系统的当前状态,按照策略和算法来选择下一个要执行的进程。

调度实体的工作过程分为以下几个步骤:- 进程创建:当一个新进程被创建时,调度器会为其分配一个初始优先级,并将其加入就绪队列。

- 进程执行:调度器从就绪队列中选择一个或多个进程,将它们分配给处理器执行。

执行过程中,进程可能因时间片用完或被阻塞而放弃处理器资源。

- 进程更新:调度器周期性地更新进程的优先级和状态,以反映其当前的执行情况。

- 进程退出:当进程完成执行或被终止时,调度器会将其从进程队列中移除。

3.调度策略和队列Linux调度器支持多种调度策略,如FIFO(先进先出)、SJF(短作业优先)和RR(时间片轮转)。

调度策略决定了进程在队列中的排列顺序,从而影响了调度器选择下一个进程的依据。

Linux中有两个主要的进程队列:就绪队列和运行队列。

就绪队列包含了所有等待处理器资源的进程,而运行队列则存放了当前正在执行的进程。

调度器会根据策略从就绪队列中选择一个或多个进程,将其加入运行队列。

4.进程优先级和调度算法Linux中的进程优先级是一个0-139的整数,优先级数值越低,进程获得处理器资源的机会越高。

Linux编程:模拟进程调度算法

Linux编程:模拟进程调度算法

Linux编程:模拟进程调度算法稍稍有点操作系统基础的朋友应该知道进程的调度算法,在这⾥Koala还是给⼤家略微介绍⼀下接下来将要⽤到的⼏种算法:1. 先来先服务(FCFS)采⽤FCFS调度,先请求CPU的进程会先分配到CPU。

使⽤FCFS调度的等待时间通常较长,CPU利⽤率也会较低2. 最短作业优先调度(SJF)采⽤SJF调度会选择具有最短CPU运⾏时间的进程分配CPU使⽤权。

如果两个进程的CPU区间相同,则按照FCFS来进⾏选择。

SJF调度可以证明是最佳的,它降低了平均等待时间。

3. 轮转法调度(RR)RR调度将CPU时间分为较⼩的时间⽚,调度程序循环就绪队列。

为每⼀个进程分配不超过⼀个时间⽚的CPU。

RR调度专门⽤于分时系统。

4. 优先级调度每⼀个进程都有⼀个优先级与其关联,具有最⾼优先级的进程会分配到CPU。

优先级调度的⼀个主要问题是优先级较低的进程会产⽣饥饿现象。

整个编程思路按照如下进⾏:创建主线程,主线程创建⼦线程,⼦线程有⼀个虚拟PCB主线程创建20个⼦线程,分别实现FCFS调度、SJF调度、RR调度、优先级调度,并且计算每个调度的平均等待时间。

对于每个⼦线程,在其运⾏期间,输出其占⽤的时间标号(例如,第3个线程占⽤了第10秒的CPU时间,输出为:“Thread3:10”)。

下⾯是整个的代码(仅供参考):#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<unistd.h>#include<pthread.h>#include<time.h>#include<iostream>#define Thread_Num 20using namespace std;pthread_mutex_t Device_mutex ;//Virtual PCB of threadsstruct VirtualPCB{int tid;int priority;int waittime;int runtime;int arrivetime;int visited;int tempruntime;public:int gettid(){return tid;}int getwaittime(){return waittime;}int getpriority(){return priority;}int getruntime(){return runtime;}int getarrivetime(){return arrivetime;}void setvisit(int a){visited=a;}int getvisit(){return visited;}int gettempruntime(){return tempruntime;}void setwaittime(int n){waittime = n;}void settempruntime(int n){tempruntime = tempruntime - n;}}TCB[Thread_Num];//Function to initial virtual PCBvoid t_init(){int n;srand(time(NULL));for(n =0;n<Thread_Num;n++){TCB[n].tid = n + 1;//⽤线程创建序号作为虚拟进程id//⽤随机数随机产⽣虚拟PCB的值TCB[n].priority = 1 + rand()%19;TCB[n].runtime = 1 + rand()%19;TCB[n].arrivetime = 0;//模拟时,默认进程按创建顺序依次在0时刻到达TCB[n].waittime = 0;TCB[n].visited =0;TCB[n].tempruntime = TCB[n].runtime;}}//Threads run functionvoid *t_print(void *arg){int n = *(int *)arg;//get argumentwhile(1){pthread_mutex_lock(&Device_mutex);printf("Thread_%-2d: ",n);printf("tid:%-2d priority:%-2d runtime:%-2d \n",TCB[n-1].gettid(),TCB[n-1].priority,TCB[n-1].runtime); pthread_mutex_unlock(&Device_mutex);sleep(1);break;}//printf("Error %d\n",n);pthread_exit(0);}//First come first service schedule functionvoid FCFS(){cout<<"-----------FCFS:"<<endl;int i,j;int start = 0;float waittime = 0;float avwait = 0;for(i=0;i<Thread_Num/2;i++){for(j=0;j<Thread_Num;j++){if(TCB[j].getarrivetime()==i && TCB[j].getvisit()==0){printf("Thread: %-2d Start: %-3d Runtime: %-2d\n",TCB[j].gettid(),start,TCB[j].getruntime()); waittime = waittime + (float)start;start = start + TCB[j].getruntime();TCB[j].setvisit(1);}}}avwait = waittime / (float)Thread_Num;printf("Total waitting time : %f\n",waittime);printf("Average waitting time : %f\n",avwait);}//Shortest job first schedule functionvoid SJF(){for(int k=0 ;k<Thread_Num;k++){TCB[k].setvisit(0);}cout<<"-------------SJF:"<<endl;int i,j;int start = 0;float waittime = 0;float avwait = 0;for(i=1;i<Thread_Num;i++){for(j=0;j<Thread_Num;j++){if(TCB[j].getruntime()==i && TCB[j].getvisit()==0){printf("Thread: %-2d Start: %-3d Runtime: %-2d\n",TCB[j].gettid(),start,TCB[j].getruntime()); waittime = waittime + (float)start;start = start + TCB[j].getruntime();TCB[j].setvisit(1);}}}avwait = waittime / (float)Thread_Num;printf("Total waitting time : %f\n",waittime);printf("Average waitting time : %f\n",avwait);}//Round R schedule functionvoid RR(int r){cout<<"--------------RR:"<<endl;int start = 0;float waittime = 0;float avwait = 0;for(int i=0;i<Thread_Num;i++){int totaltime = totaltime + TCB[i].getruntime();TCB[i].setvisit(0);}for(int j=0;j<20*Thread_Num;j=j+r){int k = (j%(20*r))/r;if(TCB[k].gettempruntime() > 0){int tepruntime = r;if(TCB[k].gettempruntime()-r<=0){tepruntime = TCB[k].gettempruntime();TCB[k].setwaittime(start + tepruntime - TCB[k].getruntime());}printf("Thread: %-2d Start: %-3d Runtime:%-2d \n",TCB[k].gettid(), start,tepruntime);start = start + tepruntime;TCB[k].settempruntime(r) ;}}for(int m=0;m<Thread_Num;m++){waittime += TCB[m].getwaittime();//printf("TCB[%d].getwaittime():%d\n",m+1,TCB[m].getwaittime());}avwait = waittime / (float)Thread_Num;printf("Total waitting time : %f\n",waittime);printf("Average waitting time : %f\n",avwait);}//Priority schedule functionvoid Priority(){for(int k=0 ;k<Thread_Num;k++){TCB[k].setvisit(0);}cout<<"-----------Priority:"<<endl;int i,j;int start = 0;float waittime = 0;float avwait = 0;for(i=1;i<Thread_Num;i++){for(j=0;j<Thread_Num;j++){if(TCB[j].getpriority()==i && TCB[j].getvisit()==0){printf("Thread: %-2d Start: %-3d Runtime: %-2d\n",TCB[j].gettid(),start,TCB[j].getruntime()); waittime = waittime + (float)start;start = start + TCB[j].getruntime();TCB[j].setvisit(1);}}}avwait = waittime / (float)Thread_Num;printf("Total waitting time : %f\n",waittime);printf("Average waitting time : %f\n",avwait);}//Main thread execute function to create 20 children threadsvoid *Children(void*){int ret[Thread_Num];t_init();pthread_t tid[Thread_Num];pthread_mutex_init(&Device_mutex,NULL);int i,j;for(i=0;i<Thread_Num;i++){int k =i+1;ret[i] = pthread_create(&tid[i],NULL,&t_print, &k);if(ret[i] == 0) {sleep(1);}else{printf("Thread_%-2d failed!\n",i+1);}}for(j=0;j<Thread_Num;j++)pthread_join (tid[i], NULL);pthread_mutex_destroy(&Device_mutex);pthread_exit(0);}int main(){int ret1;pthread_t tid1;//Declare main threadret1 = pthread_create(&tid1,NULL,&Children,NULL);//Create main threadif(ret1 == 0){printf("Main Thread ok!\n");sleep(20);}else{printf("Thread failed!\n");}FCFS();SJF();cout<<"Please enter RR time:\n";//Request RR timeint rr;scanf("%d",&rr);RR(rr);Priority();return 0;}OK!此代码的运⾏结果如下(部分):第⼀张图打印了⼀下虚拟PCB的部分内容:第⼆张图⽚打印了FCFS调度算法运⾏结果:第三张图⽚打印了SJF调度算法运⾏结果:第四张图⽚打印了RR调度算法运⾏结果(部分):第五张图⽚打印了Priority调度算法运⾏结果:注意看每张图下⾯的两⾏数据,分别是不同算法对应的总的进程的等待时间以及平均等待时间的⼤⼩,印证了SJF算法通常是最少平均等待时间的调度算法最后希望⼤家能够积极提建议,指出纰漏!。

操作系统实验报告——调度算法

操作系统实验报告——调度算法

操作系统实验报告——调度算法1. 实验目的本实验旨在探究操作系统中常用的调度算法,通过编写代码模拟不同的调度算法,了解它们的特点和应用场景。

2. 实验环境本次实验使用的操作系统环境为Linux,并采用C语言进行编码。

3. 实验内容3.1 调度算法1:先来先服务(FCFS)FCFS调度算法是一种简单且常见的调度算法。

该算法按照进程到达的先后顺序进行调度。

在本实验中,我们使用C语言编写代码模拟FCFS算法的调度过程,并记录每个进程的等待时间、周转时间和响应时间。

3.2 调度算法2:最短作业优先(SJF)SJF调度算法是一种非抢占式的调度算法,根据进程的执行时间来选择下一个要执行的进程。

在本实验中,我们使用C语言编写代码模拟SJF算法的调度过程,并计算每个进程的等待时间、周转时间和响应时间。

3.3 调度算法3:轮转调度(Round Robin)Round Robin调度算法是一种经典的时间片轮转算法,每个进程在给定的时间片内依次执行一定数量的时间。

如果进程的执行时间超过时间片,进程将被暂时挂起,等待下一次轮转。

在本实验中,我们使用C语言编写代码模拟Round Robin算法的调度过程,并计算每个进程的等待时间、周转时间和响应时间。

4. 实验结果分析通过对不同调度算法的模拟实验结果进行分析,可以得出以下结论:- FCFS算法适用于任务到达的先后顺序不重要的场景,但对于执行时间较长的进程可能会导致下一个进程需要等待较久。

- SJF算法适用于任务的执行时间差异较大的场景,能够提高整体执行效率。

- Round Robin算法适用于时间片相对较小的情况,能够公平地为每个进程提供执行时间。

5. 实验总结本次实验通过模拟不同调度算法的实际执行过程,深入了解了各种调度算法的原理、特点和适用场景。

通过对实验结果的分析,我们可以更好地选择合适的调度算法来满足实际应用的需求。

在后续的学习中,我们将进一步探索更多操作系统相关的实验和算法。

基于Linux系统中进程调度分析

基于Linux系统中进程调度分析

二 、 Ln x 程 调 度 时机 iu 进
Ln x 进程 调 度 时 机 与 现 代 操 作 系 统 中 的调 度 时机 基 本 一 策 略 中 ,每 个 可执 行实 时进 程 轮流 执行 一个 时间片 ,而 先进 先 出策 iu 的 略每 个 进程 按各 自在 运行 队列 中的顺序 执 行且 顺序 不能 变化 。 致 。为 了 判 断 是 否 可 以 执 行 内核 的 进 程 调 度 程 序 来 调 度 进 程 , Ln x i 中设 置 了进 程 调度 标 志n e — e c e u e d r s h d,当标 志 为 1 ,可 执 时 在 Ln x ,进 程调 度策 略共 定义 了3 : i 中 u 种
商 业 科 技
基 于Ln x 统 中进程 调度分析 iu 系
一院 浩 重庆大学软件学院
[ 要 ] Ln x - 个 多用户 多任 务 的操 作 系统 ,Ln x 实现 了对 多个进 程公 平 、高 效的调 度 ,并 不是 采 用单一 的调 度 策略 ,而是 摘 iu .  ̄- i u
几 种调度 策略 有机 地 综合 应 用。

Ln x 进 程 调 度 i 的 u
状 态变 为不 可 打断 睡眠 ,如果 调 用itr pil s e — n) ner t e l p o (,则其 u b — e
L u 支持 多 进程 。进 程控 制块 P B Po e sC nrl l k是 状 态 变为可 打 断睡 眠,l p o ( i erpil s e — n) 调用 ix n C (rc s o t o ) oB c Se — n) n r t e l p o ( e 或 t u b— e 将 系统 中最 为 重 要 的 数据 结 构 之 一 ,用 来 存 放 进程 所 必 需 的各 种 信 s h d l ) ceu ( e 函数 把 睡眠进 程释 放

linux下常见的调度策略及调度原理

linux下常见的调度策略及调度原理

linux下常见的调度策略及调度原理Linux是一种开源的操作系统,广泛应用于服务器和嵌入式设备中。

在Linux系统中,进程调度策略是操作系统的核心组成部分之一,它决定了进程的执行顺序和时间分配。

本文将介绍Linux下常见的调度策略及其调度原理。

在Linux系统中,常见的进程调度策略包括先来先服务(FCFS)、最短作业优先(SJF)、时间片轮转(RR)和优先级调度(Priority Scheduling)等。

先来先服务(FCFS)是一种简单而直观的调度策略,它按照进程到达的先后顺序进行调度。

即当一个进程到达系统时,它将被放入就绪队列的末尾,并等待CPU的分配。

当CPU空闲时,系统将选择就绪队列中的第一个进程分配给CPU执行。

这种调度策略的优点是公平性强,但缺点是无法处理长作业和短作业的差异,容易产生"饥饿"现象。

最短作业优先(SJF)调度策略是根据进程的执行时间来决定优先级的调度策略。

即系统会选择执行时间最短的进程先执行,以减少平均等待时间。

这种调度策略的优点是能够最大程度地减少平均等待时间,但缺点是可能会出现长作业等待时间过长的问题。

时间片轮转(RR)是一种基于时间片的调度策略,每个进程被分配一个固定长度的时间片。

当一个进程的时间片用完时,系统将把CPU分配给下一个进程。

这种调度策略的优点是能够有效地平衡进程之间的响应时间,但缺点是可能会导致频繁的上下文切换。

优先级调度(Priority Scheduling)是一种根据进程优先级来决定调度顺序的策略。

每个进程被分配一个优先级,优先级越高的进程越容易被调度执行。

这种调度策略的优点是能够根据不同进程的需求进行灵活调度,但缺点是可能会导致低优先级进程的"饥饿"问题。

在Linux系统中,调度算法的实现是通过内核的进程调度器来完成的。

内核中的调度器会根据不同的调度策略来选择下一个要执行的进程,并将其上下文切换到CPU中执行。

进程的调度实验报告(3篇)

进程的调度实验报告(3篇)

第1篇一、实验目的通过本次实验,加深对操作系统进程调度原理的理解,掌握先来先服务(FCFS)、时间片轮转(RR)和动态优先级(DP)三种常见调度算法的实现,并能够分析这些算法的优缺点,提高程序设计能力。

二、实验环境- 编程语言:C语言- 操作系统:Linux- 编译器:GCC三、实验内容本实验主要实现以下内容:1. 定义进程控制块(PCB)结构体,包含进程名、到达时间、服务时间、优先级、状态等信息。

2. 实现三种调度算法:FCFS、RR和DP。

3. 创建一个进程队列,用于存储所有进程。

4. 实现调度函数,根据所选算法选择下一个执行的进程。

5. 模拟进程执行过程,打印进程执行状态和就绪队列。

四、实验步骤1. 定义PCB结构体:```ctypedef struct PCB {char processName[10];int arrivalTime;int serviceTime;int priority;int usedTime;int state; // 0: 等待,1: 运行,2: 完成} PCB;```2. 创建进程队列:```cPCB processes[MAX_PROCESSES]; // 假设最多有MAX_PROCESSES个进程int processCount = 0; // 实际进程数量```3. 实现三种调度算法:(1)FCFS调度算法:```cvoid fcfsScheduling() {int i, j;for (i = 0; i < processCount; i++) {processes[i].state = 1; // 设置为运行状态printf("正在运行进程:%s\n", processes[i].processName); processes[i].usedTime++;if (processes[i].usedTime == processes[i].serviceTime) { processes[i].state = 2; // 设置为完成状态printf("进程:%s 完成\n", processes[i].processName); }for (j = i + 1; j < processCount; j++) {processes[j].arrivalTime--;}}}```(2)RR调度算法:```cvoid rrScheduling() {int i, j, quantum = 1; // 时间片for (i = 0; i < processCount; i++) {processes[i].state = 1; // 设置为运行状态printf("正在运行进程:%s\n", processes[i].processName); processes[i].usedTime++;processes[i].serviceTime--;if (processes[i].serviceTime <= 0) {processes[i].state = 2; // 设置为完成状态printf("进程:%s 完成\n", processes[i].processName); } else {processes[i].arrivalTime++;}for (j = i + 1; j < processCount; j++) {processes[j].arrivalTime--;}}}```(3)DP调度算法:```cvoid dpScheduling() {int i, j, minPriority = MAX_PRIORITY;int minIndex = -1;for (i = 0; i < processCount; i++) {if (processes[i].arrivalTime <= 0 && processes[i].priority < minPriority) {minPriority = processes[i].priority;minIndex = i;}}if (minIndex != -1) {processes[minIndex].state = 1; // 设置为运行状态printf("正在运行进程:%s\n", processes[minIndex].processName);processes[minIndex].usedTime++;processes[minIndex].priority--;processes[minIndex].serviceTime--;if (processes[minIndex].serviceTime <= 0) {processes[minIndex].state = 2; // 设置为完成状态printf("进程:%s 完成\n", processes[minIndex].processName); }}}```4. 模拟进程执行过程:```cvoid simulateProcess() {printf("请选择调度算法(1:FCFS,2:RR,3:DP):");int choice;scanf("%d", &choice);switch (choice) {case 1:fcfsScheduling();break;case 2:rrScheduling();break;case 3:dpScheduling();break;default:printf("无效的调度算法选择。

Linux操作系统的进程调度机制

Linux操作系统的进程调度机制

Linux操作系统的进程调度机制进程调度是操作系统中重要的一项功能,它决定了在多个进程同时运行时的优先级和时间分配。

Linux操作系统采用了多种进程调度算法,包括时间片轮转调度算法、优先级调度算法和反馈调度算法,以满足不同应用场景下的需求。

一、时间片轮转调度算法时间片轮转调度算法是Linux操作系统中最常用的调度算法之一。

它将CPU时间分为一个个时间片,每个进程在一个时间片内运行,当时间片用完后,进程被暂停,CPU切换到下一个进程。

这种调度算法公平而高效,保证了各个进程都能有公平的运行时间。

二、优先级调度算法优先级调度算法是基于进程优先级的调度方法。

Linux操作系统中每个进程都有一个优先级,优先级高的进程会被优先调度执行,而优先级低的进程会被暂时延迟。

优先级调度算法可以确保重要任务的及时执行,但也可能导致低优先级进程长时间得不到执行,产生“饥饿”现象。

三、反馈调度算法反馈调度算法是一种基于进程历史行为的动态调度算法。

Linux操作系统中的反馈调度算法将进程分为多个优先级队列,新创建的进程首先进入最高优先级队列,如果某个进程的执行时间超过了一个时间限制,该进程会被降低优先级,放入下一个较低的队列中执行。

这种调度算法可以灵活地根据进程的实际运行情况进行调整,以提高系统的整体效率。

总结:Linux操作系统采用了时间片轮转调度算法、优先级调度算法和反馈调度算法等多种进程调度算法,以满足不同应用场景下的需求。

时间片轮转调度算法保证了各个进程都能有公平的运行时间;优先级调度算法可以确保重要任务的及时执行;反馈调度算法根据进程的实际运行情况进行调整,提高了系统的整体效率。

这些调度算法共同协作,保障了Linux操作系统的稳定性和性能。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

LINUX进程调度算法的分析何 翔,顾 新(西安电子科技大学,陕西西安 710071)摘 要进程调度对一个操作系统来说是至关重要的,它起着非常关键的作用。

本文针对Linux操作系统中的普通进程调度算法进行了分析,对以进程为CPU时间分配单位和以用户为CPU时间分配单位的两种算法进行了分析和对比。

对它们在不同环境下对进程调度效率和公平性的影响进行了探讨,并总结出它们各自适用的环境。

最后为了进一步提高进程调度的效率和公平性,提出了混合算法的思想。

关键词进程调度;普通进程;动态优先级中图分类号 TP3161 前 言在Linux操作系统中,有两种常用的普通进程调度算法。

它们分别以进程和用户为调度单位来进行CPU时间的分配。

这两种算法分别体现了多进程环境下系统运行的高效性和多用户环境下的公平性。

但是这两种算法都有各自的适用环境,因此它们各自都有优缺点。

本文从多用户的公平性和多进程的高效性出发对这两种算法进行了分析和比较,最后提出了混合算法的思想,使进程调度更加高效和公平。

2 进程调度进程调度要满足高效率,公平性,响应时间快,周转时间短和吞吐量大等要求。

Linux操作系统的内核根据进程响应时间的情况把进程分为3大类:交互进程;批处理进程;实时进程。

内核在此基础上实现了3种不同的调度策略:SCHED_ FIFO(即先进现出策略);SCHED_RR(即轮转策略);SCHED_OTHER(适合交互分时的程序)。

进程调度时机,即调度器何时开始启动。

可以在以下几个时刻进行进程调度:(1)进程状态转换的时刻;(2)可运行队列中新增加一个进程时;(3)当前进程的时间片用完时;(4)进程从系统返回到用户态时;(5)内核处理完中断后,进程返回到用户态时。

在以上几种情况下进程调度可以解释为在下面几个状态中进行切换。

进程调度的流程如图1所示。

图1 进程调度的流程图图1的转换条件如下:(1)调度;(2)时间片用完;(3)跟踪并调度;(4)退出;(5)收到信号并醒来;(6)等待资源到位再调度;(7)等待资源到位再调度;(8)等待资源到位;(9)资源到位或收到信号。

3 普通进程调度算法的分析3.1 按进程调度的算法分析Schedulue()是按进程调度算法的主要函数,是系统的核心函数。

它的核心代码如下:next=idle_task(this_cpu);电子科技 2005年第9期(总第192期)21LINUX 进程调度算法的分析IT Age/ Sep. 15, 200522c=-1000;list_for_each(tmp,&runqueue_head){p=list_entry(tmp, struct task_struct, run_list);if(can_schedule(p, this_cpu)){int weight =goodness(p , this_cpu, prev->active_mm);if(weight>c)c=weight, next=p;}}这种算法是对可运行队列中的进程进行一次遍历,拿当前进程的权值和其余进程的权值进行比较。

初始的权值为c =-1000,这只有在可运行队列为空时才成立,其他进程的权值(weight )是由goodness()函数计算出来的。

当队列中某个进程的权值最大且大于当前进程的权值时,系统就把CPU 的占有权让给这个进程,否则当前进程继续占有CPU 。

这种算法在用户拥有的进程数相差不大时,可以提高系统效率。

但在各用户进程数相差悬殊时,就会产生某些拥有很少进程的用户的“饥饿感”并加大进程调度的不公平性。

举个例子——例如A ,B 两个用户,A 用户开了99个进程,B 用户只开了1个进程,根据按进程的调度算法,CPU 的时间是平均分配给每个进程的,因此系统将大部分的CPU 时间分配给了A 用户,而B 用户得到的CPU 时间只有1%。

所以B 用户一定感到很不公平。

3.2 按用户调度的算法分析算法流程如图2所示。

图中标号含义如下: (1)遍历所有用户结构,并把move_task 置空; (2)访问每一个进程;(3)判断进程所属用户的CPU 时间是否为0; (4)重新计算该进程的counter 值; (5)判断是否遍历完了所有的进程; (6)访问每一个用户;(7)判断此用户的CPU 时间是否为0; (8)把该用户放到可运行队列尾部; (9)重新计算该用户的CPU 时间; (10)判断是否遍历完了所有用户; (11)结束算法。

图2 算法流程图算法中的用户结构如下: struct user_struct {atomic_t count; atomic_t processes; atomic_t files; struct user_struct *next, **pprev; uid_t uid; struct user_struct *our_next ,*our_prev; //遍历用户结构时所需的两个指针 struct task_struct *move_task; //保存当前进程信息的指针long cpu_ticks; //用户实际拥有CPU 时间的变量 }这种算法是以用户为单位来分配CPU 的占用时间在用户拥有进程数相差很大时,可以有效的提高调度的公平性。

但在用户拥有的进程数相差不大时,就会因为多余的循环而使系统效率下降。

我们假设有两个用户分别拥有M 和N 个进程,对以上两种算法进行了,如表1。

表1 两种算法比较运行状态两用户瞬时CPU 占用之比进程瞬时占用率之比进程占用CPU 时间之比按进程调度算法 M 个进程M :N 1:1 1:1 按用户调度算法N 个进程1:1N :M 1:1LINUX 进程调度算法的分析电子科技/2005年9月15234 混合算法的思想4.1 算法描述在以上分析的基础之上笔者提出了一种混合算法的思想。

它是在按用户调度算法的基础上进行修改而得来的。

算法中主要增加了一个常数和一个数据结构,如下:const int ourschedule; //常数 struct mixing_struct { int user[UID_SZ]; //保存各用户的进程数 int max_process; //用户中拥有最多的进程数 int min_process; //用户中拥有最少的进程数 }该算法分为两个阶段。

第一个阶段:需要对每个用户所拥有的进程数进行统计。

在进程控制块task_struct 结构中有个指针user ,用来指向一个user_struct 结构。

一个用户常常有许多个进程,所以有关用户的一些信息并不专属于某一个进程。

这样,属于同一个用户的进程就可以通过指针user 共享这些信息。

显然,每个用户有且有一个user_struct 结构。

结构中有个计数器count,对属于该用户的进程数进行计数。

在进行下一个进程调度开始时对所有用户结构struct user_struct 进行遍历。

在遍历过程中计录用户结构中的count 值并写入数组int user[UID_SZ]中。

UID_SZ 代表系统中的用户数。

然后再对该数组进行排序,并把排序结果的最大,最小值分别写入变量max_process ,min_process 中。

因为在每次进程调度时用户数的变动不会太大且用户中的进程数也不会有太大的变动,所以采取冒泡排序的方法。

这样可以提高排序的效率。

最后用排序中的最大值减去最小值得到一个差值。

第二个阶段:根据常数和差值的比较结果选择调度算法,当差值等于或大于这个常数值时,也就是说系统中有些用户所拥有的进程数相差过大时就选择按用户为CPU 时间分配单位的调度算法,否则就选择按进程为CPU 时间分配单位的调度算法。

算法流程如图3所示。

算法思想如下:user_struct up; //进程结构变量 const int ourschedule; //常数值int difference; //差值mixing_struct userstruct; //记录用户进程数的结构变量 for_each_user_struct(up) //遍历每一个用户结构 userstruct .user[]=count; //统计所有计数器的值 for(i=0;i< UID_SZ,i++) {对计数值进行排序;max_process=排序最大值; min_process=排序最小值;}difference= max_process -min_process; if(difference >=ourschedule) {选择按用户调度的算法; } else {选择按进程调度的算法; }图3 算法流程图schedule()函数是被频繁调用的一个函数,它的运行直接影响到了系统的效率,因此所添加的代码应该是被调用的频率越小越好。

在这种原则之下,我们发现有一段代码只有在CPU 的时间段(epoch )全部耗尽的时候才去调用。

而在此时可以根据我们添加代码的调度信息,达到选择调度算法的目的。

LINUX 进程调度算法的分析IT Age/ Sep. 15, 200524在schedule()函数选择下一个进程时,它将判断是否需要计算进程的counter 值,这个过程在运行队列中所有进程都用完了时间片时才被调用。

而正是在这时把混合调度算法的代码加入被调用的那段程序中,所以对系统的效率影响最小。

4.2 算法效率分析通过前面的分析可以看出,影响按进程调度算法的效率主要是它的核心算法中扫描整个可运行队列链表的那个循环。

也就是说那个循环对当前可运行队列中的所有进程进行了一次遍历。

它的输入是goodness()函数的返回值即权值(weight )和假设的初始权值c =-1000。

它的输出是可以占有CPU 的下一个可运行队列中的进程。

我们假设按进程调度算法中核心算法的时间复杂度为O (n ),其中n 为可运行队列中的进程数。

影响按用户调度算法效率主要是两次用户结构的遍历和一次对进程队列的遍历。

第一次遍历用户结构所作的操作比较简单,只是把变量move_task 置空。

第二次遍历用户结构所作的操作稍微复杂一些,主要是把CPU 时间为0的用户置于队尾并且重新计算其可占用CPU 的时间。

因为系统中的用户数大大少于进程数且这两次遍历所进行的操作时间相差不大,所以这两个循环的时间复杂度都可看作O (m ),其中m 为用户数。

还有一次进程队列的遍历其操作主要是对进程占用CPU 时间的计算和用户占用CPU 时间的调整。

它和按进程调度算法中进程队列遍历所进行的操作(对进程权值进行比较)相比,稍微复杂一些。

因为按进程调度算法中的进程遍历其主要操作的依据(权值的计算)是在goodness()中进行的,所以省去了大部分的时间。

相关文档
最新文档