操作系统实验——线程创建与调度
summary
type
status
slug
category
date
tags
icon
password
comment
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()会提前结束线程,推测调度算法有缺陷,产生于无边界约束,后期修正。(已修正)
若有时间,计划折腾其他调度算法。
感谢:林坤、王凯学长!
Loading...