嵌入式工程师实现一个简单的操作系统(五)

以嵌入式工程师的角度来实现一个简单的操作系统!

1. 前言

上一节我们已经实现了非常简单的操作系统任务创建、任务调度、任务切换等。你看出来了吗?

我们在简单的回顾一下这些功能是怎么对应的?

任务创建:

/* 创建一个任务 */
task_init(&taska, taska_stack + sizeof(taska_stack), taska_fun);

任务调度与任务切换

/* 在定时器中断中,进行任务切换
 * 现在的调度策略:一共2个任务,轮流执行,每次在定时中断中切换到另一个任务
 * 非常
 */
void timer_handler(struct irq_desc *desc)
{
    arch_timer_compare(arch_timer_frequecy());
    log_i("arch timer_handler!");

    if((count++) % 2 == 0)
    {
        log_i("switch to taskb!");
        interrupt_task_switch_from_to(&taska, &taskb);
    }
    else
    {
        log_i("switch to taska!");
        interrupt_task_switch_from_to(&taskb, &taska);
    }
}

上面的任务切换逻辑,非常简单,但是效率也是非常的低!

2. 优化任务调度的设计

2.1 主要问题

我们上面的程序存在几个缺点:

  • 任务调度切换时,是人为规定的策略,当添加新的任务之后,还需要修改切换策略
  • 任务信息结构体是分散定义的,在进行切换时需要统一管理和查找,需要优化任务结构
  • 任务调度的策略不能是简单的平均执行,而是应该根据优先级、时间片的用户需求,做出一定的选择

2.2 优化方向与目标

目前我们优先实现前面2个存在的问题,实现以下2个目标:

  1. 创建多个任务时、无需修改任务调度的代码
  2. 创建多个任务时,可以让每个任务都被执行

2.3 实现方向

我们需要确保在进行任务切换的时候,找到所有的任务、遍历所有的任务

我们将定义一个全局变量数组,数组的元素就是任务结构体。我们创建任务的时候填充数组元素,在进行任务切换时,遍历数组,查找任务

在遍历任务数组的时候,我们需要通过一些方式来当前的元素是否是有效的任务,毕竟我们不一定会每一个任务数组的元素都定义了任务。假设我们定义任务数组的个数是64个,时间创建任务是4个,此时就存在60个任务数组是没有任务的。我们只需要在4个有效任务中进行切换即可。

如何区分任务是否是有效的任务,可以通过多种方式,比如在任务结构体中添加一个成员叫做pid任务编号。当任务无效的时候设置pid=-1,当创建任务时,从全局的任务数组中分配一个任务结构体,然后分配一个唯一的大于等于0的pid号。作为有效任务的标记。

3. 代码编写

  1. 为任务信息结构体添加一个pid成员变量
struct task
{
    void *sp;               // sp
    unsigned long spsr;     //程序状态寄存器
    unsigned long elr;      //异常返回寄存器

    long pid;               //任务id,非负数时表示有效任务
};
  1. 定义全局变量,任务信息结构体数组
#define G_TASK_NUMBER 64
struct task g_task[G_TASK_NUMBER];
  1. 初始化全局任务信息
/**
 * @brief 初始化全部任务为无效任务
 *
 * @return int 支持的任务数量
 */
int global_task_config(void)
{
    int i = 0;

    for(i; i < G_TASK_NUMBER; i++)
    {
        g_task[i].elr = 0;
        g_task[i].sp = 0;
        g_task[i].spsr = 0;
        g_task[i].pid = -1;
    }

    return i;
}
  1. 修改创建任务实现,需要从全局变量获取任务
//1. 定义一个获取空闲任务和释放空闲任务得到函数
//2. 创建任务时候需要获取任务,删除任务的时候需要释放任务

/**
 * @brief 请求获取一个空闲任务结构体
 *
 * @return struct task* 申请到的任务
 */
struct task *requset_task()
{
    for(int i = 0; i < G_TASK_NUMBER; i++)
    {
        if(g_task[i].pid > 0)
        {
            //只要保证各个任务的pid非负并且不重复就可以
            g_task[i].pid = i;
            return &g_task[i];
        }
    }

    return NULL;
}

/**
 * @brief 将指定的任务释放掉,变为空闲状态
 *
 * @param free
 * @return long
 */
long free_task(struct task *free)
{
    long pid = free->pid;
    free->pid = -1;

    return pid;
}

//3. 从全局任务结构体去创建任务
/**
 * @brief 从全局任务信息中创建一个任务,返回任务的pid
 *
 * @param sp_addr
 * @param pc_addr
 * @return long
 */
long task_create(void *sp_addr, void *pc_addr)
{
    struct task *new_task;

    new_task = requset_task();
    if(new_task != NULL)
    {
        task_init(new_task, sp_addr, pc_addr);
        return new_task->pid;
    }
    else
    {
        printf("error: can not get task!\n");
        return -1;
    }
}
  1. 实现自动调度 功能
今天有点晚了,还没有想好怎么实现。明天继续研究一下

欢迎大家持续关注,作为一个小白,我会一步步去实现一些操作系统的基本功能,逐渐丰富

在学习过程中,也会参考一些现成的RTOS或者操作系统的相关实现,比如rtthread等,可以提供一些灵感

在学习中遇到问题欢迎私信一起交流

代码在github同步开源开发,如果无法浏览github代码,可以关注后在后台联系,提供获取方式

附项目链接:https://github.com/jhbdream/armv8_os

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值