《30天自制操作系统》15_day_学习笔记
harib12a:
这一部分我们来尝试两个任务的切换。下面我们一步一步的看:
1、定义TSS任务状态段(task statuc segment);定义的一种段,需要在GDT中定义使用
//TSS任务状态段(task statuc segment)
struct TSS32 {//26个int成员,104字节
//与任务设置相关的信息(任务切换时,除backlink,都不会被写入)
int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
//32位寄存器;eip任务返回时,找到返回的地址
int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
//16位寄存器
int es, cs, ss, ds, fs, gs;
//第一行一样,有关任务设置的信息。任务切换时CPU不写
//ldtr = 0; iomap = 0x4000_0000
int ldtr, iomap;
};
2、尝试两个任务的切换。A和B
//(1)、两个任务定义
struct TSS32 tss_a, tss_b;
//(2)、任务初始化:ldtr和iomap赋初值
tss_a.ldtr = ;
tss_a.iomap = 0x40000000;
tss_b.ldtr = ;
tss_b.iomap = 0x40000000;
//(3)、在GDT中定义3号、4号
set_segmdesc(gdt + , , (int) &tss_a, AR_TSS32);
set_segmdesc(gdt + , , (int) &tss_b, AR_TSS32);
3、TR(task register)寄存器:让CPU记住当前运行哪一个任务(GDT中任务号*8)
//HariMain
load_tr( * );
//naskfunc.nas
_load_tr: ; void load_tr(int tr);
LTR [ESP+] ; tr
RET
//任务切换函数
_taskswitch4: ; void taskswitch4(void);
JMP *:;4*8用来指向TSS。这个是任务段啊!哥哥
RET ;从汇编语言返回到C语言执行
4、程序执行10s后进行任务切换
void HariMain(){
//.......B的任务栈
tss_b.esp = task_b_esp;//B的任务栈
task_b_esp = memman_alloc_4k(memman, * ) + * ;
//.......
else if (i == ) { /* 10秒定时器超时 */
putfonts8_asc_sht(sht_back, , , COL8_FFFFFF, COL8_008484, "10[sec]", );
taskswitch4(); //进行任务切换
} else if (i == ) { /* 3昩僞僀儅 */
//......
}
harib12b:
上一步,我们让任务B切换到任务A;下面再切回任务A 。改写task_b_main();最后调用taskswitch3()切回任务A:
void task_b_main(void)
{ //.....
timer = timer_alloc();
timer_init(timer, &fifo, );
timer_settime(timer, );
//....定时器超时时间为5s,5s后切回任务A。3号
taskswitch3();
} // 跳到GDT中3号段的位置
_taskswitch3: ; void taskswitch3(void);
JMP *:
RET
harib12c:
上面我们进行了任务A、B之间的切换,方法是为A\B都写一个任务入口的函数,在这个函数中调用teskswitch();然而在真正的系统中,如果我们有100个任务,难道我们要去为这100个任务写100个处理函数?显然这不是我们想要的,接下来我们来实现一个通用函数来进行任务切换。
;函数:farjmp(eip,cs);
;例如:farjmp(0,4*8);表示切换到GDT的4号段的任务
_farjmp: ; void farjmp(int eip, int cs);
JMP FAR [ESP+] ; eip, cs
RET
接着准备一个任务切换的计时器。timer_ts,用每0.02s执行一次任务的切换。每次farjmp切换返回的时候,将定时器重新设定到0.02s后,让程序返回0.02s后再次执行任务切换。
//HariMain部分代码:
load_tr( * )//初始化TR寄存器位3号,任务A运行
for (;;) {
//...
farjmp(, * );//跳到4号,任务B
timer_settime(timer_ts, );//延时0.02s
//...
}
void task_b_main(void)//任务B做的事情。
{ //...
farjmp(, * );跳到3号,任务A
timer_settime(timer_ts, );//这个好像没有执行啊!!!???
}
harib12d:
sht_back背景图层实在HariMain中定义的。怎么让void task_b_main(void)知道sht_back的值?我们将将sht_back的地址写到内存0x0fec中。然后再task_b_main(void)中读出来:
//HariMain将sht_back写到内存中。
*((int *) 0x0fec) = (int) sht_back;
//task_b_main将sht_back的值读出来
sht_back = (struct SHEET *) *((int *) 0x0fec);
harib12e:
我们发现虽然上面实现任务之间的不断切换,但是在真机上的运行时间太慢了。因为count每计数一次就刷新一次。然而我们并不要求刷新这么频繁,人眼是分辨不出来的。我们只需要0.01s刷新一次就行了/。修改task_b_main()
sht_back从HM中传过来的方法:将sht_back的地址放到任务B段的起始位置,这样任务B在执行task_b_main时,会把开始的4个字节的数据当作参数*sht_back的地址。另外,task_b_main实际上就是任务B执行的内容。没有任何函数调用,不用return来返回调用处。
void task_b_main(struct SHEET *sht_back)
{
struct FIFO32 fifo; //32位的FIFO缓冲区
struct TIMER *timer_ts, *timer_put;//两个定时器
int i, fifobuf[], count = ;
char s[]; fifo32_init(&fifo, , fifobuf);//FIFO缓冲区初始化fifobuf[128]
timer_ts = timer_alloc(); //任务切换定时器,0.02s
timer_init(timer_ts, &fifo, ); //数据为2
timer_settime(timer_ts, ); timer_put = timer_alloc(); //刷新(输出)定时器,0.01s
timer_init(timer_put, &fifo, ); //数据为1
timer_settime(timer_put, ); for (;;) {
count++;
io_cli();
if (fifo32_status(&fifo) == ) { //缓冲区为空
io_sti();
} else {
i = fifo32_get(&fifo); //超时了,获得定时器号
io_sti();
if (i == ) { //数据为1,刷新定时器超时
sprintf(s, "%11d", count);
putfonts8_asc_sht(sht_back, , , COL8_FFFFFF, COL8_008484, s, );
timer_settime(timer_put, );//输出后,在设定定时器1
} else if (i == ) { //数据为2,任务切换定时器。
farjmp(, * ); //切换任务后再设定定时器2
timer_settime(timer_ts, );
}
}
}
}
harib12f:
上面我们将刷新频率固定到0.01s一次。这里我们来测试一些程序的运行速度。在task_b_main中加入一些内容:
void task_b_main(struct SHEET *sht_back)
{ //......增加一个定时器
timer_1s = timer_alloc();
timer_init(timer_1s, &fifo, );
timer_settime(timer_1s, );
//.....
if (i == ) { //100号;10s
//这里没100次中断,显示count计数的值。
sprintf(s, "%11d", count - count0);
putfonts8_asc_sht(sht_back, , , COL8_FFFFFF, COL8_008484, s, );
count0 = count;
timer_settime(timer_1s, );
}
//........
} //接下来笔者把显示计数的定时器去掉,重新测试了一下
//发现速度的确有所提升,这里主要是要知道测试的方法。*****
harib12g:
我们上面的多任务实在HM和TB中写入任务切换来实现的。下面来创建真正的多任务
1、创建多任务函数
//mtask.c文件
#include "bootpack.h"
struct TIMER *mt_timer;
int mt_tr;
//初始化*mt_timer;mt_tr;
void mt_init(void)
{
mt_timer = timer_alloc();//为定时器mt_timer分配内存
//我们不再需要向FIFO中写数据了
timer_settime(mt_timer, );//设置超时时间0.02s
mt_tr = * ;//将要写到GDT的3号段
return;
}
void mt_taskswitch(void)
{
if (mt_tr == * ) {
mt_tr = * ;
} else {
mt_tr = * ;
}
timer_settime(mt_timer, );//再定时0.02s
farjmp(, mt_tr);//任务切换
return;
}
2、修改定时器中断inthandler20()
void inthandler20(int *esp)
{ //.......
for (;;) {
/* timers的计时器全部在工作中,不需要flag */
if (timer->timeout > timerctl.count) {
break; //没有超时,不发生中断。回去
}
/* 超时了,下面进行中断处理 */
timer->flags = TIMER_FLAGS_ALLOC;
if (timer != mt_timer) { //发生中断的定时器不是*mt_timer;
//向FOFO写数据
fifo32_put(timer->fifo, timer->data);
} else {
//*mt_timer;不用想FIFO写数据
ts = ; /* mt_timer超时了 */
}
timer = timer->next; /* 下一个计时器 */
}
timerctl.t0 = timer; //t0放定时器链表的第一个地址
timerctl.next = timer->timeout;//下一个定时器的超时时间。
if (ts != ) {
//ts就是用来进行任务A\B切换的标志
mt_taskswitch();
}
return;
}
3、接下来,删除bootpack.c中原来的任务A.B切换的代码即可!
QUE:为什么不在tm_timer超时的地方直接调用mt_taskswitch();?
ANS:调用mt_taskswitch();进行任务切换的时候,即便中断还没有处理完成,IF(中断允许标识)可能会被重设回1,(因为任务切换的时候同时会被切换EFLAGS),这样,在定时器中断还没有处理完成的时候,会产生下一个中断请求,导致程序出错。记住:任务可以切换,中断时不能切换的。中断必须处理完成后,才能处理下一个中断,不然会有意想不到的错误。
《30天自制操作系统》15_day_学习笔记的更多相关文章
- 《30天自制操作系统》学习笔记--Mac下工具的使用
现在来介绍官网上下的工具怎么用首先是官网地址,书上有个注释上有:hrb.osask.jp 翻译成中文大概是这个样子滴. 上面有两个文件可以下载,一个是工具,一个是工具的源代码,很好的学习资料 下面把工 ...
- 《30天自制操作系统》学习笔记--Mac环境搭建
弄了三天了,终于弄好了,先说结果,就是作者在网站上放了os x的工具(hrb.osask.jp,也有linux下的工具,可以自己去下载),也就是说我白忙活了三天... 再说一下这几天都干啥了,主要是想 ...
- 《30天自制操作系统》学习笔记--番外篇之Mac环境下的工具介绍
这几天又有点不务正业了,书也没看,一直在搞这个破环境,尝试各种做法,网上各种垃圾信息,浪费了很多时间,说的基本都是废话,不过还是找到了一些,赶紧写下来,不然这个过几天又忘了 首先是环境,我用的是Max ...
- 《30天自制操作系统》读书笔记(5) GDT&IDT
梳理项目结构 项目做到现在, 前头的好多东西都忘了, 还是通过Makefile重新理解一下整个项目是如何编译的: 现在我们拥有这么9个文件: ipl10.nas InitialProgramLo ...
- 《30天自制操作系统》读书笔记(3) 引入C语言
这一次的学习相当曲折, 主要是因为粗心, Makefile里面的错误导致了文件生成出现各种奇奇怪怪的问题, 弄得心力交瘁, 因此制作过程还是尽量按着作者的路子来吧. 作者提供的源码的注释在中文系统下是 ...
- 《30天自制操作系统》读书笔记(2)hello, world
让系统跑起来 要写一个操作系统,我们首先要有一个储存系统的介质,原版书似乎是06年出版的,可惜那时候没有电脑,没想到作者用的还是软盘,现在的电脑谁有软驱?不得已我使用一张128M的SD卡来代替,而事实 ...
- 30天自制操作系统第九天学习笔记(u盘软盘双启动版本)
暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078 ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第九天的课程已学完,确实有点不想写 ...
- 从你的u盘启动:30天自制操作系统第四天u盘启动学习笔记
暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078 ,更多学习中的问题.资料,群里分享 developing environment:ubuntu 关于u盘启动自己做的操 ...
- 30天自制操作系统第八天学习笔记(u盘软盘双启动版本)
暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078 ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第八天的学习思考: 关于鼠标是怎么 ...
- 《30天自制操作系统》笔记(03)——使用Vmware
<30天自制操作系统>笔记(03)——使用Vmware 进度回顾 在上一篇,实现了用IPL加载OS程序到内存,然后JMP到OS程序这一功能:并且总结出下一步的OS开发结构.但是遇到了真机测 ...
随机推荐
- 【BZOJ2818】Gcd 欧拉筛
Description 给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对. Input 一个整数N Output 如题 Sample Input 4 Sam ...
- SpringMVC异常处理机制详解[附带源码分析]
目录 前言 重要接口和类介绍 HandlerExceptionResolver接口 AbstractHandlerExceptionResolver抽象类 AbstractHandlerMethodE ...
- ArrayList和Vector的扩容机制
ArrayList和Vector都是继承了相同的父类和实现了相同的接口.如下 public class Vector<E> extends AbstractList<E> im ...
- jquery插件之tab标签页或滑动门
该插件乃本博客作者所写,目的在于提升作者的js能力,也给一些js菜鸟在使用插件时提供一些便利,老鸟就悠然地飞过吧. 此插件旨在实现目前较为流行的tab标签页或滑动门特效,在此插件中默认使用的是鼠标滑过 ...
- 详解CALayer 和 UIView的区别和联系
详解CALayer 和 UIView的区别和联系 前言 前面发了一篇iOS 面试的文章,在说到 UIView 和 CALayer 的区别和联系的时候,被喵神指出没有切中要点,所以这里就 CALay ...
- HTTP头部解析
当我们打开一个网页时,浏览器要向网站服务器发送一个HTTP请求头,然后网站服务器根据HTTP请求头的内容生成当次请求的内容发送给浏览器.你明白HTTP请求头的具体含意吗?下面一条条的为你详细解读,先看 ...
- 原生javascript封装ajax和jsonp
在我们请求数据时,完成页面跨域,利用原生JS封装的ajax和jsonp: <!DOCTYPE html> <html lang="en"> <head ...
- Sqoop_ 简单介绍
一.基本作用 概念: Sqoop被称为协作框架,是在Hadoop.2.X生态系统的辅助型框架,简单说,就是一个数据转换工具,类似的协作框架有文件收集库框架Flume,任务协调框架Oozie,大数据We ...
- 行业集中度(Concentration Ratio)
行业集中度是决定市场结构最基本.最重要的因素,集中体现了市场的竞争和垄断程度,经常使用的集中度计量指标有:行业集中率(CRn指数).赫尔芬达尔—赫希曼指数(Herfindahl-HirschmanIn ...
- 兼容的firstChild,lastChild,nextSibling,previousSibling写法
在IE下是支持firstChild,lastChild,nextSibling,previousSibling 但是在FF下,由于它会把标签之间的空格当成文本节点,所以为了准确地找到相应的元素,会用 ...