欢迎来到装配图网! | 帮助中心 装配图网zhuangpeitu.com!
装配图网
ImageVerifierCode 换一换
首页 装配图网 > 资源分类 > DOCX文档下载
 

RT-Thread龙芯移植技术文档分析

  • 资源ID:64487538       资源大小:1.42MB        全文页数:151页
  • 资源格式: DOCX        下载积分:12积分
快捷下载 游客一键下载
会员登录下载
微信登录下载
三方登录下载: 微信开放平台登录 支付宝登录   QQ登录   微博登录  
二维码
微信扫一扫登录
下载资源需要12积分
邮箱/手机:
温馨提示:
用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
支付方式: 支付宝    微信支付   
验证码:   换一换

 
账号:
密码:
验证码:   换一换
  忘记密码?
    
友情提示
2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

RT-Thread龙芯移植技术文档分析

第一章RT-THREA嗡介RT-Thread是一款来自中国的开放源代码实时操作系统,并且是一款商业许可证非常宽松的实时操作系统。下图是RT-Thread及外围组件的基本框架图:FinSH ShellRT-Device FilesystemRT-虚拟文件系统LwIP轻型TCP/IP协议栈RT-Thread/GUI图形用户界面RT-ThreadKernel实时内核:srcObjectManagement对象管理:object.cKernel Library 内核:kservice.cRealtimeScheduler实时调度器:schedule.cThreadManagement线程管理:thread.cInternalThreadCommunication线程间通信:ipc.cClockManagement时钟管理:clock.ctimer.cDeviceDriverIO设备驱动:device.cCPUArchitecture芯片移植:libcpuBoardSupportPackage板级支持包:bspHardware硬件,CPU/SRAM/Flash/UART/EMACetcRT-ThreadKernel内核部分包括了RT-Thread的核心代码,包括对象管理器,线程管理及调度,线程间通信等的微小内核实现(最小能够到达2.5kROM,1kRAM体积占用)。内核库是为了保证内核能够独立运作的一套小型类似实现(这部分根据编译器自带C库的情况会有些不同,使用GC编译器时,携带更多的标准C库实现)。CPIM板级支持包包含了RT-Thread支持的各个平台移植代码,通常会包含两个汇编文件,一个是系统启动初始化文件,一个是线程进行上下文切换的文件,其他的都是CM文件。1.1 实时内核1.1.1 任务/线程调度在RT-Thread中线程是最小的调度单位,线程调度算法是基于优先级的全抢占式多线程调度算法,支持256个线程优先级(也能通过配置文件更改为最大支持32个或8个线程优先级,针对STM32B常配置是32个线程优先级),0优先级代表最高优先级,255优先级留给空闲线程使用;支持创建相同优先级线程,相同优先级的线程采用可设置时间片的轮转调度算法;调度器寻找下一个最高优先级就绪线程的时间是恒定的(时间复杂度是1,即O(1)。系统不限制线程数量的多少,只和硬件平台的具体内存相关。1.1.2 任务同步机制系统支持信号量、互斥锁作为线程间同步机制。互斥锁采用优先级继存方式以解决优先级翻转问题。信号量的释放动作可安全用于中断服务例程中。同步机制支持线程按优先级等待或按先进先出方式获取信号量或互斥锁。1.1.3 任务问通信机制系统支持事件、邮箱和消息队列等通信机制。事件支持多事件”或触发“及“与触发",适合于线程等待多个事件情况。邮箱中一封邮件的长度固定为4字节,效率较消息队列更为高效。通信设施中的发送动作可安全用于中断服务例程中。通信机制支持线程按优先级等待或按先进先出方式获取。1.1.4 时间管理系统使用时钟节拍来完成同优先级任务的时间片轮转调度;线程对内核对象的时间敏感性是通过系统定时器来实现的;定时器支持软定时器及硬定时器(软定时器的处理在系统线程的上下文中,硬定时器的处理在中断的上下文中);定时器支持一次性超时及周期性超时。1.1.5 内存管理系统支持静态内存池管理及动态内存堆管理。从静态内存池中获取内存块时间恒定,当内存池为空时,可把申请内存块的线程阻塞(或立刻返回,或等待一段时间后仍未获得内存块返回。这取决于内存块申请时设置的等待时间),当其他线程释内存块到内存池时,将把相应阻塞线程唤醒。动态堆内存管理对于不同的系统资源情况,提供了面向小内存系统的管理算法及大内存系统的SLA汕存管理算法。1.1.6 设备管理系统实现了按名称访问的设备管理子系统,可按照统一的API界面访问硬件设备。在设备驱动接口上,根据嵌入式系统的特点,对不同的设备可以挂接相应的事件,当设备事件触发时,通知给上层的应用程序。1.2 虚拟文件系统RT-Thread提供的文件系统称为设备文件系统,它主要包含了一个非常轻型的虚拟文件系统。虚拟文件系统的好处就是,不管下层采用了什么文件系统,例如内存虚拟文件系统,FAT3蚁件系统还是YAFFS闪存文件系统,对上层应用程序提供的接口都是统一的。1.3 轻型IP协议栈LwIP是瑞士计算机科学院(SwedishInstituteofComputerScience)的AdamDunkels等开发的一套用于嵌入式系统的开放源代码TCP/IP协议栈,它在包含完整的TCF议实现基础上实现了小型的资源占用,因此它十分适合于使用到嵌入式设备中,RT-Thread采用LwIP做为默认的TCP/IP协议栈,同时根据小型设备的特点对其进行再优化,体积相对进一步减小,RAM占用缩小到5kB附近(依据上层应用使用情况会有浮动)。1.4 shell系统RT-Thread的shell系统一一FinSH,提供了一套供用户在命令行操作的接口,主要用于调试、查看系统信息。由于系统程序大多数采用笊言来编写,FinSH命令行的设计被设计成类似C语言表达式的风格:它能够解析执行大部分C语言的表达式,也能够使用类似于C语言的函数调用方式(或函数指针方式)访问系统中的函数及全局变量。1.5 图形用户界面RT-Thread/GUI组件是一套完全针对嵌入式系统而进行优化的图形用户界面,它在保留通常意义的多窗口的前提下,提出了面板,工作台,视图的概念,通过一个个视图的渲染展现出图形用户界面绚丽的外观。它同样也包括了基本控件的支持、中文显示的支持、多线程的支持;针对嵌入式系统计算能力不足的特点,它会自动对界面区域进行可视区域的剪切,该重绘显示的地方进行重绘,被覆盖的地方则不进行重复绘图。1.6 POSIX标准POSIX!PortableOperatingSystemInterfaceofUnix的缩写,被大量的使用于UNIX类(开源的例如Linux,FreeBSD)操作系统中,是通用操彳系统上的工业标准。RT-Thread中的POSIX标准组件实现了POSIX标准所要求的大部分API接口,包括其中的动态加载接口和POSIX程库接口。1.7 用户应用模块用户应用模块提供给了用户一个独立加载应用程序的接口,能够让用户程序类似通用计算机上进行程序编写、运行。同时依然能够保持RT-Thread所提倡的实时性能。它采用把用户程序编译成与位置无关运行的方式,并在内存区域中开辟一块独立的区域,让用户应用程序独立运行。当用户应用程序退出时,系统将对这块内存区域进行清空。这样它能够保证即使应用程序运行出错时也不会对系统造成灾难性的影响。1.8 龙芯1SoC321评台支持情况SoC321配一款采用龙芯I32位高性能RISC白SoC它具备250MHz勺系统主频,兼容MIPS2旨令集,并采用5级动态流水线。在龙芯1SoC3210平台上,RT-Thread已经实现了下列特性的支持:实时内核;shell;虚拟文件系统;图形用户界面;posiX准组件;用户应用模块。第二章内核对象模型RT-Thread的内核对象模型是一种非常有趣的面向对象实现方式。由于C语言更为面向系统底层,操作系统核心通常都是采用C语言和汇编语言混合编写而成。C语言作为一门高级计算机编程语言,一般被认为是一种面向过程的编程语言:程序员按照特定的方式把要处理事物的过程一级级分解成一个个子过程。面向对象源于人类对世界的认知多偏向于类别模式,根据世界中不同物品的特性分门别类的组织在一起抽象并归纳,形成各个类别的自有属性。在计算机领域一般采用一门新的,具备面向对象特征的编程语言实现面向对象的设计,例如常见的编程语言C+,Java,Python等。那么RT-Thread既然有意引入对象系统,为什么不直接采用C+俅实现?这个需要从C+的实现说起,用过C+的开发人员都知道,C+钠对象系统中会引入很多未知的东西,例如虚拟重载表,命名粉碎,模板展开等。对于一个需要精确控制的系统,这不是一个很好的方式,假于它人之手不如握入己手!面向对象有它非常优越的地方,取其精华(即面向对象思想,面向对象设计),也就是RT-Thread内核对象模型的来源。RT-Thread实时操作系统中包含一个小型的,非常紧凑的对象系统,这个对象系统完全采用以言实现。在了解RT-Thread内部或采用RT-Thread编程时有必要先熟悉它,它是RT-Thread实现的基础。2.1 C语言的对象化模型面向对象的特征主要包括:封装,隐藏内部实现继承,复用现有代码多态,改写对象行为采用以言实现的关键是如何运用笊言本身的特性来实现上述面向对象的特征。2.1.1 封装封装是一种信息隐蔽技术,它体现于类的说明,是对象的重要特性。封装使数据和加工该数据的方法(函数)封装为一个整体,以实现独立性很强的模块,使得用户只能见到对象的外特性(对象能接受哪些消息,具有那些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象者的使用分开,使用者不必知晓行为实现的细节,只须用设计者提供的消息来访问该对象。在C语言中,大多数函数的命名方式是动词+名词的形式,例如要获取一个semaphore,会命名成takesemaphore,重点在take这个动作上。在RT-Thread系统的面向对象编程中刚好相反,命名为rt_sem_take,即名词+动词的形式,重点在名词上,体现了一个对象的方法。另外对于某些方法,仅局限在对象内部使用,它们将采用static修辞把作用范围局限在一个文件的内部。通过这样的方式,把一些不想让用户知道的信息屏蔽在封装里,用户只看到了外层的接口,从而形成了面向对象中的最基本的对象封装实现。一般属于某个类的对象会有一个统一的创建,析构过程。在RT-Thread中这些分为两类(以semaphore对象为例):对象内存数据块已经存在,需要对它进行初始化-rt_sem_init;对象内存数据块还未分配,需要创建并初始化-rt_sem_create。可以这么认为,对象的创建(create)是以对象的初始化(init)为基础的,创建动作相比较而言多了个内存分配的动作。相对应的两类析构方式:由rt_sem_init初始化的semaphore对象一rt_sem_detach由rt_sem_create创建的semaphore对象一rt_sem_delete2.1.2 继承继承性是子类自动共享父类之间数据和方法的机制。它由类的派生功能体现。一个类直接继承其它类的全部描述,同时可修改和扩充。继承具有传递性。继承分为单继承(一个子类只有一父类)和多重继承(一个类有多个父类,当前RT-Thread的对象系统不能支持)。类的对象是各自封闭的,如果没继承性机制,则类对象中数据、方法就会出现大量重复。继承不仅支持系统的可重用性,而且还促进系统的可扩充性。类似的实现代码如下程序清单:/*父类*/structparent_class.inta,b;char*str;;/*继承于父类的子类*/structchild_class.structparent_classp;inta,b;;/*操作示例函数*/voidfunc()structchild_classobj,*obj_ptr;/*子类对象及指针*/structparent_class*parent_ptr;/*父类指针*/obj_ptr=&obj;/*取父指针*/parent_ptr=(structparent_class*)&obj;/*可通过转换过类型的父类指针访问相应的属性*/parent_ptr->a=1;parent_ptr->b=5;/*子类属性的操作*/obj_ptr->a=10;obj_ptr->b=100;在上面代码中,注意child_class结构中第一个成员p,这种声明方式代表child_class类型的数据中开始的位置包含一个parent_class类型的变量。在函数func中obj是一个child_class对象,正像这个结构类型指示的,它前面的数据应该包含一个parent_class类型的数据。在第21行的强制类型赋值中parent_ptr指向了obj变量的首地址,也就是obj变量中的p对象。好了,现在parent_ptr指向的是一个真真实实的parent类型的结构,那么可以按照parent的方式访问其中的成员,飞然也包括可以使用和parent结构相关的函数来处理内部数据,因为一个正常的,正确的代码,它是不会越界访问parent结构体以外的数据。经过这基本的结构体层层相套包含,对象简单的继存关系就体现出来了:父对象放于数据块的最前方,代码中可以通过强制类型转换获得父对象指针。2.1.3 多态对象根据所接收的消息而做出动作。同一消息为不同的对象接受时可产生完全不同的行动,这种现象称为多态性。利用多态性用户可发送一个通用的信息,而将所有的实现细节都留给接受消息的对象自行决定,如是,同一消息即可调用不同的方法。例如:RT-Thread系统中的设备:抽象设备具备接口统一的读写接口。串口是设备的一种,也应支持设备的读写。但串口的读写操作是串口所特有的,不应和其他设备操作完全相同,例如操作串口的操作不应应用于S*设备中。多态性的实现受到继承性的支持,利用类继承的层次关系,把具有通用功能的协议存放在类层次中尽可能高的地方,而将实现这一功能的不同方法置于较低层次,这样,在这些低层次上生成的对象就能给通用消息以不同的响应。RT-Thread对象模型采用结构封装中使用指针的形式达到面向对象中多态的效果,例如:/*抽象父类*/structparent_classinta;/*反映不同类别属性的方法*/void(*vfunc)(inta);/*抽象类的方法调用*/voidparent_class_vfunc(structparent_class*self,inta)一一一assert(self!=NULL);assert(slef->vfunc!=NULL);/*调用对象本身的虚拟函数*/self->vfunc(a);/*继承自parent_class的子类*/structchild_class-structparent_classparent;intb;/*子类的构造函数*/voidchild_class_init(structchild_class*self)一一一structparent_class*parent;/*强制类型转换获得父类指针*/parent=(structparent_class*)self;assert(parent!=NULL);/*设置子类的虚拟函数*/parent->vfunc=child_class_vfunc;一一/*子类的虚拟函数实现*/staticvoid_child_class_vfunc(structchild_class*self,inta)b = a + 10;self->2.2 内核对象模型2.2.1 静态对象和动态对象RT-Thread的内核映像文件在编译时会形成如下图所示的结构(以STM32KeilMDK为例):其中主要包括了这么几段:段名称ER_IROM1代码正文段,以及只读数据。GC伸一月是.text和.rodata段RWJRAM1数据段,通常又分为存放带初始值的RW_IRAM1$RW和存放无初始值的RW_IRAM1$ZI相对应的,在GCCb一般称为.data段和.bss段。如下图所示,在STM32k,分成了RW_IRAMRER_IROMRW_IRAM1$ZIRW_IRAM1$RW0x20000000ER_IROM10x08000000当系统运行时,这些段也会相应的映射到内存中。在RT-Thread系统初始化时,通常ZI或.bss段会清零,而堆(Heap)则是RWJRAMt除了RWHZI以外可用的内存空间(具体的地址空间在系统启动时由链接时的参数指定),星统运行时动态分配的内存块就在堆的空间中分配出来的,如下代码:rt_uint8_t*msg_ptr;msg_ptr=(rt_uint8_t*)rt_malloc(128);rt_memset(msg_ptr,0,128);msg_ptr指向的128字节内存空间位于堆空间中。而一些全局变量则是存放于RW_IRAM1$RWRW_IRAM1$Z,RW_IRAM1$RW的是具有初始值的全局变量(而常量形式的全局变量则放置在ER_IROM般中,是只读属性的),如下代码:#include<rtthread.h>conststaticrt_uint32_tsensor_enable=0x000000FE;rt_uint32_tsensor_value;rt_bool_tsensor_inited=RT_FALSE;voidsensor_init()/*.*/sensor_value存放在RW_IRAM1$政中,系统启动后会自动初始化成零。sensor_inited变量则存放在RW_IRAM1$RWK而sensor_enable存放在RW_IROM1中。在RT-Thread内核对象中分为两类:静态内核对象和动态内核对象。静态内核对象通常放在RW_IRAM1$RWRW_IRAM1$破中,在系统启动后在程序中初始化;动态内核对象则是从堆中创建的,而后手工做初始化。RT-Thread中操作系统级的设施都是一种内核对象,例如线程,信号量,互斥量,定时器等。以下的代码所示的即为静态线程和动态线程的例子:/*线程1的对象和运行时用到的栈*/staticstructrt_threadthreadl;staticrt_uint8_tthread1_stack512;/*线程1入口*/voidthread1_entry(void*parameter)一inti;while(1)for(i=0;i<10;i+)rt_kprintf("%d'n",i);/*延时100个OSTick*/rt_thread_delay(100);一一/*线程2入口*/voidthread2_entry(void*parameter)-intcount=0;while(1)rt_kprintf("Thread2count:%dn",+count);/*延时50个OSTick*/rt_thread_delay(50);一一/*用户应用程序入口*/intrt_application_init()一一rt_thread_tthread2_ptr;rt_err_tresult;/*初始化线程1*/*线程的入口是thread1_entry,参数是RT_NULL*线程栈是thread1_stack*优先级是200,时间片是10个OSTick*/result=rt_thread_init(&thread1,"thread1",thread1_entry,RT_NULL,&thread1_stack0,sizeof(thread1_stack),200,10);/*启动线程*/if(result=RT_EOK)rt_thread_startup(&thread1);/*创建线程2*/*线程的入口是thread2_entry,参数是RT_NULL*栈空间是512,优先级是250,时间片是25个OSTick*/thread2_ptr=rt_thread_create("thread2",thread2_entry,RT_NULL,512,250,25);/*启动线程*/if(thread2_ptr!=RT_NULL)rt_thread_startup(thread2_ptr);return0;例子中,thread1是一个静态线程对象,而thread2是一个动态线程对象。thread1对象的内存空间,包括线程控制块thread1,栈空间thread1_stack都是编译时决定的,因为代码中都不存在初始值,者B统一放在RW_IRAM1$破中。thread2运行中用到的空间都是动态分配的,包括线程控制块(thread2_ptr指面的内容)和栈空间。2.2.2内核对象管理工作模式线程对象互斥量对象事件对象邮箱对象消息队列对象定时器对象RT-Thread采用内核对象管理系统来访问/管理所有内核对象。内核对象包含了内核中绝大部分设施,而这些内核对象可以是静态分配的静态对象,也可以是从系统内存堆中分配的动态对象。通过内核对象系统,RT-Thread做到了不依赖于具体的内存分配方式,系统的灵活性得到极大的提高。RT-Thread内核对象包括:线程,信号量,互斥锁,事件,邮箱,消息队列和定时器,内存池,设备驱动等。对象容器中包含了每类内核对象的信息,包括对象类型,大小等。对象容器给每类内核对象分配了一个链表,所有的内核对象都被链接到该链表上。下图显示了RT-Thread中各类内核对象的派生和继承关系。对于每一种具体内核对象和对象控制块,除了基本结构外,还有自己的扩展属性(私有属性),例如,对于线程控制块,在基类对象基础上进行扩展,增加了线程状态、优先级等属性。这些属性在基类对象的操作中不会用到,只有在与具体线程相关的操作中才会使用。因此从面向对象的观点,可以认为每一种具体对象是抽象对象的派生,继承了基本对象的属性并在此基础上扩展了与自己相关的属性。rt_cbjectArt_mempoolrt timerrCsemaphorertmailboxrt mutexn eventrt _nies sage_queue+name +type +£Lg在对象管理模块中,定义了通用的数据结构,用来保存各种对象的共同属性,各种具体对象 只需要在此基础上加上自己的某些特别的属性,就可以清楚的表示自己的特征。这种设计方法的rt_ipc_pbct+fluspend threadrt threadrt device优点:1.提高了系统的可重用性和扩展性,增加新的对象类别很容易,只需要继承通用对象的属 性再加少量扩展即可。2.提供统一的对象操作方式,简化了各种具体对象的操作,提高了系统的可靠性。2.2.3对象控制块struct rt_object/*内核对象名称*/charname RT_NAME_MAX;/*内核对象类型*/rt_uint8_ttype ;/*内核对象的参数*/rt uint8 tflag ;/*内核对象管理链表 */rt list tlist ;目前内核对象支持的类型如下:enum rt_object_class_typeRT_Object_Class_Thread = 0,#ifdef RT_UsINg_SEMAPHORERT_Object_Class_Semaphore,#endif |#ifdef RT_USING_MUTEXRT_Object_Class_Mutex,#endif |#ifdef RT_USING_EVENTRT_Object_Class_Event,#endif I-#ifdef RT_USING_MAILBOXRT_Object_Class_MailBox, #endif |/*对象为线程类型*/*对象为信号量类型*/*对象为互斥锁类型*/*对象为事件类型/*对象为邮箱类型*/*/#ifdefRT_USING_MESSAGEQUEUERT_Object_Class_MessageQueue,/*对象为消息队列类型*/#endif|#ifdefRT_USING_MEMPOOLRT_Object_Class_MemPool,/*对象为内存池类型*/#endif|#ifdefRT_USING_DEVICERT_Object_Class_Device,/*对象为设备类型*/#endif|RT_Object_Class_Timer,/*对象为定时器类型*/RT_Object_Class_Unknown,/*对象类型未知*/RT_Object_Class_Static=0x80/*对象为静态对象*/;从上面的类型说明,我们可以看出,如果是静态对象,那么对象类型的最高位将是1,否则就是动态对象。2.2.4内核对象接口初始化系统对象在初始化各种内核对象之前,首先需对对象管理系统进行初始化。在系统中,每类内核对象都有一个静态对象容器,一个静态对象容器放置一类内核对象,初始化对象管理系统的任务就是初始化这些对象容器,使之能够容纳各种内核对象,初始化系统对象使用以下接口:voidrt_system_object_init(void);以下是对象容器的数据结构:structrt_object_information一一enumrt_object_class_typetype;/*对象类型*/rt_list_tobject_list;/*对象链表*/rt_size_tobject_size;/*对象大小*/;一一一一种类型的对象容器维护了一个对象链表objectlist,所有对于内核对象的分配,释放操作均在该链表上进行。初始化对象使用对象前须先对其进行初始化。初始化对象使用以下接口:voidrt_object_init(structrt_object*object,enumrt_object_class_typetype,constchar*name);对象初始化,实现上就是把对象放入到其相应的对象容器中,即将对象插入到对象容器链表中。脱离对象从内核对象管理器中脱离一个对象。脱离对象使用以下接口:voidrt_object_detach(rt_object_tobject);使用该接口后,静态内核对象将从内核对象管理器中脱离,对象占用的内存不会被释放。分配对象在当前内核中,定义了数种内核对象,这些内核对象被广泛的用于线程的管理,线程之间的同步,通信等。因此,系统随时需要新的对象来完成这些操作,分配新对象使用以下接口:rt_object_t rt_object_allocate( char * name);enum rt_object_class type type,const使用以上接口,首先根据对象类型来获取对象信息,然后从内存堆中分配对象所需内存空间,然后对该对象进行必要的初始化,最后将其插入到它所在的对象容器链表中。删除对象不再使用的对象应该立即被删除,以释放有限的系统资源。删除对象使用以下接口:voidrt_object_delete(rt_object_tobject);使用以上接口时,首先从对象容器中脱离对象,然后释放对象所占用的内存。辨别对象判断指定对象是否是系统对象(静态内核对象)。辨别对象使用以下接口:rt_err_trt_object_is_systemobject(rt_object_tobject);通常采用rt_object_init()方式挂接到内核对象管理器中的对象是系统对象。第三章线程调度与管理一个典型的简单程序会设计成一个串行的系统运行:按照准确的指令步骤一次一个指令的运行。但是这种方法对于复杂一些的实时应用是不可行的,因为它们通常需要在固定的时间内“同时”处理多个输入输出,实时软件应用程序应该设计成一个并行的系统。并行设计需要开发人员把一个应用分解成一个个小的,可调度的,序列化的程序单元。当合理的划分任务,正确的并行执行时,这种设计能够让系统满足实时系统的性能及时间的要求。3.1 实时系统的需求如第二章里描述的,实时系统指的是在固定的时间内正确地对外部事件做出响应。这个“时间内”(英文叫做deadline),系统内部会做一些处理,例如输入数据的分析计算,加工处理等。而在这段时间之外,系统可能会闲下来,做一些空余的事。例如一个手机终端,当一个电话拨入的时候,系统应当及时发出振铃、声音提示以通知主人有来电,询问是否进行接听。而在非电话拨入的时候,人们可以用它进行一些其它工作,例如听音乐,玩游戏等。从上面的例子我们可以看出,实时系统是一种需求倾向性的系统,对于实时的事件需要在第一时间内做出回应,而对非实时任务则可以在实时事件到达时为之让路一一被抢占。所以实时系统也可以看成是一个等级系统,不同重要性的任务具有不同的优先等级:重要的事件能够优先被响应执行,非重要的事件可以适当往后推迟。在RT-Thread实时操作系统中,任务采用了线程来实现,线程是RT-Thread中最基本的调度单位,它描述了一个任务执行的上下文关系,也描述了这个任务所处的优先等级。重要的任务能拥有相对较高的优先级,非重要的任务优先级可以放低,并且可以类似Linux一样具备分时的效果。3.2 线程调度器RT-Thread中提供的线程调度器是基于全抢占式优先级的调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。系统总共支持256个优先级(0255,数值越小的优先级越高,0为最高优先级,255分配给空闲线程使用,一般用户不使用。在一些资源比较紧张的系统中,可以根据实际情况选择只支持8个或32个优先级的系统配置)。在系统中,当有比当前线程优先级还要高的线程就绪时,当前线程将立刻被换出,高优先级线程抢占处理机进行执行。如下图所示,RT-Thread调度器实现中包含一组,总共256个优先级队列数组(如果系统最大支持32个优先级,那么这里将是32个优先级队列数组),每个数组元素中放置相同优先级的链表头。这些相同优先级的列表形成一个双向环形链表,255优先级队列中一般只包含一个idle线程。在优先级队列1#和2#中,分别有线程A-线程C。由于线程A、B的优先级比线程C勺高,所以此时线程C导不到运行,必须要等待线程A-线程B(因为阻塞)都让出处理机后才能得到执行。当一个操作系统仅仅具备了高优先级任务能够“立即”获得处理机进行执行,它依然不算是实时操作系统:这个查找最高优先级线程的“立即”过程决定了调度时间的确定性,例如一个包含n个就绪任务的系统中,如果仅仅从头找到尾,那么这个时间将直接和n相关。这下一个就绪线程抉择时间的长短极大的影响到系统的实时性:当所有就绪线程都链接在它们对应的优先级队列中时,抉择过程演变为在优先级数组中寻找最高优先级线程。RT-Thread内核中采用了基于位图的优先级算法(时间复杂度0(1),即与就绪线程的多少无关),通过位图的定位快速的获得优先级最高的线程。RT-Thread内核中也允许创建相同优先级的线程。相同优先级的线程采用时间片轮转方式进行调度(也就是通常说的分时调度器)。时间片轮转调度仅在当前系统中无更高优先级就绪线程存在的情况下才有效。如上图例中所示的线程A和线程B,假设它们一次最大允许运行的时间片分别是10个时钟节拍和7个时钟节拍。那么线程B的运行需要在线程A运行完它的时间片(10个时钟节拍)后才能获得运行(如果中途线程At挂起了,线程B因为变成系统中就绪线程中最高的优先级,会马上获得运行)。每个线程的时间片大小可以在初始化或创建这个线程时指定。因为RT-Thread调度器的实现是采用优先级链表的方式,所以系统中的总线程数不受限制,只和系统所能提供的内存资源相关。为了保证系统的实时性,系统尽最大可能地保证高优先级的线程得以运行。线程调度的原则是一旦任务状态发生了改变,并且当前运行的线程优先级小于优先级队列组中线程最高优先级时,立刻进行线程切换(除非当前系统处于中断处理程序中或禁止线程切换的状态)。3.3 线程控制块线程控制块是操作系统用于控制线程的一个数据结构,它会存放线程的一些信息,例如优先级,线程名称等,也包含线程与线程之间的链表结构,线程等待事件集合等。在RT-Thread实时操作系统中,线程控制块由结构体structrt_thread(如下代码中所示)表示,另外一种写法是rt_thread_t,表示的是线程的句柄,在C语言中实现是指向线程控制块/*rt_thread_t线程句柄,指向线程控制块的指针*/typedefstructrt_thread*rt_thread_t;/*线程控制块*/structrt_thread一/*RT-Thread根对象定义*/charnameRT_NAME_MAX;/*对象的名称*/rt_uint8_ttype;/*对象的类型*/rt_uint8_tflags;/*对象的参数*/rt_list_tlist;/*对象链表*/rt_list_ttlist;/*线程链表*/#if/*栈指针及入口 */void *sp ;/*线程的栈指针*/void *entry ;/*线程入口*/void *parameter ;/*线程入口参数*/void *stack_addr ;/*线程栈地址*/rt_uint16_tstack_size ;/*线程栈大小*/rt_err_terror ;/*线程错误号*/rt_uint8_tstat ;/*线程状态*/*优先级相关域*/rt_uint8_tcurrent_priority;/*当前优先级*/rt_uint8_tinit_priority;/ *初始线程优先级*/RT_THREAD_PRIORITY_MAX > 32rt_uint8_t number;rt_uint8_t high_mask;#endif rt uint32 tnumber_mask ;#if defined(RT_USING_EVENT)/*事件相关域*/rt_uint32_t event_set;rt_uint8_t event_info;#endifrt_ubase_tinit_tick ;rt_ubase_tremaining_tickstruct rt_timer thread_timerrt_uint32_tuser_data;; 一一一/*线程初始tick*/*线程当次运行剩余tick*/*线程定时器*/*用户数据*/最后的一个成员user_data可由用户挂接一些数据信息到线程控制块中,以提供类似线程私有数据的实现,例如LwIP线程中用于放置定时器链表头。3.4 线程状态线程运行的过程中,在一个时间内只允许一个线程在处理器中运行,从运行的过程上划分,线程有多种不同的运行状态,如运行态,非运行态等。在RT-Thread实时操作系统中,线程包含四种状态,操作系统会自动根据它运行的情况而动态调整它的状态。RT-Thread中的四种线程状态:状态描述RT_THREAD_INIT/CLOSE线程初始状态。当线程刚开始创建还没开始运行时就处于这个状态;当线程运行结束时也处于这个状态。在这个状打下,线程不参与调度RT_THREAD_SUSPEND挂起态。线程此时被挂起:它可能因为资源/、可用而等待挂起;或主动延时一段时间而被挂起。在这个状蕊下,线程不参与调度RT_THREAD_READY就绪态。线程正在运行;或当前线程运行完让出处理机后,操作系统寻找最高优先级的就绪态线程运行RT_THREAD_RUNNING运行态。线程当前正在运行,在单核系统中,只后rt_thread_self()函数返回的线程处于这个状态;在多核系统中则不受这个限制。RT-Thread实时操作系统提供一系列的操作系统调用接口,使得线程的状态在这四个状态之间来回的变换。例如一个就绪态的线程由于申请一个资源(使用rt_sem_take),而可能进入阻塞态。又例如,一个外部中断发生,转入中断服务例程,在中断服务例程中释版了相应的资源,导致唤醒了另一阻塞状态的高优先级线程,改变其状态为就绪态,导致当前运行线程切换等等。几种状态间的转换关系如下图所示:rt_thread_create/init一一一»/初始状态rt_thread_startup就绪状态rt_thread_resume rt_sem release rt_mutex_release rt_event_send rt_mb_send rt_mq_send rt_mp_releasert_thread_delete/detachrt_thread_suspendrt_thread_delayrt_sem_takertmutextakert_event_recvrt_mq_recv上rt_mb_recv运行状态丁rt_mp_alloc线程通过调用函数rt_thread_create/init调用进入到初始状态(RT_THREAD_INIT/RT_THREAD_CLOSE),通过函数rt_thread_startup调用后进入到就绪状态(RT_THREAD_READY)。当这个线程调用rt_thread_delay,rt_sem_take,rt_mb_recv等函数时,将主动挂起或由于获取不到资源进入到挂起状态(RT_THREAD_SUSP日ND至挂点状态的线程,如果它等待超时依然未获得资源或由于其他线程释放了资源,它将返回到就绪状态。3.5 空闲线程空闲线程是系统线程中一个比较特殊的线程,它具备最低的优先级,当系统中无其他线程可运行时,调度器将调度到空闲线程。空闲线程通常是一个死循环,永远不被挂起。在RT-Thread实时操作系统中空闲线程提供了钩子函数,可以让系统在空闲的时候执行一些特定的任务,例如系统运行指示灯闪烁,电源管理等。在允许钩子函数之外,RT-Thread把真正的线程删除动作也放到了空闲线程中(在删除线程时,仅改变线程的状态为关闭状态不再参与系统调度)。3.6 调度器相关接口3.6.1 调度器初始化在系统启动时需要执行调度器的初始化,以初始化调度器用到的一些全局变量。调度器初始化可以调用以下接口。voidrt_system_scheduler_init(void);3.6.2 启动调度器在系统完成初始化后切换到第一个线程,可以调用如下接口。voidrt_system_scheduler_start(void);在调用这个函数时,它会查找系统中优先级最高的就绪态线程,然后切换过去运行。Note:在调用这个函数前,必须先做idle线程的初始化,即保证系统至少能够找到一个就绪状态的线程进行执行。此函数是永远不会返回的。3.6.3 执行调度让调度器执行一次线程的调度可通过如下接口。voidrt_schedule(void);调用这个函数后,系统会计算一次系统中就绪态的线程,如果存在比当前线程更高优先级的线程时,系统将切换到高优先级的线程去。通常情况下,用户不需要直接调用这个函数。IE在中断服务例程中也可以调用这个函数,如果满足任务切换的条件,它会记录下中断前的线程及需要切换到的更高优先级线程,在中断服务例程处理完毕后执行真正的线程上下文切换(即中断中的线程上下文切换),最终切换到目标线程去。3.6.4 设置调度器钩子整个系统的运行基本上都处于一个线程运行、中断触发响应中断、切换到其他线程,甚至是线程间的切换过程中。有时用户可能会想知道在一个时刻发生了什么样的线程切换,可以通过调用下面的函数接口设置一个相应的钩子函数。在系统线程切换时,这个钩子函数将被调用。voidrt_scheduler_sethook(void(*hook)(structrt_thread*from,structrt_thread*to);调用这个函数可设置一个声明成如下形式voidhook(structrt_thread*from,structrt_thread*to);的函数作为系统线程切换时被自动调用的钩子函数。其中参数,from、to分别指出了切换到和切换出线程的控制块指针。请仔细编写你的钩子函数,如有不甚将很可能导致整个系统运行不正常(在这个钩子函数中,基本上不允许调用系统API)。3.7 线程相关接口3.7.1 线程创建一个线程要成为可执行的对象就必须由操作系统内核来为它创建/初始化一个线程句柄。可以通过如下的接口来创建一个线程。rt_thread_trt_thread_create(constchar调用这个函数后,系统会从动态堆内存中分配一个线程句柄(即 照参数中指定的栈大小从动态堆内存中分配相应的空间。创建一个线程的例子如下代码所示。/*程序清单:动态线程*这个程序会初始化2个动态线程:*它们拥有共同的入口函数,相同的优先级但是它们的入口参数不相同*/#include <rtthread.h>#defineTHREAD_PRIORITY25#defineTHREAD_STACK_SIZE512#define THREAD_TIMESLICE 5/*指向线程控制块的指针*/staticrt_thread_t tid1 = RT_NULL;staticrt_thread_t tid2 = RT_NULL;/* 线程入口 */static void thread_entry (void * parameter).rt_uint32_t count = 0;rt_uint32_t no = (rt_uint32_t) parameter;/* 获得正确的入口参数*/while (1) /*打印线程计数值输出*/rt_kprintf( "thread%d count: %dn", no, count +);/* 休眠 10 个 OS Tick */线程删除例子如下:/*程序清单:删除线程*name,void(*entry)(void*parameter),void*parameter,rt_uint32_tstack_size,rt_uint8_tpriority,rt_uint32_ttick);在调用这个函数时,需要为线程指定名称,线程入口位置,入口参数,线程栈大小,优先级及时间片大小。线程名称的最大长度由宏RT_NAME_MAX指定,多余部分会被自动截掉。栈大小的单位是字节,在大多数系统中需要做对齐(例如ARM体系结构中需要向4字节对齐)。线程的优先级范围根据系统配置情况,如果支持256级线程优先级,那么范围是从0255,数值越小优先级越高0。时间片(tick)的单位是操作系统的时钟节拍,当系统中存在相同优先级线程时,这个参数指定线程一次调度能够运行的最大时间长度,这段时间片运行结束后,调度器自动选择下一个就绪的同优先级线程进行运行。确定一个线程的栈空间大小,是一件令人繁琐的事情。在RT-Thread中,可以先指定一TCB线程控制块)以及按个稍微大的栈空间,例如1024或2048,然后在FinSHshell中通过list_thread()命令查看线程运行的过程中线程使用栈的最大值,它能够看到从线程启动运行时,到当前时刻点,线程使用的最大栈深度)。rt_thread_delay(10);/*用户应用入口*/int rt_application_init/* 创建线程1 */tidl = rt_thread_create( thread_entry,()"t1",void这个例子会创建两个线程,在一个线程中删除另外一个线程*/#include <rtthread.h>#defineTHREAD_PRIORITY25#defineTHREAD_STACK_SIZE512#define THREAD_TIMESLICE 5)1,/*线程入口是thread_entry,THREAD_STACK_SIZE,THREAD_PRIORITY,THREAD_TIMESLICE);if(tid1!=RT_NULL)rt_thread_startup(tid1);elsereturn-1;/*创建线程2*/tid2=rt_thread_create("t2",thread_entry,(void*)2,/*线程入口是thread_entry,THREAD_STACK_SIZE,THREAD_PRIORITY,THREAD_TIMESLICE);if(tid2!=RT_NULL)rt_thread_startup(tid2);elsereturn-1;入口参数是1 */入口参数是2 */return0;3.7.2 线程删除一个线程通过rt_thread_create创建出来后,因为出错或其他原因需要删除一个线程。当需要删除用rt_thread_create创建出的线程时,可以使用以下接口:rt_err_trt_thread_delete(rt_thread_tthread)调用该接口后,线程对象将会被移出线程队列并且从内核对象管理器中删除,线程占用的堆栈空间也会被释放以回收相应的空间进行其他内存分配。rt_thread_delete删除线程接口仅把相应的线程状态更改为RT

注意事项

本文(RT-Thread龙芯移植技术文档分析)为本站会员(无***)主动上传,装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知装配图网(点击联系客服),我们立即给予删除!

温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!