操作系统实验四·xv6系统优先级调度·进程运行信息

1.实验目的

实现优先级调度,同时可以手动修改进程的优先级,并显示所有进程的运行状况信息,而且可以跟踪每个进程的调度性能,当进程结束时打印进程的准备时间,开始时间,运行时间,结束时间。

2.实验内容

在分配的这一部分中,您将把调度程序从简单循环更改为优先级调度程序。为每个进程添加一个优先级值(假设取0到31之间的范围)。范围并不重要,它只是概念的证明。从就绪列表中进行调度时,始终会首先调度优先级最高的线程/进程。
添加一个系统调用来更改进程的优先级。进程可以在任何时候改变它的优先级。如果优先级低于就绪列表中的任何进程,则必须切换到该进程。
添加一个系统调用来显示所有进程的信息。
添加字段来跟踪每个进程的调度性能。这些值应该允许您计算每个流程的周转时间和等待时间。添加一个系统调用来提取这些值,或者在进程退出时将它们打印出来。

4. 实验环境

Linux虚拟机
操作系统:Ubantu 16.04 32位
虚拟机软件:VMware Workstation 15
虚拟处理器:1个2核

5. 程序设计和实现

5.1 系统函数修改

5.1.1 proc.h
首先修改PCB内容,增添上我们需要记录的信息,优先级和相关事件。

// Per-process state
struct proc {
  uint sz;                     // Size of process memory (bytes)
  pde_t* pgdir;                // Page table
  char *kstack;                // Bottom of kernel stack for this process
  enum procstate state;        // Process state
  int pid;                     // Process ID
  struct proc *parent;         // Parent process
  struct trapframe *tf;        // Trap frame for current syscall
  struct context *context;     // swtch() here to run process
  void *chan;                  // If non-zero, sleeping on chan
  int killed;                  // If non-zero, have been killed
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)
  int create_time;
  int ready_time;
  int run_time;
  int finish_time;
  int priority;
};

5.1.2 trap.c
我们将使用xv6系统中代表时间量的信息,它的本质是技术系统启动以来经过的时间片的数量,而一个时间片的大小是固定的,因此从某种意义上来说,ticks就是一个度量时间的单位,ticks与时间片数量的关系由如下代码展示:

case T_IRQ0 + IRQ_TIMER:
    if(cpuid() == 0){
      acquire(&tickslock);
      ticks++;
      wakeup(&ticks);
      release(&tickslock);
    }
    lapiceoi();
    break;

5.1.3 proc.c
5.1.3.1首先修改allocproc,这个函数主要负责初始化
我们在这个函数里面使得所有新建的进程的优先级为12,同时标记启动时间。

// PAGEBREAK: 32
// Look in the process table for an UNUSED proc.
// If found, change state to EMBRYO and initialize
// state required to run in the kernel.
// Otherwise return 0.
static struct proc *
allocproc(void)
{
    struct proc *p;
    char *sp;

    acquire(&ptable.lock);

    for (p = ptable.proc; p < &ptable.proc[NPROC]; p++)
        if (p->state == UNUSED)
            goto found;

    release(&ptable.lock);
    return 0;

    found:
    p->state = EMBRYO;
    p->pid = nextpid++;

    p->create_time = ticks;
    p->priority = 12;

    release(&ptable.lock);

    // Allocate kernel stack.
    if ((p->kstack = kalloc()) == 0)
    {
        p->state = UNUSED;
        return 0;
    }
    sp = p->kstack + KSTACKSIZE;

    // Leave room for trap frame.
    sp -= sizeof *p->tf;
    p->tf = (struct trapframe *) sp;

    // Set up new context to start executing at forkret,
    // which returns to trapret.
    sp -= 4;
    *(uint *) sp = (uint) trapret;

    sp -= sizeof *p->context;
    p->context = (struct context *) sp;
    memset(p->context, 0, sizeof *p->context);
    p->context->eip = (uint) forkret;

    return p;
}

5.1.3.2 修改fork函数,这里面使进程处于可执行状态,这里记录上我们的准备好的时间

// Create a new process copying p as the parent.
// Sets up stack to return as if from system call.
// Caller must set state of returned proc to RUNNABLE.
int
fork(void)
{
    int i, pid;
    struct proc *np;
    struct proc *curproc = myproc();

    // Allocate process.
    if ((np = allocproc()) == 0)
    {
        return -1;
    }

    // Copy process state from proc.
    if ((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0)
    {
        kfree(np->kstack);
        np->kstack = 0;
        np->state = UNUSED;
        return -1;
    }
    np->sz = curproc->sz;
    np->parent = curproc;
    *np->tf = *curproc->tf;

    // Clear %eax so that fork returns 0 in the child.
    np->tf->eax = 0;

    for (i = 0; i < NOFILE; i++)
        if (curproc->ofile[i])
            np->ofile[i] = filedup(curproc->ofile[i]);
    np->cwd = idup(curproc->cwd);

    safestrcpy(np->name, curproc->name, sizeof(curproc->name));

    pid = np->pid;

    acquire(&ptable.lock);

    np->state = RUNNABLE;
    np->ready_time = ticks;

    release(&ptable.lock);

    return pid;
}

5.1.3.3 修改exit函数,这个函数负责程序结束时的一些操作,我们在这里记上进程结束的时间

// Exit the current process.  Does not return.
// An exited process remains in the zombie state
// until its parent calls wait() to find out it exited.
void
exit(void)
{
    struct proc *curproc = myproc();
    struct proc *p;
    int fd;

    if (curproc == initproc)
        panic("init exiting");

    // Close all open files.
    for (fd = 0; fd < NOFILE; fd++)
    {
        if (curproc->ofile[fd])
        {
            fileclose(curproc->ofile[fd]);
            curproc->ofile[fd] = 0;
        }
    }

    begin_op();
    iput(curproc->cwd);
    end_op();
    curproc->cwd = 0;

    acquire(&ptable.lock);

    // Parent might be sleeping in wait().
    wakeup1(curproc->parent);

    // Pass abandoned children to init.
    for (p = ptable.proc; p < &ptable.proc[NPROC]; p++)
    {
        if (p->parent == curproc)
        {
            p->parent = initproc;
            if (p->state == ZOMBIE)
                wakeup1(initproc);
        }
    }

    // Jump into the scheduler, never to return.
    curproc->state = ZOMBIE;
    curproc->finish_time = ticks;
    sched();
    panic("zombie exit");
}

5.1.3.4 修改wait函数,这个函数处理程序已经结束被清除掉的时候操作,这里我们在程序被清除前打印进程的所有信息

// Wait for a child process to exit and return its pid.
// Return -1 if this process has no children.
int
wait(void)
{
    struct proc *p;
    int havekids, pid;
    struct proc *curproc = myproc();

    acquire(&ptable.lock);
    for (;;)
    {
        // Scan through table looking for exited children.
        havekids = 0;
        for (p = ptable.proc; p < &ptable.proc[NPROC]; p++)
        {
            if (p->parent != curproc)
                continue;
            havekids = 1;
            if (p->state == ZOMBIE)
            {
                // Found one.
                pid = p->pid;
                cprintf("**************************\n");
                cprintf("pid %d Created time: %d\n", pid, p->create_time);
                cprintf("pid %d Ready time: %d\n", pid, p->ready_time);
                cprintf("pid %d Run time: %d\n", pid, p->run_time);
                cprintf("pid %d Finish time: %d\n", pid, p->finish_time);
                cprintf("Current time: %d\n", ticks);
cprintf("**************************\n");
                kfree(p->kstack);
                p->kstack = 0;
                freevm(p->pgdir);
                p->pid = 0;
                p->parent = 0;
                p->name[0] = 0;
                p->killed = 0;
                p->state = UNUSED;
                release(&ptable.lock);
                return pid;
            }
        }

        // No point waiting if we don't have any children.
        if (!havekids || curproc->killed)
        {
            release(&ptable.lock);
            return -1;
        }

        // Wait for children to exit.  (See wakeup1 call in proc_exit.)
        sleep(curproc, &ptable.lock);  //DOC: wait-sleep
    }
}

5.1.3.5 修改scheduler函数,这个函数负责进行调度进程,所以也是我们需要主要关注的函数,这里我们使得高优先级进程总是被选择去执行

//PAGEBREAK: 42
// Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up.
// Scheduler never returns.  It loops, doing:
//  - choose a process to run
//  - swtch to start running that process
//  - eventually that process transfers control
//      via swtch back to the scheduler.
void
scheduler(void)
{
    struct proc *p;
    struct cpu *c = mycpu();
    c->proc = 0;
    for (;;)
    {
        // Enable interrupts on this processor.
        sti();

        // Loop over process table looking for process to run.
        acquire(&ptable.lock);
        struct proc *TempProcess;
        struct proc *HighPriorityProcess = 0;
        for (p = ptable.proc; p < &ptable.proc[NPROC]; p++)
        {
            if (p->state != RUNNABLE)
                continue;
            HighPriorityProcess = p;
            for (TempProcess = ptable.proc; TempProcess < &ptable.proc[NPROC]; TempProcess++)
            {
                if (TempProcess->state != RUNNABLE)
                    continue;
                if (HighPriorityProcess->priority < TempProcess->priority)
                    HighPriorityProcess = TempProcess;    //find the runnable process with highest priority
            }
            // Switch to chosen process.  It is the process's job
            // to release ptable.lock and then reacquire it
            // before jumping back to us.
            p = HighPriorityProcess;
            c->proc = p;
            switchuvm(p);
            p->state = RUNNING;
            p->run_time=ticks;
            swtch(&(c->scheduler), p->context);
            switchkvm();

            // Process is done running for now.
            // It should have changed its p->state before coming back.
            c->proc = 0;

        }
        release(&ptable.lock);
    }
}

5.1.3.6 新增changepri函数,用于修改对应的进程的优先级

int
changepri(int pidNum, int priority)
{
    struct proc *p;
    acquire(&ptable.lock);
    for (p = ptable.proc; p < &ptable.proc[NPROC]; p++)
    {
        if (p->pid == pidNum)
        {
            p->priority = priority;
            break;
        }
    }
    release(&ptable.lock);
    return pidNum;
}
5.1.3.7 新增showpid函数,用于展示所有进程的信息
int
showpid(void)
{
    struct proc *p;
    sti();
    acquire(&ptable.lock);
    cprintf("name \t pid \t state \t       priority \n");
    for (p = ptable.proc; p < &ptable.proc[NPROC]; p++)
    {
        if (p->state == SLEEPING)
        {
            cprintf("%s \t %d \t SLEEPING \t %d\n", p->name, p->pid, p->priority);
        }
        else if (p->state == RUNNING)
        {
            cprintf("%s \t %d \t RUNNING \t %d\n", p->name, p->pid, p->priority);
        }
        else if (p->state == RUNNABLE)
        {
            cprintf("%s \t %d \t RUNNABLE \t %d\n", p->name, p->pid, p->priority);
        }
    }
    cprintf("\n");
    release(&ptable.lock);
return 23;
}

5.1.4 syscall.c
添加系统调用

extern int sys_showpid(void);
extern int sys_changepri(void);
[SYS_showpid] sys_showpid,
[SYS_changepri] sys_changepri,

5.1.5 syscall.h

#define SYS_showpid 22
#define SYS_changepri 23

5.1.6 sysproc.c
建立系统调用和函数关系

int
sys_changepri(void)
{
    int pidnum;
    int priority;
    if (argint(0, &pidnum) < 0)
    {
        return -1;
    }
    if (argint(1, &priority))
    {
        return -1;
    }
    return changepri(pidnum, priority);
}

int sys_showpid(void)
{
    return showpid();
}

5.1.7 user.h

int showpid(void);
int changepri(int, int);

5.1.8 usys.S

SYSCALL(showpid)
SYSCALL(changepri)

5.1.9 defs.h
proc.c中添加

int             changepri(int pidNum, int priority);
int             showpid(void);

5.1.10 ps.c
新建ps,用于调用showpid

#include "types.h"
#include "stat.h"
#include "user.h"

int
main(int args,char *argv[])
{
    showpid();
    return 0;
}

5.1.10 change.c
用于调用changepri,来修改进程优先级

#include "types.h"
#include "user.h"

int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        printf(0, "error(params)\n");
        printf(0, "the buffer is not enough\n");
        return 0;
    }
    else
    {
        int pidnum = atoi(argv[1]);
        int priority = atoi(argv[2]);

        if (priority < 0 || priority >31)
        {
            printf(0, "priority num error\n");
            return 0;
        }
        else
        {
            changepri(pidnum, priority);
        }
    }
    return 0;
}

5.1.10 myfork.c
自己写一个进程,让他做一些无用的计算来做测试

#include "types.h"
#include "stat.h"
#include "user.h"

int
main(int argc, char *argv[])
{
    int id = fork();
    if (id < 0)
    {
        printf(1, "%d failed in fork!\n", getpid());
    }
    else if (id == 0)
    { // child
        printf(1, "Child %d created\n", getpid());
        double b = 1;
        for (int a = 0; a < 1000000000000; a += 1)
        {
            b += 0.001;
            b = b * 101010101.1 - 0.005 / 10.0;
        }
    }
    else
    { //parent
        wait();
        printf(1, "Parent %d killed\n", getpid());
    }
    return 0;
}

5.1.11 Makefile
修改Makefile,将需要编译的程序加入到清单中

    _ps\
    _change\
    _myfork\

5.2 编译运行

make qemu 启动xv6系统后执行我们的函数myfork,同时使用ps指令展示此时进程
请添加图片描述
可以看到子进程5正在运行,这时候我们来更改他的优先级,把他改到15,再执行ps来展示信息
请添加图片描述
可以看到5进程的优先级成功的被更改到了15,我们再新增自己的进程
请添加图片描述

这时候可以看到又建立了11进程,由于新建的进程优先级为12所以系统仍然在执行5进程,我们这时候降低5进程的优先级
请添加图片描述
可以看到低优先级的进程这时候处于等待状态,11进程正在运行,这时候我们结束掉11进程
请添加图片描述
可以看到11进程被结束了,同时输出了相应的创建时间,准备时间,开始时间和结束时间。
请添加图片描述
这时候由于11进程已经被结束了,所以系统又让5进程开始执行了

  • 11
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhj12399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值