小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     try

11     {

12         App.Init();  //初始化APP

13         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);  //  恢复任务 2

67         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   XXXXX

11  *        OS:MyOS V1.0 Bete

12  *        TinyBooter:V1.0 Bate

13  *        App:V1.0 Bate

14  *        Date:18th Sep,2004

15  *        Author:BigBear

16  * Time:2014-9-18 17:12:05

17  * 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位,就是乘以1024

6     // 拷贝一部分栈内容到新栈

7     memcpy((void*)(top - 0x40), (void*)p, 0x40);


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

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

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

先写到这里。



End!

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




                            

你可能感兴趣的:(操作系统)