操作系统实验----线程的调度

学 生 实 验 报 告

实验课程名称        操作系统               

开课实验室                         

学       院 年级   专业班      

学 生 姓 名    学  号       

开 课 时 间  2023    2024   学年第    学期

总 成 绩

教师签名

操作系统》实验报告

 开课实验室:DS1502                                           2024  年  4 5  

学院

年级、专业、班

姓名

成绩

课程

名称

计算机操作系统实验

实验项目

名    称

线程的调度

指导教师

教师评语

教师签名:

年  月  日

  • 实验目的

掌握线程的调度(优先级调度)

FAQ

二、实验内容

优先级分为静态优先级和动态优先级,静态优先级(nice):内核不会修改它,不随时间而变化,除非用户通过系统调用setpriority进行修改;动态优先级(priority):内核根据线程使用CPU的状况、静态优先级nice和系统负荷计算出来,会随时间而变化。本实验最终的调度依据为调度器只根据动态优先级进行调度。

具体步骤如下:

  • Step1:在“struct tcb”中增加线程的静态优先级nice
  • Step2:增加系统调用
  • Step3:在“struct tcb”中,再增加两个属性。estcpu:表示线程最近使用了多少CPU时间;priority:表示线程的动态优先级
  • Step4:增加一个全局变量g_load_avg来表示系统的平均负荷
  • Step5:属性计算,计算estcpu和priority
  • Step6:修改task.c中的函数schedule,实现优先级调度算法
  • Step7:测试调度器

三、使用仪器、材料

编译器、链接器、调试器

GCC、LD、GDB

PC模拟器(虚拟机)

Qemu


实验过程原始记录(数据、图表、计算等)

  • Step1:在文件“kernel.h”中的“struct tcb”中增加线程的静态优先级nice
    • 加在kstack字段之后、signature字段之前
    • 在文件“task.c”中的函数sys_task_create中初始化nice=0
    • nice是整数,取值范围[-NZERO, NZERO-1],值越小优先级越高。在文件“syscall-nr.h”中定义NZERO #define NZERO 20

  • Step2:在文件“task.c”中增加系统调用
    • int sys_getpriority(int tid)
      • 成功返回线程tid的(nice+NZERO),失败返回-1

         

    •  int sys_setpriority(int tid, int prio)
      • 把线程tid的nice设为(prio-NZERO)
        • prio必须在[0,2*NZERO-1]内
      • 成功返回0,失败返回-1
  • Step3:在“struct tcb”中,再增加两个属性

    

    • estcpu:表示线程最近使用了多少CPU时间
      • 在函数sys_task_create中初始化estcpu=0
      • 每次定时器中断:g_task_running->estcpu++,task0除外!
      • 每秒钟为所有线程(运行、就绪和等待)更新一次

           

    • priority:表示线程的动态优先级
      • priority = PRI_USER_MAX-(estcpu/4)-(nice*2)
        • 截断到PRI_USER_MIN到PRI_USER_MAX范围内
          • #define PRI_USER_MIN    0
          • #define PRI_USER_MAX  127
        • 值越大优先级越高

           

  • Step4:增加一个全局变量
    • g_load_avg:表示系统的平均负荷(在定时器isr_timer函数中实现)
      • 初值为0
      • 每秒钟更新一次(nready表示处于就绪状态的线程个数,task0除外)
        • g_load_avg=(59/60) ×g_load_avg+(1/60) × nready

  • Step5:属性计算
    • g_load_avg 和线程的estcpu
      • 在定时器的中断处理函数(ISR)中计算
        • 文件timer.c中的函数isr_timer
      • 如何每隔一秒计算一次?
        • (g_timer_ticks % HZ)
          • =0,表示1秒钟已经过去
          • 否则,还不到1秒钟
  • Step6:修改task.c中的函数schedule,实现优先级调度算法
    • 先计算各个线程的动态优先级priority(task0除外!),然后进行调度
    • 注意:
      • 线程0(task0,它的ID=0)是一个特殊的线程,仅当没有其他可运行的线程时,才能调度task0运行!
      • 函数schedule被执行时,CPU的中断已经被关闭。

  • Step7:测试调度器
    • 创建两个冒泡排序的线程
      • 分别占用屏幕的两个位置
      • 利用drwasort函数帮助画图

    • 另外创建一个控制线程
      • 循环等待键盘输入
        • int key = getchar();
      • 如果key=0x4800(up)/0x5000(down)
        • 调用setpriority调高/低左边线程的静态优先级
      • 如果key=0x4d00(right)/0x4b00(left)
        • 调用setpriority调高/低右边线程的静态优先级

    • 在主函数中调用

五、
验结果及分析

(下方线条的长度越长,优先级越低)

结论:

  • 了解优先级调度在操作系统中的实现
  • 线程调度的算法中优先级调度可能出现优先级反转和饥饿现象

附:main函数所有代码

/*

 * vim: filetype=c:fenc=utf-8:ts=4:et:sw=4:sts=4

 */

#include <inttypes.h>

#include <stddef.h>

#include <math.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/mman.h>

#include <syscall.h>

#include <netinet/in.h>

#include <stdlib.h>

#include "graphics.h"

#include "syscall-nr.h"

extern void* tlsf_create_with_pool(void* mem, size_t bytes);

extern void* g_heap;

int tid_foo1, tid_foo2, tid_foo3;

typedef struct Numbers {

    int arr[100];

}MyNumberStruct;

int xlen;

int ylen;

int size = 100;

/**

 * GCC insists on __main

 *    http://gcc.gnu.org/onlinedocs/gccint/Collect2.html

 */

void __main()

{

    size_t heap_size = 32 * 1024 * 1024;

    void* heap_base = mmap(NULL, heap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);

    g_heap = tlsf_create_with_pool(heap_base, heap_size);

}

void drawsort(int arr[], int flag) {

    if (flag == 1) {

        for (int i = 0; i < size; i++) {

            //line(586, i * 4, 586 + xlen / 3, i * 4, RGB(0, 0, 0));

            //line(586, i * 4, 586 + arr[i] * 2.5, i * 4, RGB(255, 255, 255));

            line(0, i * 4, xlen / 3, i * 4, RGB(0, 0, 0));

            line(0, i * 4, arr[i] * 2.5, i * 4, RGB(0, 0, 255));

        }

    }

    else if (flag == 2) {

        for (int i = 0; i < size; i++) {

            line(300, i * 4, 300 + xlen / 3, i * 4, RGB(0, 0, 0));

            line(300, i * 4, 300 + arr[i] * 2, i * 4, RGB(255, 0, 0));

        }

    }

}

void tsk_foo1(void* pv) // 线程1的冒泡排序

{

    printf("This is 1 task foo with tid=%d\r\n", task_getid());

    tid_foo1 = task_getid();

    MyNumberStruct N = (MyNumberStruct)(*((MyNumberStruct*)pv));

    struct timespec req, rem;

    req.tv_sec = 0.9;

    req.tv_nsec = 1000000L;

    for (int i = 0; i < size; i++) {

        for (int j = 0; j < size - i - 1; j++) {

            if (N.arr[j] > N.arr[j + 1]) {

                int temp = N.arr[j];

                N.arr[j] = N.arr[j + 1];

                N.arr[j + 1] = temp;

                nanosleep(&req, &rem);

            }

        }

        drawsort(N.arr, 1);

    }

    drawsort(N.arr, 1);

    task_exit(0);//不能直接return,必须调用task_exit

}

void tsk_foo2(void* pv)//线程2的冒泡排序

{

    printf("This is 2 task foo with tid=%d\r\n", task_getid());

    tid_foo2 = task_getid();

    MyNumberStruct N1 = (MyNumberStruct)(*((MyNumberStruct*)pv));

    struct timespec req, rem;

    req.tv_sec = 0.9;

    req.tv_nsec = 1000000L;

    for (int i = 0; i < size; i++) {

        for (int j = 0; j < size - i - 1; j++) {

            if (N1.arr[j] > N1.arr[j + 1]) {

                int temp = N1.arr[j];

                N1.arr[j] = N1.arr[j + 1];

                N1.arr[j + 1] = temp;

                nanosleep(&req, &rem);

            }

        }

        drawsort(N1.arr, 2);

    }

    drawsort(N1.arr, 2);

    task_exit(0);//不能直接return,必须调用task_exit

}

void tsk_foo3(void* pv) //控制线程

{

    printf("This is control task foo with tid=%d\r\n", task_getid());

    line(80, 450, 100 + 10 * getpriority(tid_foo1), 450, RGB(0, 0, 255));

    line(80, 500, 100 + 10 * getpriority(tid_foo2), 500, RGB(255, 0, 0));

    while (1) {

        int key = getchar();

        int a = getpriority(tid_foo1);

        int b = getpriority(tid_foo2);

        if (key == 0x4800 || key == 0x5000) {

            if (key == 0x4800) {//up

                setpriority(tid_foo1, a + 10);

                a = getpriority(tid_foo1);

                line(80, 450, 500, 450, RGB(0, 0, 0));//进度条越长,优先级越低

                line(80, 450, 100 + 10 * a, 450, RGB(0, 0, 255));

            }

            else {

                setpriority(tid_foo1, a - 1);

                a = getpriority(tid_foo1);

                line(80, 450, 500, 450, RGB(0, 0, 0));

                line(80, 450, 100 + 10 * a, 450, RGB(0, 0, 255));

            }

        }

        else if (key == 0x4d00 || key == 0x4b00) {

            if (key == 0x4d00) {//right

                setpriority(tid_foo2, b + 3);

                b = getpriority(tid_foo2);

                line(80, 500, 500, 500, RGB(0, 0, 0));

                line(80, 500, 100 + 10 * b, 500, RGB(255, 0, 0));

            }

            else {

                setpriority(tid_foo2, b - 1);

                b = getpriority(tid_foo2);

                line(80, 500, 500, 500, RGB(0, 0, 0));

                line(80, 500, 100 + 10 * b, 500, RGB(255, 0, 0));

            }

        }

        else {

        }

    }

    task_exit(0);

}

/**

 * 第一个运行在用户模式的线程所执行的函数

 */

void main(void* pv)

{

    int mode = 0x115;

    init_graphic(mode);//进入图形模式

    xlen = g_graphic_dev.XResolution;

    ylen = g_graphic_dev.YResolution;

    //生成随机数

    MyNumberStruct N;

    for (int i = 0; i < size; i++) {

        N.arr[i] = rand() % 50 + 1;

    }

    MyNumberStruct N1;

    for (int i = 0; i < size; i++) {

        N1.arr[i] = rand() % 80 + 1;

    }

    //*************************************************************************************************************//

    //线程函数

    unsigned char* stack_foo1;

    unsigned int  stack_size1 = 1024 * 1024;

    stack_foo1 = (unsigned char*)malloc(stack_size1);

    //*************************************************************************************************************//

    //线程函数

    unsigned char* stack_foo2;

    unsigned int  stack_size2 = 1024 * 1024;

    stack_foo2 = (unsigned char*)malloc(stack_size2);

    //*************************************************************************************************************//

    unsigned char* stack_foo3;

    unsigned int  stack_size3 = 1024 * 1024;

    stack_foo3 = (unsigned char*)malloc(stack_size3);

    //*************************************************************************************************************//

    tid_foo3 = task_create(stack_foo3 + stack_size3, &tsk_foo3, &N1);

    setpriority(tid_foo3, 1);

    tid_foo1 = task_create(stack_foo1 + stack_size1, &tsk_foo1, &N);

    setpriority(tid_foo1, 3);

    tid_foo2 = task_create(stack_foo2 + stack_size2, &tsk_foo2, &N1);

    setpriority(tid_foo2, 3);

    while (1)

        ;

    task_exit(0);

    free(stack_foo1);

    free(stack_foo2);

    free(stack_foo3);

}

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值