请选择 进入手机版 | 继续访问电脑版

EDABOSS电子论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1056|回复: 0

[转帖] ucos原理--任务简介

[复制链接]

18

主题

0

回帖

54

E币

技术员

Rank: 2

积分
36
发表于 2017-4-1 15:19:46 | 显示全部楼层 |阅读模式
本以为做个读书笔记,谁知不懂的太多,抄书好了。
来源:《嵌入式实时操作系统-uc/os原理与实践》   卢友亮 编著


一:操作系统
1:操作系统基本功能:
     任务管理
    CPU管理
   内存管理 ----给任务分配内存空间,内存结束后释放内存空间
   文件管理----对文件存储器的存储空间进行组织,非配和回收,负责文件的存储,检索,共享和保护
I/O设备管理


第二章:  实时操作系统(RTOS)
一:概述:
(一):定义
是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统做出快速响应,调度一切可利用的资源完成实时任务,并控制所有实时任务协调一致运行的操作系统。


硬实时操作系统:要求在规定的时间内必须完成操作
软实时操作系统:只要按照任务的优先级,尽可能快地完成操作


(二):特征:

1:多任务:系统中多个任务同时运行。当时cpu只有一个,在某一个时刻,只有一个任务占有cpu,因此,多任务操作系统的核心任务之一是任务调度,为任务分配cpu时间。

2:多级中断机制:
以确保对紧迫程度较高的实时时间进行及时响应和处理----例如:温度超高的报警先处理


3:优先级调度机制:
越紧迫的任务优先级越高
任务管理模块必须根据优先级调度任务,而又能保证任务在切换过程中不被破坏。



(三):任务

1:任务简介
任务有 睡眠,就绪,运行,阻塞,挂起等多种状态
典型的任务运行方式是循环。

例1:
标准的任务结构:(用户任务)
void   usertask(void *pParam)    //参数是指针类型的
{
   int  i=*((int *)pParam);             //将指针强制转换为指向整数类型的指针,然后取改指针所指的内容,将它赋值给一个局部变量i

for( ; ; )                                    //进入无限循环
printf("%d",i++);          //在循环体打印输出当前的计数值i


OSTimeDly(OS_TICKS_PER_SEC);    //调用操作系统演示函数,延时1s。1s后又可以获得运行,继续循环
}


注:
OSTimeDly首先将本任务 阻塞掉,这样,即使该任务优先级最高,也可以让低优先级的任务获得运行的机会。操作系统1s后使这个任务重新就绪,重新得到运行。




2:多任务:
优点:可以大大提高cpu的利用率,使应用程序分成多个程序模块,实现模块化,应用程序更易于设计和维护

例如:在ARM采集处理系统中,同时采集16路信号,又同时对多信号进行处理和传输,
可以创建16个任务负责16路信号的采集,
创建一个任务对信号进行处理,
再创建一个任务负责数据的传输。

例:2:
int main(int argc, char **argv)
{
int  p[2];
p[0]=0;
p[1]=100;

OSInit();                                   //对ucosii内核进行初始化
OSTaskCreate(TaskStart,0,&TaskStk[0][TASK_STK_SIZE-1],TaskStart_Prio);   //创建一个设置时钟中断的任务

//创建用户任务,任务代码是例1的usertask,参数是p,即指向p[0]的指针,优先级是5
OSTaskCreate(
usertask,p,&TaskStk[2][TASK_STK_SIZE-1],5);  
//创建用户任务,任务代码是例1的usertask,参数是p+1,即指向p[1]的指针,优先级是6
OSTaskCreate(usertask,p+1,&TaskStk[3][TASK_STK_SIZE-1],6);
OSStart();        //开始启动多任务
return 0;

}




分析:
两个用户任务的优先级虽然不同,但是都执行延时操作,也就是说主动放弃cpu,因此可以轮流运行。
(如果没有执行延时操作,把自己阻塞起来,在高优先级任务结束前,低优先级的任务是不能得到运行的)


运行结果:  第一个任务的参数是0,从0开始计数,第二个任务的参数是100,从100开始计数。
两个任务轮流输出结果到屏幕,多任务被调度运行了。

0
100
1
101
2
102
3
103


3:任务状态:


6591730194407.jpg



1:睡眠态:
在调用 OSTaskCreate(任务创建函数)创建之前,处于睡眠状态。   
睡眠状态的任务是不会运行的,操作系统也不会为他设置运行而准备的数据结构,没有给它配置任务模块。
2:就绪态
当操作系统调用 OSTaskCreate()创建一个任务后,任务进入就绪态。
图中可以看到,任务也可以从其他状态进入就绪态。

处于就绪态的任务 :操作系统已经为其运行配置好了任务控制模块等数据结构。
当没有比它优先级更高的任务或者优先级更高的任务处于阻塞态的时候,就可以被系统调度进入运行态,操作系统是调用任务切换函数完成的。


3:运行态:
运行态是任务真正占有cpu,得到运行。这时运行的代码就是任务的代码,如usertask。

处于运行态的任务如果运行完成,就会进入睡眠态。
如果更高优先级的任务抢占了cpu,就会转到就绪态。

如果 因为等待某一事件,例如等待1s的时间,OSTimeDly(OS_TICKS_PER_SEC); 需要暂时放弃cpu的使用权而让其他任务得到运行,就进入阻塞态。

当由于中断的到来而使cpu进入中断服务函数ISR,必然使正在运行过得任务放弃cpu而转入中断服务程序,这是被中断的程序就会被挂起而进入挂起态。


4:阻塞态:
阻塞态对于操作系统的调度、任务的协调运行时很重要的。
图中所示:并不是只有一个高优先级的任务在运行,是因为usertask在没有事情可做,在等待1s的时候,不是强行运行代码,而使把自己阻塞起来,使操作系统可以调度其他的任务。OSTimeDly(OS_TICKS_PER_SEC);

当任务在等待某些还没有被释放的资源或等待一定时间的时候,要阻塞起来,等到条件满足的时候再重新回到就绪态,又能被操作系统调度以进入运行态。这是实时操作系统必须实现的功能之一。

注意:错误:一些不理解操作系统的读者编程时候,在等待的时候常常使用for循环,不停的执行代码而使cpu的利用率暴增,使系统的运行,甚至造成死机,十分恶劣。这样不可取。

5:挂起态:
当任务在运行时,因为中断的发生(例如定时器中断每个时钟滴答)中断一次,被剥夺cpu的使用权而进入挂起状。在中断返回的时候,若该任务还是最高优先级的,则恢复运行。
如果不是这样,就回到就绪态。



4:任务切换:-----Context Switch
任务切换时暂停一个任务的运行,云更新另一个处于就绪态的任务,暂停一个任务,以后又能恢复运行,必须考虑将这个任务的信息保存,而恢复运行的时候需要将这些信息恢复到运行环境。

因此,任务切换必须做环境的保存和恢复的操作。
环境的保存和恢复与任务有关,业余任务运行的硬件环境有关(PC和ARM有不同的寄存器,所以不同,因此涉及到汇编语言实现的最底层代码。---因此需要一定的计算机原理或者嵌入式系统的基本知识。


多任务的关键在于.如何将调度,ucos采用的是可剥夺优先级调度算法。
多任务下,个任务还要按照一定的次序运行,因此存在同步问题,因此引入了信号量的改年来进行同步。

人物间有相互通信的需求,因此操作系统需要有邮箱、消息等用于通信的数据结构,以便多任务通信

多个任务可能真多有限的资源而不发生冲突,因此操作系统还需要管理各任务,使他们能够充分利用资源而不发生冲突,因此又产生了互斥,死锁等概念。





5:可重入函数  与   不可重入函数  (任务应该调用可重入函数)
可重入函数: 指一个函数可以被多个任务调用。(不需要担心在任务切换的过程中,代码的执行会掺和啥呢个错误的结果)
不可重入函数:如果可能产生错误的结果,就是不可重入函数。


例3:
启动两个用户任务:usertask1和usertask2    两个任务都调用add2函数(将返回两个参数相加的结果),使用了全局变量a和b。

void usertask1(void *pParam)
{
int sum;
for( ; ; )
   {
      printf("task%  call add2(1,2)",1);
  sum=add2(1,2);
printf("task%  call add2(1,2)  solution is %d",1,sum);
   }
}



void usertask2(void *pParam)
{
int sum;
for( ; ; )
   {
      printf("task%  call add2(100,200)",2);
  sum=add2(100,200);
printf("task%  call add2(100,200)  solution is %d",2,sum);
   }
}




a:
使用不可重入函数得到的错误结果:

int a,b;                                    //定义全局变量,各个任务都可以访问的全局变量
int add2(int p1,int,p2)       //函数add2()实现对两个参数相加,返回结果,因为使用了全局变量,所以在任务切换过程中,他得知可能改变
{
a=p1;
b=p2;
OSTimeDly(OS_TICKS_PER_SEC);          //延时1s任务被阻塞,以保证任务被切换
return(a+b);                                          //返回相加结果
}



运行结果:
task1 call add2(1,2)  solution is 300

task2 call add2(100,200)  solution is 3

task1 call add2(1,2)  solution is 300

task2 call add2(100,200)  solution is 3

分析:
结果完全错误。
usertask1的优先级为5,usertask2的优先级为6。
操作系统首先调度usertask1运行,对变量a、b进行赋值,运行结果为a=1,b=2.
然后调用OSTimeDly(OS_TICKS_PER_SEC);延时,usertask1被阻塞,操作系统进行一次任务切换。
usertask2得到寻衅,对a,b进行赋值,运行结果为a=100,b=200.
在经过一段延时后,usertask1被唤醒,重新得到运行,返回a+b的结果,于是有了1+2=300的错误结果。

打印出第一行后,usertask1仍然要进行循环,于是又调用add2,又给a、b进行赋值,运行结果为a=1,b=2,
再延时,进入usertask2延时结束得到运行,打印出了第二行100+200=3的错误结果。






b:使用可重入函数得到正确结果


                              
int add2(int p1,int,p2)       //函数add2()实现对两个参数相加,返回结果,因为使用了全局变量,所以在任务切换过程中,他得知可能改变
{
int a,b;  

a=p1;
b=p2;
OSTimeDly(OS_TICKS_PER_SEC);          //延时1s任务被阻塞,以保证任务被切换
return(a+b);                                          //返回相加结果
}



运行结果:
task1 call add2(1,2)  solution is 3

task2 call add2(100,200)  solution is 300

task1 call add2(1,2)  solution is 3

task2 call add2(100,200)  solution is 300



分析:
结果正确。
原因是add2函数采用了可重入代码----------因此在实时多任务操作系统中,公用函数口用该采用可重入代码。
积分规则
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|EDABOSS电子论坛

GMT+8, 2024-4-19 07:38 , Processed in 0.044356 second(s), 23 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表