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_学习笔记的更多相关文章

  1. 《30天自制操作系统》学习笔记--Mac下工具的使用

    现在来介绍官网上下的工具怎么用首先是官网地址,书上有个注释上有:hrb.osask.jp 翻译成中文大概是这个样子滴. 上面有两个文件可以下载,一个是工具,一个是工具的源代码,很好的学习资料 下面把工 ...

  2. 《30天自制操作系统》学习笔记--Mac环境搭建

    弄了三天了,终于弄好了,先说结果,就是作者在网站上放了os x的工具(hrb.osask.jp,也有linux下的工具,可以自己去下载),也就是说我白忙活了三天... 再说一下这几天都干啥了,主要是想 ...

  3. 《30天自制操作系统》学习笔记--番外篇之Mac环境下的工具介绍

    这几天又有点不务正业了,书也没看,一直在搞这个破环境,尝试各种做法,网上各种垃圾信息,浪费了很多时间,说的基本都是废话,不过还是找到了一些,赶紧写下来,不然这个过几天又忘了 首先是环境,我用的是Max ...

  4. 《30天自制操作系统》读书笔记(5) GDT&IDT

    梳理项目结构 项目做到现在, 前头的好多东西都忘了, 还是通过Makefile重新理解一下整个项目是如何编译的: 现在我们拥有这么9个文件: ipl10.nas    InitialProgramLo ...

  5. 《30天自制操作系统》读书笔记(3) 引入C语言

    这一次的学习相当曲折, 主要是因为粗心, Makefile里面的错误导致了文件生成出现各种奇奇怪怪的问题, 弄得心力交瘁, 因此制作过程还是尽量按着作者的路子来吧. 作者提供的源码的注释在中文系统下是 ...

  6. 《30天自制操作系统》读书笔记(2)hello, world

    让系统跑起来 要写一个操作系统,我们首先要有一个储存系统的介质,原版书似乎是06年出版的,可惜那时候没有电脑,没想到作者用的还是软盘,现在的电脑谁有软驱?不得已我使用一张128M的SD卡来代替,而事实 ...

  7. 30天自制操作系统第九天学习笔记(u盘软盘双启动版本)

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第九天的课程已学完,确实有点不想写 ...

  8. 从你的u盘启动:30天自制操作系统第四天u盘启动学习笔记

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 developing environment:ubuntu 关于u盘启动自己做的操 ...

  9. 30天自制操作系统第八天学习笔记(u盘软盘双启动版本)

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第八天的学习思考: 关于鼠标是怎么 ...

  10. 《30天自制操作系统》笔记(03)——使用Vmware

    <30天自制操作系统>笔记(03)——使用Vmware 进度回顾 在上一篇,实现了用IPL加载OS程序到内存,然后JMP到OS程序这一功能:并且总结出下一步的OS开发结构.但是遇到了真机测 ...

随机推荐

  1. BZOJ 1083 题解

    1083: [SCOI2005]繁忙的都市 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2431  Solved: 1596[Submit][Sta ...

  2. Js:DOM对象操作常用的方法和属性

  3. HDU 1520 树形dp裸题

    1.HDU 1520  Anniversary party 2.总结:第一道树形dp,有点纠结 题意:公司聚会,员工与直接上司不能同时来,求最大权值和 #include<iostream> ...

  4. SDL实战,小游戏

    http://www.cppblog.com/sandy/archive/2005/12/28/2219.html sdl1教学 http://kelvmiao.info/sdl-tutorial-c ...

  5. 瀑布流 &留言板

    实例:瀑布流 留言板(一)瀑布流瀑布流实现原理分析1.ajax文件内容function ajax(method, url, data, success) {    var xhr = null;   ...

  6. Invalid escape sequence(valid ones are \b \t \n \f \r \" \' \\)

    Invalid escape sequence(valid ones are \b \t \n \f \r \" \' \\) 在运行eclipse的相关程序代码时遇到了报错信息,查看控制台 ...

  7. 项目管理gitflow的用法(转)

    在这里主要讲一下我在项目中用到的关于gitflow的用法.   公司的项目中,专门有一台用来存放版本库的服务器,路径是在默认的安装目录/opt/git/,那么在使用的时候,如果你是一个功能模块或者是一 ...

  8. 纪念逝去的岁月——C/C++选择排序

    选择排序 代码 #include <stdio.h> void printList(int iList[], int iLen) { ; ; i < iLen; i++) { pri ...

  9. push splice filter用法

    checkedData.push(record); 直接在record 这个数组后面添加; var index =jQuery.inArray(record,checkedData);// 获取ind ...

  10. Background agent

    Periodic Task Periodic agents run for a small amount of time on a regular recurring interval. Typica ...