RT-thread内核之对象模型

RT-Thread的内核对象模型是一种非常有趣的面向对象实现方式。由于C语言更为面向系统底层,操作系统核心通常都是采用C语言和汇编语言混合编写而成。C语言作为一门高级计算机编程语言,一般被认为是一种面向过程的编程语言:程序员按照特定的方式把要处理事物的过程一级级分解成一个个子过程。面向对象源于人类对世界的认知多偏向于类别模式,根据世界中不同物品的特性分门别类的组织在一起抽象并归纳,形成各个类别的自有属性。在计算机领域一般采用一门新的,具备面向对象特征的编程语言实现面向对象的设计,例如常见的编程语言C++,Java,Python等。那么RTThread既然有意引入对象系统,为什么不直接采用C++来实现? 这个需要从C++的实现说起,用过C++的开发人员都知道,C++的对象系统中会引入很多未知的东西,例如虚拟重载表,命名粉碎,模板展开等。对于一个需要精确控制的系统,这不是一个很好的方式,假于它人之手不如握入己手!面向对象有它非常优越的地方,取其精华(即面向对象思想,面向对象设计),也就是RT-Thread内核对象模型的来源。RT-Thread实时操作系统中包含一个小型的,非常紧凑的对象系统,这个对象系统完全采用C语言实现。



C语言的对象化模型:采用C语言实现的关键是如何运用C语言本身的特性来实现上述面向对象的特征。

1、封装,隐藏内部实现;

封装是一种信息隐蔽技术,它体现于类的说明,是对象的重要特性。封装使数据和加工该数据的方法(函数)封装为一个整体,以实现独立性很强的模块,使得用户只能见到对象的外特性(对象能接受哪些消息,具有那些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象者的使用分开,使用者不必知晓行为实现的细节,只须用设计者提供的消息来访问该对象。在C语言中,大多数函数的命名方式是动词+名词的形式,例如要获取一个semaphore,会命名成take_semaphore,重点在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_delete.

2、继承,复用现有代码;

继承性是子类自动共享父类之间数据和方法的机制。它由类的派生功能体现。一个类直接继承其它类的全部描述,同时可修改和扩充。继承具有传递性。继承分为单继承(一个子类只有一父类)和多重继承(一个类有多个父类,当前RT-Thread的对象系统不能支持)。类的对象是各自封闭的,如果没继承性机制,则类对象中数据、方法就会出现大量重复。继承不仅支持系统的可重用性,而且还促进系统的可扩充性。

 1 类似的实现代码如下程序清单:
 2
 3 /* 父类 */
 4 struct parent
 5 {
 6    int a, b;
 7    char *str;
 8 };
 9
10 /* 继承于父类的子类 */
11 struct child
12 {
13   struct parent p;
14   int a, b;
15 };
16
17 /* 操作示例函数*/
18 void func()
19 {
20   struct child     obj, *obj_ptr; /* 子类对象及指针 */
21   struct parent  *parent_ptr; /* 父类指针 */
22   obj_ptr = &obj;
23
24   /* 取父指针 */
25   parent_ptr = (struct parent*) &obj;
26
27   /* 可通过转换过类型的父类指针访问相应的属性 */
28   parent_ptr->a = 1;
29   parent_ptr->b = 5;
30   /* 子类属性的操作 */
31   obj_ptr->a = 10;
32   obj_ptr->b = 100;
33 }  

在上面代码中,注意child结构中第一个成员p,这种声明方式代表child类型的数据中开始的位置包含一个parent类型的变量。在函数func中obj是一个child对象,正像这个结构类型指示的,它前面的数据应该包含一个parent类型的数据。在第25行的强制类型赋值中parent_ptr指向了obj变量的首地址,也就是obj变量中的p对象。好了,现在parent_ptr指向的是一个真真实实的parent类型的结构,那么可以按照parent的方式访问其中的成员,当然也包括可以使用和parent结构相关的函数来处理内部数据,因为一个正常的,正确的代码,它是不会越界访问parent结构体以外的数据。

经过这基本的结构体层层相套包含,对象简单的继存关系就体现出来了:父对象放于数据块的最前方,代码中可以通过强制类型转换获得父对象指针。

3、多态,改写对象行为;

对象根据所接收的消息而做出动作。同一消息为不同的对象接受时可产生完全不同的行动,这种现象称为多态性。利用多态性用户可发送一个通用的信息,而将所有的实现细节都留给接受消息的对象自行决定,如是,同一消息即可调用不同的方法。例如:RT-Thread系统中的设备:抽象设备具备接口统一的读写接口。串口是设备的一种,也应支持设备的读写。但串口的读写操作是串口所特有的,不应和其他设备操作完全相同,例如操作串口的操作不应应用于SD卡设备中。多态性的实现受到继承性的支持,利用类继承的层次关系,把具有通用功能的协议存放在类层次中尽可能高的地方,而将实现这一功能的不同方法置于较低层次,这样,在这些低层次上生成的对象就能给通用消息以不同的响应。

 RT-Thread对象模型采用结构封装中使用指针的形式达到面向对象中多态的效果,例如: 1 /* 抽象父类 */
 2 struct parent
 3 {
 4   int a;
 5
 6   /* 反映不同类别属性的方法 */
 7   void (*vfunc)(int a);
 8 }
 9
10 /* 抽象类的方法调用 */
11 void parent_vfunc(struct parent *self, int a)
12 {
13   assert(self != NULL);
14   assert(slef->vfunc != NULL);
15   /* 调用对象本身的虚拟函数 */
16   self->vfunc(a);
17 }
18
19 /* 继承自parent的子类 */
20 struct child
21 {
22   struct parent p;
23   int b;
24 };
25
26 /* 子类的构造函数 */
27 void child_init(struct child* self)
28 {
29   struct parent* parent;
30
31   /* 强制类型转换获得父类指针 */
32   parent = (struct parent*) self;
33   assert(parent != NULL);
34
35   /* 设置子类的虚拟函数 */
36   parent->vfunc = child_vfunc;
37 }
38 /* 子类的虚拟函数实现 */
39 static void child_vfunc(struct child* self, int a)
40 {
41   self->b = a + 10;
42 }

在RT-Thread内核对象中分为两类:静态内核对象和动态内核对象。

静态内核对象通常放在RW(用于放置带初始值的全局变量)或ZI(用于放置未初始化或初始化0的全局变量)段中,在系统启动后在程序中初始化;动态内核对象则是从堆中创建的,然后手工做初始化。

时间: 2024-09-29 00:38:21

RT-thread内核之对象模型的相关文章

RT Thread学习历程(1):串口乱码问题

因为学习实时系统,最近接触到RT Thread. 把RT Thread官网上的示例代码烧录到STM32的板子上之后,在串口软件上接收到的全是乱码,一开始以为是串口软件的问题,换了2个软件之后情况都一样,最后发现是晶振的问题,我用的是STM32F407VGT6,晶振要设为8MHz,代码相应的设置晶振的部分也要修改.

STM32 + RT Thread OS 串口通讯

1.   创建项目 a)   禁用Finsh和console b)   默认情况下,项目文件包含了finsh,它使用COM1来通讯,另外,console输出(rt_kprintf)也使用了COM1.因此,在运行scons命令生成项目文件之前,修改rtconfig.h,禁用这两项.(下图L65, L70) c)   生成项目文件 运行scons --target=mdk4 –s 打开生成的项目文件,可以看到,文件组finsh已经不再被包含进来了. d)   创建echo.c 新建一个C文件echo

RT thread 设备驱动之串口设备

本文以stm32f4xx平台介绍串口驱动,主要目的是: 1.RTT中如何编写中断处理程序 2.如何编写RTT设备驱动接口代码 3.了解串行设备的常见处理机制 所涉及的主要源码文件有:usart.c,usart.h,serial.c,serial.h 一.RTT的设备驱动程序概述 编写uart的驱动程序,首先需要了解RTT的设备框架,这里以usart的驱动来具体分析RTT的IO设备管理.注:参考<RTT实时操作系统编程指南> I/O设备管理一章. 我们可以将USART的硬件驱动分成两个部分,如下

向linux内核中添加外部中断驱动模块

本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内核中添加外部中断驱动模块.7.完整驱动程序代码.linux的内核版本为linux2.6.32.2. 一.linux模块的框架以及混杂设备相关知识 1.内核模块的框架如下图所示,其中module_init()(图中有误,不是modules_init)只有在使用insmod命令手动加载模块时才会被调用,

实验六分析Linux内核创建一个新进程的过程

王康 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 1,进程的描述 操作系统三大功能:进程管理(核心),内存管理,文件系统 1,进程控制块PCB--task_struct 也叫进程描述符,为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. struct task_struct数据结构很庞大:可以看到state进程状态,stack内核堆栈,

Linux内核设计与实现 读书笔记

第三章 进程管理 1. fork系统调用从内核返回两次: 一次返回到子进程,一次返回到父进程 2. task_struct结构是用slab分配器分配的,2.6以前的是放在内核栈的栈底的:所有进程的task_struct连在一起组成了一个双向链表 3. 2.6内核的内核栈底放的是thread_info结构,其中有指向task_struct的指针: 4. current宏可以找到当前进程的task_struct:X86是通过先找到thread_info结构,而PPC是有专门的寄存器存当前task_s

RT-Thread--内存管理

内存管理的功能特点 RT-Thread 操作系统在内存管理上,根据上层应用及系统资源的不同,有针对性地提供了不同的内存分配管理算法.总体上可分为两类:内存堆管理与内存池管理,而内存堆管理又根据具体内存设备划分为三种情况: 第一种是针对小内存块的分配管理(小内存管理算法): 第二种是针对大内存块的分配管理(slab 管理算法): 第三种是针对多内存堆的分配情况(memheap 管理算法) 内存堆管理 内存堆管理用于管理一段连续的内存空间,如下图所示,RT-Thread 将 “ZI 段结尾处” 到内

LwIP学习笔记——STM32 ENC28J60移植与入门

0.前言 去年(2013年)的整理了LwIP相关代码,并在STM32上"裸奔"成功.一直没有时间深入整理,在这里借博文整理总结.LwIP的移植过程细节很多,博文也不可能一一详解个别部分只能点到为止. [本文要点] [1]不带操作系统的LwIP移植,LwIP版本为1.4.1. [2]MCU为STM32F103VE,网卡为ENC28J60. [3]移植过程重点描述ethernetif.c和LwIP宏配置等. [4]一个简单的TCP echo例子. [5]力求简单,没有DHCP功能,甚至没有

多线程的两种实现方式

java中多线程可以采用两种方式实现,分别是继承Thread类重写run方法和实现Runnable接口重写run方法. 继承Thread类重写run方法举例如下: /* 需求:在主线程之外继承Thread类创建两独立线程,分别打印1至50. */ class ThreadTest extends Thread{ public void run(){ for(int i = 1; i <= 50; i++) System.out.println(Thread.currentThread().get