type
status
date
slug
summary
tags
category
icon
password
comment_flag
SLUGS
说明:
最近做了实验二和实验三,由于两者相关,所以堆在一起总结。 另外注意到,各个bbs上也流传着往届学长写的总结,这也是老师只甩给我们PPT,我们也能做出这实验的原因之一。 在此呢,我就写写自己的总结,当然也参考了往届学长的做法(,并且还把老师的PPT抄了一遍)。

实验目的

  • 掌握线程的创建
  • 掌握线程的调度
    • 静态优先级调度
    • 动态优先级调度

实验内容

先贴实验内容:(EPOS系统,我会在上篇文章的实验一给出)
  • 创建两个冒泡排序的线程,记为A和B
    • 分别占用屏幕的两个位置
    • 在屏幕上用进度条动态显示两个线程的静态优先级
  • 另外创建一个控制线程
    • 循环等待键盘输入
      • int key = getchar();
    • 如果key==0x4800(up)/0x5000(down)
      • 调用setpriority调高/低A线程的静态优先级
      • 更新A的优先级进度条
    • 如果key==0x4d00(right)/0x4b00(left)
      • 调用setpriority调高/低B线程的静态优先级
      • 更新B的优先级进度条
    • 把控制线程的静态优先级设置到最高,以保证控制效果

预备知识

线程API

下面说明实验所用到的API函数。 线程创建
tos :用户栈的栈顶指针 func :线程函数 pv :传递给线程函数func的参数,一般传结构体指针以传多个参数 返回值 :大于0,则表示新创建线程之ID
线程退出
code_exit :线程的退出代码
获取当前线程的ID
等待线程退出
tid :要等待线程之ID pcode_exit :如果非NULL,用于保存线程tid的退出代码
Sample:

随机数组生成

图形模式API

下面说明本实验可能用到的API函数。 获取当前系统支持的图形模式
需在文本模式下运行才可看到结果。 老师PPT参考模式列表:
notion image
进入图形模式
mode: 为上述图形模式对应的值,如int mode=0x100
获取屏幕的分辨率 直接访问变量获取: 水平:g_graphic_dev.XResolution 垂直:g_graphic_dev.YResolution
注:g_graphic_dev在graphics.h中被声明为一个全局变量,使用时应包含此头文件。
打像素点
(x, y)是像素点坐标 cr是颜色,用宏定义RGB(r,g,b)生成,其中r,g,b的取值范围都是0-255
如何从cr中取出r,g,b?用getXValue(cr),其中X=R,G,B,如getRValue(cr)取出cr的R分量值。
退出图形模式

线程调度

优先级
  • 静态优先级(nice):内核不会修改它,不随时间而变化,除非用户通过系统调用setpriority进行修改。
  • 动态优先级(priority):内核根据线程使用CPU的状况、静态优先级nice和系统负荷计算出来,会随时间而变。作为最终的调度依据,即调度器只根据动态优先级进行调度。
线程数据结构
epos的线程数据结构为单向链表:
notion image
注意:
  • 在系统启动过程中,g_task_runningNULL
  • task0(tid=0)是系统空闲线程(system idle thread),当没有其他线程可运行时,才运行task0!

关于定点数

本次实验为追求性能采用了定点数去定义一部分变量。
  • 浮点(float-point)表示 :精度高,性能差!
  • 定点(fixed-point)表示:精度低,性能好!
注:文件fixedptc.h中定义了定点数类型fixedpt及其运算,所以使用时需包含该头文件
另外,阅读往届学长的实验报告,发现代码中出现很多的fixedpt_mul(,)fixedpt_div(,)fixedpt_add(,)fixedpt_sub(,)fixedpt_fromint(,)fixedpt_toint(,)FIXEDPT_ONE……这是出于对性能的考虑。 但其中fixedpt_add(,)fixedpt_sub(,)的宏定义如下:
因此定点加减法可直接使用+、-,因为没任何性能提升,反而让代码像a piece of shit。

实验步骤

增加tcb属性nice

说明:
  • nice是整数,取值范围[-NZERO, NZERO-1],值越小优先级越高
  • #define NZERO 20
①\kernel\kernel.h中,为线程数据结构struct tcb线程的静态优先级nice属性。
②在\kernel\task.c中,函数sys_task_create中初始化nice=0

增加系统调用

系统调用说明:
  • int getpriority(int tid)
    • 成功返回(nice+NZERO),失败返回-1
  • int setpriority(int tid, int prio)
    • 把线程tid的nice设为(prio-NZERO)
    • prio必须在[0,2*NZERO-1]
    • 成功返回0,失败返回-1
①按照实验一,增加系统调用getpriority ,setpriority, 大致步骤略述:
  1. \userapp\include\syscall.h 声明系统调用API
  1. \userapp\lib\syscall-wrapper.S 定义封装例程
  1. \include\syscall-nr.h 定义系统调用号
  1. \kernel\kernel.h 声明系统调用服务例程函数sys_setprioritysys_getpriority
  1. \kernel\machdep.c 实现两个系统调用服务例程函数。
  1. \kernel\machdep.c 在系统调用分发函数syscall()中根据系统调用号加入SYSCALL_xxx分支。
②主要代码: syscall()分发函数中添加分支:
系统调用服务例程函数的实现:

tcb增加动态优先级属性

待增加的变量说明
  • estcpu-表示线程所用CPU时间,fixedpt型,定时器中断会更新一次,并且每秒钟所有线程也会更新其estcpu
  • priority-表示线程的动态优先级,int型,范围0-127
  • g_load_avg-表示系统的平均负荷,fixedpt型,每秒更新
更新规则: estcpu:
可化为
prority:
g_load_avg:
可化为
 
①在struct tcb中,增加两个属性estcpu, priority,头部声明一个全局变量g_load_avg \kernel\kernel.h中: 在nice属性后插入
头部需声明:
在\kernel\task.c中: 头部声明:
②在函数sys_task_createnew->nice=0后初始化estcpu=0
③在init_task()中初始化g_load_avg
④每次定时器中断更新estcpug_load_avg: 在\kernel\timer.c中isr_timer()函数前添加UpdateEstcpu()实现每秒为所有线程(运行、就绪和等待)更新estcpug_load_avgisr_timer()中每次定时器中断更新estcpu,其中g_timer_ticks % HZ == 0表示已过1秒,此时应调用UpdateEstcpu()

优先级调度算法

调度算法: ①线程链表中仅有task0 ? (切换到task0,重新调度) : ② ②队首为task0 ? (从g_task_head->next开始遍历,然后③) : (从g_task_head开始遍历,然后③) ③从遍历列表中找出最大优先级的tcb,同时更新priority(除task0) ④切换到选中的tcb,重新调度
算法实现: 修改\kernel\task.c中的函数schedule(),实现优先级调度算法。 附上修改后的代码,说明见注释:

测试调度器

后面的工作就是创建线程、定义线程函数、折腾画图函数等操作,不做介绍,直接贴代码。 main.c函数中:
lab3.h
 

实验结果

notion image

总结

  • 编程时对性能更加注重
  • 对线程的调度有初步认识
  • 温故了C,感觉自己的C越来越垃圾
  • 不足:
    • 实验中对nice以及priority属性的范围未做显式约束
    • 神奇的bug是,main()中添加task_wait()会提前结束线程,推测调度算法有缺陷,产生于无边界约束,后期修正。(已修正)
若有时间,计划折腾其他调度算法。
感谢:林坤、王凯学长!
操作系统实验——线程同步操作系统实验——创建系统调用
Loading...