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参考模式列表:
进入图形模式
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的线程数据结构为单向链表:
注意:
- 在系统启动过程中,
g_task_running
是NULL
!
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
,
大致步骤略述:\userapp\include\syscall.h
声明系统调用API
\userapp\lib\syscall-wrapper.S
定义封装例程
\include\syscall-nr.h
定义系统调用号
\kernel\kernel.h
声明系统调用服务例程函数sys_setpriority
、sys_getpriority
\kernel\machdep.c
实现两个系统调用服务例程函数。
\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_create
中new->nice=0
后初始化estcpu=0
③在
init_task()
中初始化g_load_avg
④每次定时器中断更新
estcpu
和g_load_avg
:
在\kernel\timer.c中isr_timer()
函数前添加UpdateEstcpu()
实现每秒为所有线程(运行、就绪和等待)更新estcpu
和g_load_avg
,isr_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
实验结果
总结
- 编程时对性能更加注重
- 对线程的调度有初步认识
- 温故了C,感觉自己的C越来越垃圾
- 不足:
- 实验中对
nice
以及priority
属性的范围未做显式约束 - 神奇的bug是,main()中添加
task_wait()
会提前结束线程,推测调度算法有缺陷,产生于无边界约束,后期修正。(已修正)
若有时间,计划折腾其他调度算法。
感谢:林坤、王凯学长!