进程调度实验

一、实验目的

  1. 通过编写程序实现进程或作业先来先服务、高优先权、按时间片轮转调度算法,进一步掌握进程调度的概念和算法,加深对处理机分配的理解。
  2. 了解进程(线程)的调度机制。
  3. 学习使用进程(线程)调度算法,掌握相应的与调度有关的 API 函数。

二、实验环境

Tencent 云服务器一台

在这里插入图片描述

三、实验内容

1 先来先服务算法

1.1 算法简介

先来先服务算法是最简单的调度算法,既可以用于作业调度 ,也可以用于程序调度,当作业调度中采用该算法时,系统将按照作业到达的先后次序来进行调度,优先从后备队列中,选择一个或多个位于队列头部的作业,把他们调入内存,分配所需资源、创建进程,然后放入“就绪队列”,直到该进程运行到完成或发生某事件堵塞后,进程调度程序才将处理机分配给其他进程。

1.2 算法流程

程序流程图如下:

请添加图片描述

1.3 代码实现

请参考 2.3

2 短作业优先算法

2.1 算法简介

对预计执行时间短的作业(进程)优先分派处理机。通常后来的短作业不抢先正在执行的作业。也就是说,不但要考虑进程的到达时间,还要考虑进程需要运行的时间。当一个进程正在运行时,假如有其他的进程到达,那么这些到达的进程就需要按照其需要运行的时间长短排序,运行时间短的在前,运行时间长的在后。

2.2 算法流程

程序流程图如下:

在这里插入图片描述

2.3 代码实现

我们按照 1 2 中的思路设计先来先服务算法和短作业优先算法,编写代码如下:

# include <stdio.h> 
# include <stdlib.h>
# define MAX 100

int n;
float t,d; /*定义两个全局变量*/ 
struct /*定义一个结构体数组,包括进程的信息*/ 
{ 
    int id; 
    float ArriveTime; 
    float RequestTime; 
    float StartTime; 
    float EndTime; 
    float RunTime; 
    float DQRunTime; 
    int Status; 
}arrayTask[MAX]; /*定义初始化的结构体数组*/ 


void GetTask()/*给结构体数组赋值,输入到达,服务时间*/ 
{ 
    int i;
    float a;
    printf("input the number of task: ");
    scanf("%d",&n); 
    for(i=0;i<n;i++)
    {
        arrayTask[i].id=i+1; 
        printf("input the the ArriveTime of arrayTask[%d]: ",i); /*用户输入进程的时间,初始为零 */ 
        scanf("%f",&a); 
        arrayTask[i].ArriveTime=a; 
        printf("input the RequestTime of arrayTask[%d]: ",i); 
        scanf("%f",&a); 
        arrayTask[i].RequestTime=a; 
        arrayTask[i].StartTime=0; 
        arrayTask[i].EndTime=0; 
        arrayTask[i].RunTime=0; 
        arrayTask[i].Status=0; /*开始默认的标志位零*/ 
    } 
} 

int FCFS() /*定义 FCFS 中寻找未执行的进程的最先到达时间*/ 
{  
    int i,j,w=0; 
    /*在结构体数组中找到一个未执行的进程*/  
    for(i=0;i<n;i++)  
    {  
        if(arrayTask[i].Status==0)  
        {  
            t=arrayTask[i].ArriveTime;
            w=1;
        }  
        if(w==1)  break; 
    }  
    for(i=0;i<n;i++) /*查找数组中到达时间最小未执行的进程*/  
    {  
        if(arrayTask[i].ArriveTime<t&&arrayTask[i].Status==0)  t=arrayTask[i].ArriveTime; 
            
    } /*返回最小到达时间的数组的下标*/  
    for(i=0;i<n;i++)  
    {  
        if (arrayTask[i].ArriveTime==t)  return i;  
    }  
}  

int SFJ() /*定义 FCFS 中寻找未执行的进程的最先到达时间*/ 
{ 
    int i,x=0,a=0,b=0; /*判断是不是第一个执行的进程*/ 
    float g;
    for(i=0;i<n;i++) 
    { 
        if(arrayTask[i].Status==1) 
        {
            g=arrayTask[i].EndTime; 
            x=1; 
        } 
    }
    if(x==0) /*第一个执行的进程按 FCFS*/ 
    { 
        t=arrayTask[0].ArriveTime; 
        for(i=0;i<n;i++) 
        { 
            if(arrayTask[i].ArriveTime<t) 
            { 
                t=arrayTask[i].ArriveTime; 
                a=i; 
            } 
        } 
        return a;
    } 
    else 
    { 
        for(i=0;i<n;i++) 
        {
            if(arrayTask[i].EndTime>g) g=arrayTask[i].EndTime; 
        } 
        for(i=0;i<n;i++) 
        {
            if(arrayTask[i].Status==0 && arrayTask[i].ArriveTime<=g) 
            {
                t=arrayTask[i].RequestTime; 
                a=i; 
                b=1;
            } /*判断有没有进程在前个进程完成前到达*/ 
        } 
        if(b!=0) /*有进程到达则按 SFJ*/ 
        {
            for(i=0;i<n;i++) 
            { 
                if(arrayTask[i].Status==0&&arrayTask[i].ArriveTime<=g&&arrayTask[i].RequestTime<t) 
                {
                    t=arrayTask[i].RequestTime;
                    a=i;
                } 
            } 
            return a;
        } 
        else
        { 
            /*否则按 FCFS*/ 
            for(i=0;i<n;i++) 
            {
                if(arrayTask[i].Status==0) t=arrayTask[i].ArriveTime;
            } 
            for(i=0;i<n;i++) 
            { 
                if(arrayTask[i].Status==0&&arrayTask[i].ArriveTime<t) 
                {
                    t=arrayTask[i].ArriveTime; 
                    a=i; 
                } 
            } 
            return a;
        } 
    } 
} 


void New(int s) /*定义执行进程后相关数据的修改*/ 
{  
    int i,g=0; 
    for(i=0;i<n;i++) 
    { 
        if(arrayTask[i].Status==0) continue; 
        else 
        { 
            g=1; 
            break; 
        } 
    } 
    if(g==0) /*当处理的是第一个未执行的进程时执行*/ 
    { 
        arrayTask[s].StartTime=arrayTask[s].ArriveTime; 
        arrayTask[s].EndTime=arrayTask[s].RequestTime+arrayTask[s].ArriveTime; 
        arrayTask[s].RunTime=arrayTask[s].RequestTime; 
        arrayTask[s].Status=1; 
        g=2; 
    } 
    else if(g==1) /*当处理的不是第一个未执行的进程时执行*/ 
    { 
        arrayTask[s].Status=1; 
        for(i=0;i<n;i++) 
        { 
            if(arrayTask[i].Status==1) 
            d=arrayTask[i].EndTime; //用 d 保存作业结束时间
        } 
        for(i=0;i<n;i++) /*查找最后执行的进程的完成时间*/ 
        { 
            if(arrayTask[i].EndTime>d&&arrayTask[i].Status==1) d=arrayTask[i].EndTime; 
        } 
        /*判断修改的进程的到达时间是否在前一个执行的进程的完成时间前面 */
        if(arrayTask[s].ArriveTime<d)  arrayTask[s].StartTime=d; 
        else arrayTask[s].StartTime=arrayTask[s].ArriveTime; 
        arrayTask[s].EndTime=arrayTask[s].StartTime+arrayTask[s].RequestTime; 
        arrayTask[s].RunTime=arrayTask[s].EndTime-arrayTask[s].ArriveTime; 
    }
    
    arrayTask[s].DQRunTime=arrayTask[s].RunTime / arrayTask[s].RequestTime;
} 
        
void Printresult(int j) /*定义打印函数*/ 
{  
    printf("%d\t",arrayTask[j].id); 
    printf("%5.2f\t",arrayTask[j].ArriveTime); 
    printf("%5.2f\t",arrayTask[j].RequestTime); 
    printf("%5.2f\t",arrayTask[j].StartTime); 
    printf("%5.2f\t",arrayTask[j].EndTime); 
    printf("%5.2f\t",arrayTask[j].RunTime); 
    printf("%5.2f\n",arrayTask[j].DQRunTime); 
}

void main() 
{ 
    int i,b,k,a,c=0; 
    int d[MAX];
    printf("F. FCFS \n"); 
    printf("S. SFJ \n"); 
    printf("Q. EXIT \n"); 
    for(i=0;;i++) 
    {
        if(c) break; 
        printf("please choose one: "); 
        a=getchar();
        getchar();
        switch(a)
        { 
            case 'Q': 
                c=1; 
                break; 
            case 'F':
                GetTask(); 
                printf("*****************************the result of FCFS*****************************\n"); 
                printf("Number\tArrive\tServer\tStart\tFinish\tRun\tDQRuntime\n"); 
                for(b=0;b<n;b++) /*调用两个函数改变结构体数的值*/ 
                { 
                    k=FCFS(); 
                    d[b]=k; 
                    New(k);
                } 
                for(b=0;b<n;b++) 
                Printresult(d[b]);/*调用打印函数打出结果*/ 
                continue; 
            case 'S': 
                GetTask(); 
                printf("******************************the result of SFJ*****************************\n"); 
                printf("Number\tArrive\tRequest\tStart\tEnd\tRun\tDQRun time\n"); 
                for(b=0;b<n;b++) 
                { 
                    k=SFJ(); 
                    d[b]=k; 
                    New(k); 
                } 
                for(b=0;b<n;b++)
                {
                    Printresult(d[b]); 
                }
                continue; 
            default:
                printf("Number Error, please input another one!\n"); 
        } 
    } 
} 

2.4 实验结果

我们运行以上代码,输入进程数目、到达时间、服务时间,如下表所示。同时我们也手动预先模拟了在先来先服务和短作业优先的情形下的进程调度情况,用于比对检验程序的正确性:

在这里插入图片描述

我们得出以下结果:

先来先服务结果

在这里插入图片描述

短作业优先结果

在这里插入图片描述

注意:程序中我们定义进程 ID 下标从 1 开始

通过比对以上两组实验结果和手动计算的结果,我们可以知道程序运行正确。

3 时间片轮转法

3.1 算法简介

时间片轮转法(Round-Robin,RR)主要用于分时系统中的进程调度。为了实现轮转调度,系统把所有就绪进程按先入先出的原则排成一个队列。新来的进程加到就绪队列末尾。每当执行进程调度时,进程调度程序总是选出就绪队列的队首进程,让它在CPU上运行一个时间片的时间。时间片是一个小的时间单位,通常为10~100ms数量级。当进程用完分给它的时间片后,系统的计时器发出时钟中断,调度程序便停止该进程的运行,把它放入就绪队列的末尾;然后,把CPU分给就绪队列的队首进程,同样也让它运行一个时间片,如此往复。

采用此算法的系统,其程序就绪队列往往按进程到达的时间来排序。进程调度程序总是选择就绪队列中的第一个进程,也就是说按照先来先服务原则调度,但一旦进程占用处理机则仅使用一个时间片。在使用先一个时间片后,进程还没有完成其运行,它必须释放出处理机给下一个就绪的进程,而被抢占的进程返回到就绪队列的末尾重新排队等待再次运行。

处理器同一个时间只能处理一个任务。处理器在处理多任务的时候,就要看请求的时间顺序,如果时间一致,就要进行预测。挑到一个任务后,需要若干步骤才能做完,这些步骤中有些需要处理器参与,有些不需要(如磁盘控制器的存储过程)。不需要处理器处理的时候,这部分时间就要分配给其他的进程。原来的进程就要处于等待的时间段上。经过周密分配时间,宏观上就象是多个任务一起运行一样,但微观上是有先后的,就是时间片轮换。

3.2 算法流程

时间片轮转算法的基本思想是,系统将所有的就绪进程按先来先服务算法的原则,排成一个队列,每次调度时,系统把处理机分配给队列首进程,并让其执行一个时间片。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序根据这个请求停止该进程的运行,将它送到就绪队列的末尾,再把处理机分给就绪队列中新的队列首进程,同时让它也执行一个时间片。

算法流程图如下:

在这里插入图片描述

3.3 代码实现

根据算法的流程及思想,我们编写代码如下:

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

typedef   struct   quen       /*定义结构*/    
{   
    char   pname[8];    
    int     time1;    
    int     time2;    
    char   state;  
    struct   quen  *next;    
}QUEN;    

void main()/*主程序*/    
{    
    QUEN   *q,*p,*head,*m;    
    char   str[8],f;    
    int   t,d,n;    
    printf("Enter the maxnumber of nodes(n):\n");
    /*输入进程数*/    
    scanf("%d",&n);    
    d=n;    
    if(d>0)    
    {  
        printf("enter the pname:");    
        scanf("%s",str);  
        printf("enter the need time:");    
        scanf("%d",&t);
        head=p=(QUEN*)malloc(sizeof(QUEN));    
        strcpy(p->pname,str);    
        p->time1=t;
        p->time2=0;
        p->state='R';
        p->next=NULL;    
        head=p;
        getchar();    
        --d;
    }    
    while(d>0)   
    {
        /*构建队列表*/    
        printf("enter the pname:");    
        scanf("%s",str);  
        printf("enter need time:");    
        scanf("%d",&t);    
        q=(QUEN *)malloc(sizeof(QUEN));    
        strcpy(q->pname,str);    
        q->time1=t;    
        q->time2=0;    
        q->state='R';    
        q->next=NULL;    
        p->next=q;    
        p=q;            // 尾插法
        --d;    
        p->next=head;   // 循环队列
        q=head;
    }    
    printf("process name need time runned static\n");    
    do
    {   
        printf("%s\t%d\t%d\t%c\n",q->pname,q->time1,q->time2,q->state);
        q=q->next;    
    }while(q!=head);    
    printf("\n");  
    do
    {    
        if(head->time2<head->time1)    
        {
            head->time2++;    
            if(head->time2==head->time1)    
            {   
                head->state='E';    
                q=head;    
                printf("The running process is %s\n",q->pname);
                printf("Pname\tLeft\tRunned\tStatus\n");    
                do
                {      
                    /*输入队列表*/    
                    printf("%s\t%d\t%d\t%c\n",q->pname,q->time1,q->time2,q->state);    
                    q=q->next;
                }while(q!=head);
                printf("\n");    
                head=head->next;    
                q=head;    
                p->next=head;    // p 是队尾
            }    
            else
            {    
                printf("The running process is %s\n",q->pname);    
                printf("Pname\tLeft\tRunned\tStatus\n");    
                do 
                {    
                    printf("%s\t%d\t%d\t%c\n",q->pname,q->time1,q->time2,q->state);    
                    q=q->next;
                }while(q!=head);    
                printf("\n");    
                head=head->next;    
                q=head;    
                p=p->next;
            }    
            printf("Is it needing new process?(y or n)\n");
            /*是否加入新的进程*/    
            getchar();  
            scanf("%c",&f);    
            if(f=='Y'||f=='y')
            {    
                getchar();  
                printf("Enter the new pname:");    
                scanf("%s",str);    
                printf("Enter the new neededtime:");    
                scanf("%d",&t);    
                m=(QUEN *)malloc(sizeof(QUEN));    
                strcpy(m->pname,str);    
                m->time1=t;    
                m->time2=0;   
                m->state='R';    
                m->next=NULL;    
                /* 全部运行完成 */
                if(q->next->state=='E')  
                {
                    p=m;    
                    head=m;  
                    p->next=head;    
                    q=head;
                }    
                else
                {
                    p->next=m;    
                    m->next=head;    
                    p=m;
                }
            }    
        }
    }while(q->next->state!='E');    
    printf("The processes are finished\n");    
}    
3.4 实验结果

我们共设置四个进程,这四个进程所需时间分别为2、5、4、3,之后我们又添加心得进程,录入所需时间 1,最终得到实验结果如下:

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里是用4个进程,分别需要2、5、1、6个单位时间运行,由运行过程可以看到,在一个时间片范围内,会选择一个进程执行,然后切换到下一个进程,所以说这种调度方式,是绝对公平的。虽说在某些情况下,带权周转时间可能不如其他的调度算法,但是,在实现人机交互上,这种算法是非常实用的。

4 优先数调度算法

4.1 算法简介

优先数调度算法常用于批处理系统中。在进程调度中,每次调度时,系统把处理机分配给就绪队列中优先数最高的进程。
它又分为两种:非抢占式优先数算法和抢占式优先数算法。

  • 在非抢占式优先数算法下,系统一旦把处理机分配给就绪队列中优先数最高的进程后,这个进程就会一直运行,直到完成或发生某事件使它放弃处理机,这时系统才能重新将处理机分配给就绪队列中的另一个优先数最高的进程。
  • 在抢占式优先数算法下,系统先将处理机分配给就绪队列中优先数最高的进程度让它运行,但在运行的过程中,如果出现另一个优先数比它高的进程,它就要立即停止,并将处理机分配给新的高优先数进程。
4.2 算法流程

算法流程如下:

在这里插入图片描述

4.3 代码实现

根据上述流程,我们编写代码如下:

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

typedef   struct   pcb/*定义结构*/    
{
    char name[5];    
    struct pcb *next;    
    int needtime;    
    int priority;    
    char state[5];    
}NODE;    

NODE  *create_process(int   n)/*创建队列*/    
{
    NODE *head,*s,*t;    
    int time,i=0,j;
    char pname[5];    
    head=(NODE*)malloc(sizeof(NODE));    
    printf("please input process name:");  
    scanf("%s",pname);    
    strcpy(head->name,pname);    
    printf("please input process time:");   
    scanf("%d",&time);
    head->needtime=time;
    printf("please input priority:");
    scanf("%d",&j);
    head->priority=j;
    strcpy(head->state,"ready");
    head->next=NULL;
    t=head;
    for(i=1;i<n;i++)    
    {  
        s=(NODE*)malloc(sizeof(NODE));    
        printf("please input process name:");    
        getchar();
        scanf("%s",pname);    
        strcpy(s->name,pname); 
        printf("please input need time:");    
        scanf("%d",&time);    
        s->needtime=time;    
        printf("please input priority:");    
        scanf("%d",&j);    
        s->priority=j;    
        strcpy(s->state,"ready");    
        s->next=NULL;    
        t->next=s;    //尾插法
        t=s;
    }    
    return head;    
}    

void pri_process(NODE *p)/*输出进程队列*/    
{
    int i;    
    NODE *q;    
    q=p->next;    
    printf("name\tneedtime\tpriority\tstate\n");  
    while(q!=NULL)    
    {
        printf("%s\t%d\t\t%d\t\t%s\n",q->name,q->needtime,q->priority,q->state);    
        q=q->next;    
    }    
}    

NODE *order(NODE *head_sort)/*对进程的优先级进行排序*/    
{
    NODE *p,*s,*q,*head,*r,*t;    
    int m,pr;  
    char name[5];    
    head=head_sort;  
    p=head->next;    
    r=p;
    t=p;    
    q=p->next;    
    /*冒泡排序*/
    while(r!=NULL)    
    {
        while(q!=NULL)    
        {
            if(p->priority<q->priority)    
            {
                m=p->priority;    
                p->priority=q->priority;    
                q->priority=m;    
                strcmp(name,p->name);  
                strcmp(p->name,q->name);    
                strcmp(q->name,name);  
                pr=p->needtime;    
                p->needtime=q->needtime;    
                q->needtime=pr;    
            }    
            p=q;  
            q=q->next;    
        }    
        r=r->next;    
        p=t;    
        q=p->next;    
    }    
    return(head_sort);  
}   
void main()/*主程序*/    
{      
    NODE *p=NULL,*head=NULL,*m=NULL,*z=NULL,*n=NULL;    
    int j,time,x=0;  
    char c,pname[5];    
    printf("please input process number!");    
    scanf("%d",&x);
    p=create_process(x);
    head=(NODE*)malloc(sizeof(NODE)); 
    head->next=p;
    pri_process(head);
    getchar();
    while(x>0)
    {
        order(head);
        m=head->next;
        strcpy(m->state,"run");    
        if(m->priority>=2)    
        m->priority--;    
        m->needtime--;    
        if(head->next!=NULL)    
        pri_process(head);    
        if(m->needtime==0)    
        {   
            head->next=m->next;    
            printf("%s has finished\n",m->name);    
            free(m);    
            x--;    
        }    
        getchar();     
    }    
    printf("over!");    
    getchar();    
}    
4.4 实验结果

我们运行以上代码,输入一组数据进行测试:

在这里插入图片描述

得到如下结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们可以看到,程序自动按照优先级从大到小的顺序进行执行,当存在较大的优先级进程时,系统会先运行完成,之后再运行优先级小的进程,程序运行的结果符合我们的预期。

  • 9
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
进程调度操作系统中非常重要的一部分,它决定了在多个进程同时运行时哪个进程应该被分配CPU时间。在Linux系统中,进程调度器使用调度策略来决定进程的优先级和CPU时间分配。 为了进行Linux进程调度实验,你需要按照以下步骤操作: 1. 创建多个进程:在Linux系统中,可以使用fork()函数创建多个进程,每个进程会有自己的进程ID和优先级。 2. 设置进程的优先级:Linux系统中有多种进程调度策略,如SCHED_FIFO、SCHED_RR和SCHED_OTHER。可以使用sched_setscheduler()函数设置进程的调度策略和优先级。 3. 执行进程:通过调用exec()函数或者其他相应的系统调用来执行进程。 4. 监控进程的运行情况:可以使用ps命令、top命令或者其他系统调用来监控进程的运行情况。 5. 分析进程调度:可以使用perf工具来分析进程的调度情况,了解进程的运行情况和性能瓶颈。 在进行Linux进程调度实验时,需要注意以下几点: 1. 确保系统的负载不会过高:在创建多个进程时,需要考虑系统的负载情况,避免过多的进程导致系统崩溃或者运行缓慢。 2. 确保进程的调度策略合理:不同的进程需要不同的调度策略和优先级,需要根据进程的特点和需求来选择适当的调度策略。 3. 分析实验结果:在实验完成后,需要对进程的运行情况和性能进行分析,找出可能存在的问题和改进的方向。 总之,Linux进程调度实验是非常重要的操作系统实验之一,可以帮助学生深入了解进程调度的原理和实现,提高操作系统的理论水平和实践能力。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值