首页 > 代码库 > 小X教你写嵌入式操作系统之------(一)多任务抢占调节机制

小X教你写嵌入式操作系统之------(一)多任务抢占调节机制

/****************************************************

Title: 嵌入式系统多任务抢占机制

Framework:MyOS V 1.0 Bate

Date:2014-9-18 21:31:54

Author:小X

Remark:ARM实现系统任务的调度

*****************************************************/

 

       今天我给大家带来的是如何理解嵌入式系统多任务机制.

       我们先来写一个多任务调节主架构。

 1 /********************************************************** 2  *  多语言嵌套写嵌入式系统框架 3  *  C/C++/C# 4  *  嵌入式系统,单片机的系统原理其实和PC类似,但是PC系统比单片机复杂上千上万倍。 5  *  本来是写C的,后来使用C++和C#大量语法,搞的代码有点不伦不类望见谅。 6  * ********************************************************/ 7 /// <summary>程序入口</summary> 8 int Main(int argc, char argv[]) 9 { 10     try11     {12         App.Init();  //初始化APP13         MyOS.Init();  //操作系统初始化14         MyOS.ShowInFo(); //操作系统信息15         Debug.Print(DateTime.Now.ToString());16         Debug.Print("All OK!");17         MyOS.TaskCreate(Task0,&StackTask0[StackSizeTask0-1],PrioTask0); //  创建一个任务18         Debug.Printf("Ready to start MyOS!\r\n\r\n"); 19         MyOS.Start();      //开始运行操作系统20         return 0;  21     } 22     catch (Exception ex)23     {24         Debug.Print(ex.Message);25     }26     while (true) { Thread.Sleep(1000); }27 } 28 void Task0(void) 29 { 30     App.Start();  //设置中断向量,启动操作系统的硬件定时器中断31     Debug.Printf("Start MyOS!\r\n"); 32     //  创建其他任务33     MyOS.TaskCreate(Task1,&StackTask1[StackSizeTask1 - 1],PrioTask1); 34     MyOS.TaskCreate(Task2,&StackTask2[StackSizeTask2 - 1],PrioTask2); 35     MyOS.TaskCreate(Task3,&StackTask3[StackSizeTask2 - 1],PrioTask3); 36     while(1) 37     { 38         Debug.Printf("Task0\r\n"); 39         MyOS.TimeDly(2000);    //     2 秒运行一次40     } 41 } 42 void Task1(void) 43 { 44     while(1) 45     { 46         Debug.Printf("Task1\r\n"); 47         MyOS.TimeDly(4000);  //   4 秒运行一次48     } 49 } 50 51 void Task2(void) 52 { 53     while(1) 54     {55         Debug.Printf("Task2\r\n"); 56         MyOS.TimeDly(1000);  //   1 秒运行一次57         Debug.Printf("Suspend Task2!\r\n"); 58         MyOS.TaskSuspend(PrioTask2);  //   进入挂起状态59     } 60 } 61 void Task3(void) 62 { 63     while(1) 64     {65         Debug.Printf("Resume Task2!\r\n"); 66         MyOS.TaskResume(PrioTask2);  //  恢复任务 267         MyOS.TimeDly(10000); 68     } 69 }

 

整体语法可多错误,我们不必在意这些细节。

系统过程分析:

我们整个系统创建了四个任务处理,任务 0 每 2 秒运行一次,

                任务 1 每 4 秒运行一次,

                任务 2 每 1 秒运行一次,

                然后即把自己挂起,

                任务 3 每 10 秒运行一次,

                并把任务 2 恢复。

/***********************先预测一下结果************************/

 

 1 /******************终端显示效果************************* 2  * Erase Done...... 3  * Erase OK. 4  * Dowload...... 5  * Dowload OK. 6  * Verify...... 7  * Verify OK. 8  * Application running ...... 9  * ......................................................10  * Info:  Hardware   XXXXX11  *        OS:MyOS V1.0 Bete12  *        TinyBooter:V1.0 Bate13  *        App:V1.0 Bate14  *        Date:18th Sep,200415  *        Author:BigBear16  * Time:2014-9-18 17:12:0517  * ALL OK!18  * Ready to start MyOS!19  * Start MyOS!20  * 21  * Tesk0;22  * Tesk1;23  * Tesk2;24  * Suspend Task2!25  * Resume Task2!26  * Tesk2;27  * Tesk0;28  * Tesk0; 29  * Tesk1;30  * Tesk0;31  * Tesk0;32  * Tesk1;33  * Tesk0;34  * Tesk2;35  * Suspend Task2!36  * Resume Task2!37  * Tesk2;38  * Tesk0;39  * Tesk0;40  * Tesk1;41  * Tesk0;42  * Tesk0;43  * Tesk1;44  *   *45  *   *46  *   *   47  *   *48  *   (循环中。。。。。。)   49  *   *50  *   * 51 ***************************************************/
什么协同多任务系统?

    在我们电脑中,腾讯让我们让成了开机登QQ的习惯,QQ登陆的过程中我们打开工作邮箱,这是QQ登录相当于任务A初始化,FoxMail相当于B初始化,我们习惯打开一下浏览器看看救赎论坛,这属于任务C。
其实单核CPU里面,这些任务是单一工作的。不存在真正的多任务机制,每个任务同一时间都使用同一个CPU是不可能的。所有单一CPU是单一任务的。但是我们操作系统是通过任务调度来实现多个任务执行。通过CPU快速切换,达到让人感觉像是多个任务同时运行一样。这个就是操作系统的多任务调度机制。
现在的计算机硬件系统中,CPU通常是两个或多核CPU。可以真正意义上同时处理多个任务。这些是X86等复杂CPU以及操作系统来协同调节任务。

  多任务的好处就是可以充分利用我们的硬件资源。在单片机中,硬件资源非常简单。多任务的机制显得更为重要。在之前的裸机程序中,我们的都是处理一件事情的时候就让他Delay一段时间。CPI在那里空转(等待),这样超级浪费CPU资源。但是我们加入任务调节机制之后,我们会让程序自己等待着,CPU去执行下一个任务,等条件满足后(比喻延时时间到了),我们在将这个任务挂载寄来去处理前面的事情。处理了之后再回来继续开始手上的事情。这样充分利用CPU。大大提高了应用的执行效率。
抢占式多任务处理调节机制。 


什么是任务的抢占式调节呢?
当系统在按任务执行时。多个任务同时请求。我们的系统到底应该执行哪一个任务呢?当然我们一般处理方式是当同时有多个任务响操作系统发出请求时,谁的任务优先级最高我们就去执行谁的程序。

我们先会定义一大堆的任务优先级别:
 1 public enum MultiTask:uint 2 { 3     PTesk0=0; 4     PTesk1=1; 5     PTesk2=2; 6     PTesk3=3; 7     PTesk4=4; 8     PTesk5=5; 9     PTesk6=6;10     PTesk7=7;11     PTesk8=8;12     PTesk9=9;    13 } ;

 

我们把每一个任务分别分配唯一的一个优先级。

 

 1 void TSys::Start() 2 { 3     debug_printf("系统准备就绪,开始循环处理%d个任务!\r\n", _TaskCount); 4  5     _Running = true; 6     while(_Running) 7     { 8         ulong now = Time.Current();    // 当前时间 9         int k = 0;10         for(int i=0; i < ArrayLength(_Tasks) && k < _TaskCount; i++)11         {12             Task* task = _Tasks[i];13             if(task)14             {15                 if(task->NextTime <= now)16                 {17                     // 先计算下一次时间18                     //task->NextTime += task->Period;19                     // 不能通过累加的方式计算下一次时间,因为可能系统时间被调整20                     task->NextTime = now + task->Period;21                     task->Callback(task->Param);22 23                     // 如果只是一次性任务,在这里清理24                     if(task->Period < 0)25                     {26                         _Tasks[i] = NULL;27                         delete task;28                         _TaskCount--;29                     }30                 }31 32                 k++;33             }34         }35     }36     debug_printf("系统停止调度,共有%d个任务!\r\n", _TaskCount);37 }

 

这个调节机制做的比较好。非严格情况下可以说是实时系统。实时操作系统的做法都是先预测任务的延时时间。

通常在ms级别之类对任务请求作出反应。但是任务的改变会带来延时的复杂度。

 

我们的任务在还没有运行或者被Delete时,它的数据或者寄存器都会先保存在私有栈中。在任务切换时,都是按顺序入栈出栈。      

 任务切换原理:我们先把当前的任务现场状况保存在自己的私有栈中,再把将要进行的数据从内存的栈中转移到CPU上面去。

主要是改变下面的三个值来改变。

堆栈指针r13(SP)   程序把寄存器数据压入堆栈,返回时再出栈,保证了程序的完整性。

连接寄存器r14(LR)  保存子程序返回地址。当程序发生异常异常模式LR用来保存异常返回地址,处理栈中断。

程序计数器r15(PC)  计数功能

这三个参数传给CPU,CPU通过他们进行新旧任务切换。

 

过程如下:


 


 

 

  旧任务挂载


 

①:处理器PC指针压栈到任务堆栈

②:SP指针保存到任务控制块

 

新任务启动:


 

①:任务控制块载入到SP指针。

②:新任务的私人堆栈中PC指针出栈。

 

新旧任务切换恰好是两个相反的过程。


我们再来写写任务调换实现函数代码:
 1 void MyOSChange (void)  2 {  3     MyOS.Open();  4     MyOS.High.Find();         // 找出就绪表中优先级最高的任务  5     if(MyOS.Tesk.High != MyOS.Ontime.High)  //  如果不是当前运行的任务,进行任务调度  6     {  7         p_spAdd  = &sp[MyOS.Ontime.High];  //  取得栈顶指针  8         p_spHigt = &sp[MyOS.Tesk.High];  9         MyOS.Ontime.High= MyOS.Tesk.High;    // 更新10         MyOS.Tesk    ();          //  调度任务 11     } 12     MyOS.Exit();13 } 

入栈出栈函数都在汇编里面。我们不做详细讲解

1 uint* p = (uint*)__get_MSP();2 3     // 直接使用RAM最后,需要减去一点,因为TSys构造函数有压栈,待会需要把压栈数据也拷贝过来4     uint top = SRAM_BASE + (ramSize << 10);5     __set_MSP(top - 0x40);    // 左移10位,就是乘以10246     // 拷贝一部分栈内容到新栈7     memcpy((void*)(top - 0x40), (void*)p, 0x40);


抢占式任务调节我们最高优先级的任务需要准备的时候。立即抢占正在运行任务的资源。
抢占优先级通俗的理解就是动态调节任务优先级别。

高优先级的任务有时候不需要CPU资源了。他就会主动请求自己挂起,然后CPu打开时钟滴答,调节任务优先级状态,执行当前状态下最高优先级的任务。

当时机滴答计数完还没有完。如果发现还有更高的优先级任务。CPU会切换更高优先级任务。这是嵌套的任务切换调节。

先写到这里。



End!

欢迎大家一起交流 ,分享程序员励志故事。   幸福的程序员 QQ群:幸福的程序员 


小X教你写嵌入式操作系统之------(一)多任务抢占调节机制