《30天自制操作系统》16_day_学习笔记

harib13a:
  今天我们要继续折腾多任务,任务的高效管理是操作系统的一个重要的任务。在今天,我们将为系统创建更加完善的任务管理系统,其中包括优先级,任务等级等。
  1、任务管理结构体

#define MAX_TASKS        1000    /* 最大任务数量 */
#define TASK_GDT0        3    /* 任务块在GDT中的初始位置:从GDT的3号段开始 */
struct TSS32 {//任务状态段,这个在前面已经提到过,这里再介绍一下
          // 26个int成员,104字节
    int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;   //与任务设置相关的信息(任务切换时,除backlink,都不会被写入)
    int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;//32位寄存器;eip任务返回时,找到返回的地址
    int es, cs, ss, ds, fs, gs;//16位寄存器
    int ldtr, iomap;      //有关任务设置的信息。任务切换时CPU不写;ldtr = 0; iomap = 0x4000_0000
};
struct TASK {//任务结构体
    //sel用来存储GDT中的编号
    //flag表示任务的状态
    //tss是任务状态段
    int sel, flags; 
    struct TSS32 tss;
};
struct TASKCTL {            //任务管理结构体
    int running;           /* 表示正在运行中的任务数量 */
    int now;              /* 记录当前正在运行的是哪一个任务 */
    struct TASK *tasks[MAX_TASKS];//记录正在运行中的任务的地址
    struct TASK tasks0[MAX_TASKS];//1000个任务
};

  2、初始化任务管理结构体

//初始化TASKCTL;初始化一个TASK为flag=2(正在运行)
//返回:返回正在运行这个任务的地址
struct TASKCTL *taskctl;
struct TIMER *task_timer;
struct TASK *task_init(struct MEMMAN *memman)
{
    int i;
    struct TASK *task;    //临时任务变量
    struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;    //初始化任务段描述的GDT
    taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));//分配任务管理结构体
    for (i = 0; i < MAX_TASKS; i++) {
        taskctl->tasks0[i].flags = 0;          //所有任务的flag置0 (未使用)
        taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;//任务在GDT中的编号
        set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);//1000个任务按照编号写入GDT中
    }
    task = task_alloc();    //临时任务分配
    task->flags = 2;       /* 临时任务为正在运行中flag=2 */
    taskctl->running = 1;    //正在运行中任务数为1
    taskctl->now = 0;      //当前运行任务编号为0(数组从0开始的)
    taskctl->tasks[0] = task;  //赋值给TASKCTK的第一个任务
    load_tr(task->sel);     //加载到TR寄存器中,让该任务运行
    task_timer = timer_alloc();//定时器设置
    timer_settime(task_timer, 2);
    return task;
}

  3、创建初始化一个任务结构的函数

/*    这个函数做的事,就是把TASKCTL中的任务数组task0[]
    的下一个没有使用的任务的任务状态段赋值,初始化,并置flag=1正在使用*/
struct TASK *task_alloc(void)
{
    int i;
    struct TASK *task;//临时的任务结构体
    for (i = 0; i < MAX_TASKS; i++) {    //按顺序查找taskctl中的所有任务
        if (taskctl->tasks0[i].flags == 0) {//找到第一个没有被分配的任务
            task = &taskctl->tasks0[i];   //赋值给临时变量task
            task->flags = 1;          /* 正在使用的标识 */
            task->tss.eflags = 0x00000202;  /* IF = 1; */
            task->tss.eax = 0;         /* 其他的值都初始化为0 */
            task->tss.ecx = 0;
            task->tss.edx = 0;
            task->tss.ebx = 0;
            task->tss.ebp = 0;
            task->tss.esi = 0;
            task->tss.edi = 0;
            task->tss.es = 0;
            task->tss.ds = 0;
            task->tss.fs = 0;
            task->tss.gs = 0;
            task->tss.ldtr = 0;
            task->tss.iomap = 0x40000000;
            return task;
        }
    }
    return 0; /* 初始化成功 */
}

  4、任务运行函数task_run

//这个函数做的事:将参数task的flag=2运行中,再将改任务的地址放到TASKCTL的tasks[]中
void task_run(struct TASK *task)
{
    task->flags = 2;               /* task的flag=2 正在运行 */
    taskctl->tasks[taskctl->running] = task; //task的地址放到ctl的tasks[]中
    taskctl->running++;             //运行中任务计数+1
    return;
}

  5、最后,任务切换task_switch()

//将now指向的运行中的任务向后移动一个
void task_switch(void)
{
    timer_settime(task_timer, 2);//任务切换的定时器
    if (taskctl->running >= 2) { //运行中的任务数>2
        taskctl->now++;      //now指向下一个运行中的任务
        if (taskctl->now == taskctl->running) {
            taskctl->now = 0;  //到达了尾部,又回到开始
        }
        farjmp(0, taskctl->tasks[taskctl->now]->sel);//JMP到now指向的新的任务的GDT
    }
    return;
}

harib13b:
  前面我们已经完成了任务管理系统的任务初始化,创建等工作。下面我们继续完善,添加任务删除(休眠)操作task_sleep.

//mtask.c
void task_sleep(struct TASK *task)
{
    int i;
    char ts = 0;
    if (task->flags == 2) {        /* 如果指定的任务处于唤醒状态 */
        if (task == taskctl->tasks[taskctl->now]) {//要休眠的是正在运行的任务
            ts = 1;          /* 让自己休眠,稍后需要进行任务切换 */
        }
        for (i = 0; i < taskctl->running; i++) {/*寻找task所在的位置 */
            if (taskctl->tasks[i] == task) {
                /* task的位置找到了 */
                break;
            }
        }
        taskctl->running--;
        if (i < taskctl->now) {
            taskctl->now--;           /* 正在运行的前移一个单位 */
        }
        for (; i < taskctl->running; i++) {//所有活跃的任务迁移一个单位
            taskctl->tasks[i] = taskctl->tasks[i + 1];
        }
        task->flags = 1; /* 不工作的状态 */
        if (ts != 0) {    //需要休眠的是正在运行的任务(自己)进行任务切换
            if (taskctl->now >= taskctl->running) {
                      /* now的值到了最后一个,跳到最前面的。 */
                taskctl->now = 0;
            }
                    //跳到GDT相应的段号。
            farjmp(0, taskctl->tasks[taskctl->now]->sel);
        }
    }
    return;
}

  接下来是FIFO中写入数据时将任务唤醒的功能,添加用于记录唤醒任务的信息成员。
  注 意:这里是有数据写入的时候就唤醒任务。

//bootpack.h
struct FIFO32 {
    int *buf;
    int p, q, size, free, flags;
    struct TASK *task;//写入数据时,需要唤醒的任务。
};
//fifo.c
void fifo32_init(struct FIFO32 *fifo, int size, int *buf, struct TASK *task)
/* FIFO缓冲区的初始化 */
{
    fifo->size = size;
    fifo->buf = buf;
    fifo->free = size;
    fifo->flags = 0;
    fifo->p = 0;     /* 写入的位置 */
    fifo->q = 0;     /* 读取的位置 */
    fifo->task = task; /* 写入数据是需要唤醒的任务 */
    return;
}

int fifo32_put(struct FIFO32 *fifo, int data)
/* FIFO中写入数据 */
{
    if (fifo->free == 0) {//没有空闲的空间了
        fifo->flags |= FLAGS_OVERRUN;
        return -1;    //返回一个错误
    }
    fifo->buf[fifo->p] = data;
    fifo->p++;
    if (fifo->p == fifo->size) {
        fifo->p = 0;
    }
    fifo->free--;
    if (fifo->task != 0) {       //有要唤醒的任务
        if (fifo->task->flags != 2) { /* 任务不是活跃状态 */
            task_run(fifo->task);   /* task_run()唤醒 */
        }
    }
    return 0;
}

harib13c:
  增加窗口的数量,增加了B0、B1、B2三个任务窗口,它们的设定相同。

void HariMain(void)
{    //....
    unsigned char *buf_back, buf_mouse[256], *buf_win, *buf_win_b;
    struct SHEET *sht_back, *sht_mouse, *sht_win, *sht_win_b[3];
    struct TASK *task_a, *task_b[3];
    //....
    /* sht_win_b */
    for (i = 0; i < 3; i++) {
        sht_win_b[i] = sheet_alloc(shtctl);
        buf_win_b = (unsigned char *) memman_alloc_4k(memman, 144 * 52);
        sheet_setbuf(sht_win_b[i], buf_win_b, 144, 52, -1); 
        sprintf(s, "task_b%d", i);
        make_window8(buf_win_b, 144, 52, s, 0);
        task_b[i] = task_alloc();
        task_b[i]->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 8;
        task_b[i]->tss.eip = (int) &task_b_main;
        task_b[i]->tss.es = 1 * 8;
        task_b[i]->tss.cs = 2 * 8;
        task_b[i]->tss.ss = 1 * 8;
        task_b[i]->tss.ds = 1 * 8;
        task_b[i]->tss.fs = 1 * 8;
        task_b[i]->tss.gs = 1 * 8;
        *((int *) (task_b[i]->tss.esp + 4)) = (int) sht_win_b[i];
        task_run(task_b[i]);
    }
    //....
}

harib13d:
  到这里,任务是以相同的速度运行的,任务切换时间都为0.02s,下面我们通过设定不同的任务切换间隔来设定任务的优先级。
  1、修改任务结构体TASK

//bootpack.h
struct TASK {
    int sel, flags; /* sel偼GDT偺斣崋偺偙偲 */
    int priority;  //这里的优先级实际上是设定了任务切换的时间。
    struct TSS32 tss;
};

  2、任务的相应的函数mtask.c

/* mtask.c节选 */
#include "bootpack.h"

struct TASKCTL *taskctl;
struct TIMER *task_timer;
struct TASK *task_init(struct MEMMAN *memman)
{    //...
    task->priority = 2; /* 0.02s */
    //...
    task_timer = timer_alloc();//设定任务切换的时间的定时器
    timer_settime(task_timer, task->priority);
    return task;
}

void task_run(struct TASK *task, int priority)
{
    if (priority > 0) {
        //初始化任务的时候,加入了设定优先级的参数。
        task->priority = priority;
    }
    if (task->flags != 2) {
        task->flags = 2; /* 摦嶌拞儅乕僋 */
        taskctl->tasks[taskctl->running] = task;
        taskctl->running++;
    }
    return;
}

void task_switch(void)
{    //.....
    //定时器时间超时才进行任务的切换
    timer_settime(task_timer, task->priority);
    if (taskctl->running >= 2) {
        farjmp(0, task->sel);
    }
    return;
}

  3、改写fifo.c中的任务唤醒

int fifo32_put(struct FIFO32 *fifo, int data)
/* FIFO傊僨乕僞傪憲傝崬傫偱拁偊傞 */
{    //.....
    fifo->free--;
    if (fifo->task != 0) {
        if (fifo->task->flags != 2) { /* 需要唤醒的任务处于非活跃状态 */
            task_run(fifo->task, 0); /* 唤醒任务,定时器为0 */
        }
    }
    return 0;
}

harib13e:

    
  我们继续来折腾优先级吧(看上图),我们将任务的优先级设计成上面的结构,最上层的LEVEL0中只要存在哪怕一个任务,则完全忽略LEVEL1和LEVEL2中的任务,只在LEVEL0中进行任务的切换。当LEVEL0中的人物全部休眠,或者全部降到下层的LEVEL,才轮到LEVEL1的任务进行切换。当LEVLE0和LEVEL1中都没有任务的时候才轮到LEVEL2出场。下面我们来看看具体的做法。

  1、修改任务结构体的定义

//bootpack.h
//对于每个LEVEL我们最多允许创建100个任务,一共10个LEVEL
#define MAX_TASKS        1000    /* 任务总数 */
#define TASK_GDT0        3       /* TSS放到GDT的3号段 */
#define MAX_TASKS_LV    100     //任务指针
#define MAX_TASKLEVELS    10    //任务级别数量
struct TSS32 {
    int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
    int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
    int es, cs, ss, ds, fs, gs;
    int ldtr, iomap;
};
struct TASK {
    int sel, flags; /* sel用来存放GDT的编号*/
    int level, priority;
    struct TSS32 tss;
};
struct TASKLEVEL {
    int running;   /*正在运行的任务的数量 */
    int now;     /* 记录当前运行的任务*/
    struct TASK *tasks[MAX_TASKS_LV];//100个任务指针
};
struct TASKCTL {
    int now_lv;    /* 当前活动中的LEVEL */
    char lv_change; /* 标志:下次切换任务时,是否需要改变LEVEL */
    struct TASKLEVEL level[MAX_TASKLEVELS];//LEVEL一共10级
    struct TASK tasks0[MAX_TASKS];     //任务总数
};

  2、我们来写几个用于操作struct TASKLEVEL的函数

//mtask.c
struct TASK *task_now(void)
{    //用来返回现在活动中的TASK的地址
    struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];
    return tl->tasks[tl->now];
}
void task_add(struct TASK *task)
{    //向TASKLEVEL中添加一个任务
    struct TASKLEVEL *tl = &taskctl->level[task->level];//当前任务的TASKLEVEL
    tl->tasks[tl->running] = task;//把任务task添加到task后面
    tl->running++;          //正在运行的数量+1
    task->flags = 2;         /* 活动中的标识 */
    return;
}
void task_remove(struct TASK *task)
{    //从TASKLEVLE中删除一个任务
    int i;
    struct TASKLEVEL *tl = &taskctl->level[task->level];//获取要删除task的TASKLEVEL

          /* task的位置 */
    for (i = 0; i < tl->running; i++) {
        if (tl->tasks[i] == task) {
            /* 找到了task出去 */
            break;
        }
    }
    tl->running--;
    if (i < tl->now) {
        tl->now--; /* 需要删除的task在now的前面 */
    }
    if (tl->now >= tl->running) {
              /* now到了任务的最后一个,又跳到头部 */
        tl->now = 0;
    }
    task->flags = 1; /* 任务标识=1 */
              /* 都往前移动一个 */
    for (; i < tl->running; i++) {
        tl->tasks[i] = tl->tasks[i + 1];
    }
    return;
}
void task_switchsub(void)
{    //用来在任务切换时,决定接下来切换到那个LEVEL
    int i;
     /* 寻找最上层的LEVEL */
    for (i = 0; i < MAX_TASKLEVELS; i++) {
        if (taskctl->level[i].running > 0) {
            break; /* 找到了 */
        }
    }
    taskctl->now_lv = i;
    taskctl->lv_change = 0;
    return;
}

  3、改写任务TASK操作函数

struct TASK *task_init(struct MEMMAN *memman)
{    //初始化函数,开始只有LEVEL0中有任务
    int i;
    struct TASK *task;
    struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;
    taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));
    for (i = 0; i < MAX_TASKS; i++) {          //初始化TASKCTL中的每一个任务,并写到GDT相应的段号
        taskctl->tasks0[i].flags = 0;
        taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;//GDT段号
        set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);
    }
    for (i = 0; i < MAX_TASKLEVELS; i++) {       //初始化LEVEL
        taskctl->level[i].running = 0;
        taskctl->level[i].now = 0;
    }
    task = task_alloc();//在LEVLE0中初始化一个任务
    task->flags = 2;    /* 活动中 */
    task->priority = 2; /* 0.02s */
    task->level = 0;    /* LEVEL的值 */
    task_add(task);     //添加到TASKLEVEL中
    task_switchsub();   /* 初始化LEVLE */
    load_tr(task->sel); //初始化TR寄存器
    task_timer = timer_alloc();//定时器
    timer_settime(task_timer, task->priority);
    return task;
}
void task_run(struct TASK *task, int level, int priority)
{    //在参数中指定LEVEL的值
    if (level < 0) {
        level = task->level; /* 不改变LEVEL(不能为负) */
    }
    if (priority > 0) {   //优先级设置
        task->priority = priority;
    }
    if (task->flags == 2 && task->level != level) { /* 改变活动中的LEVEL */
        task_remove(task);   /* 将TASK移除,flag=1执行下面的任务 */
    }
    if (task->flags != 2) {
        /* 唤醒任务 */
        task->level = level;
        task_add(task);   //把任务添加到活动中
    }
    taskctl->lv_change = 1; /* 下次任务切换时检查LEVEL */
    return;
}
void task_sleep(struct TASK *task)
{   //调用task_remove简化代码
    struct TASK *now_task;
    if (task->flags == 2) {
        /* 如果处于活动状态 */
        now_task = task_now();//当前运行的任务地址
        task_remove(task);   /* 执行后FLAG=1 */
        if (task == now_task) {
            /* 让自己休眠,需要进行切换 */
            task_switchsub();
            now_task = task_now();   /* 设定后,获取当前的任务 */
            farjmp(0, now_task->sel);//任务切换,GDT号
        }
    }
    return;
}

  4、最后修改fifo.c任务唤醒

int fifo32_put(struct FIFO32 *fifo, int data)
/* FIFO接收到数据时,将任务唤醒 */
{    //....
    fifo->free--;
    if (fifo->task != 0) {         //fifo中有需要唤醒的任务
        if (fifo->task->flags != 2) {   /* 唤醒到活跃状态 */
            task_run(fifo->task, -1, 0);  /* 修改了RUN的参数传递调用 */
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/pengfeiz/p/5848463.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值