IOT-OS之RT-Thread(三)--- C语言对象化与内核对象管理

操作系统 同时被 3 个专栏收录
26 篇文章 15 订阅
20 篇文章 10 订阅
91 篇文章 55 订阅

一、C语言对象化模型

RT-Thread的内核对象模型是一种非常有趣的面向对象实现方式。由于C语言更为面向系统底层,操作系统核心通常都是采用C语言和汇编语言混合编写而成。C语言作为一门高级计算机编程语言,一般被认为是一种面向过程的编程语言:程序员按照特定的方式把要处理事物的过程一级级分解成一个个子过程。面向对象源于人类对世界的认知多偏向于类别模式,根据世界中不同物品的特性分门别类的组织在一起抽象并归纳,形成各个类别的自有属性。

在计算机领域一般采用一门新的,具备面向对象特征的编程语言实现面向对象的设计,例如常见的编程语言C++,Java,Python等。那么RT-Thread既然有意引入对象系统,为什么不直接采用C++来实现? 这个需要从C++的实现说起,用过C++的开发人员都知道,C++的对象系统中会引入很多未知的东西,例如虚拟重载表、命名粉碎、模板展开等。对于一个需要精确控制的系统,这不是一个很好的方式,假于他人之手不如握入己手。面向对象有它非常优越的地方,取其精华(即面向对象思想,面向对象设计),也就是RT-Thread内核对象模型的来源。RT-Thread实时操作系统中包含一个小型的,非常紧凑的对象系统,这个对象系统完全采用C语言实现。

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

1.1 封装—隐藏内部实现

封装是一种信息隐蔽技术,它体现于类的说明,是对象的重要特性。封装使数据和加工该数据的方法(函数)封装为一个整体,以实现独立性很强的模块,使得用户只能见到对象的外特性(对象能接受哪些消息,具有那些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象者的使用分开,使用者不必知晓行为实现的细节,只须用设计者提供的消息来访问该对象。

在C语言中,大多数函数的命名方式是动词+名词的形式,例如要获取一个semaphore,会命名成take_semaphore,重点在take这个动作上。在RT-Thread系统的面向对象编程中刚好相反,命名为rt_sem_take,即名词+动词的形式,重点在名词上,体现了一个对象的方法。另外对于某些方法,仅局限在对象内部使用,它们将采用static修辞把作用范围局限在一个文件的内部。通过这样的方式,把一些不想让用户知道的信息屏蔽在封装里,用户只看到了外层的接口,从而形成了面向对象中的最基本的对象封装实现。

下面给出一段示例代码,展示C语言如何实现封装:

// shape.h
typedef struct{
	int x;
	int y;
}Shape;

Shape * Shape_create(int x, int y);
void Shape_init(Shape * self, int x, int y);
void Shape_move(Shape * self, int dx, int dy);

// shape.c
Shape * Shape_create(int x, int y)
{
	Shape * s = malloc(sizeof(Shape));
	s->x = x;
	s->y = y;
	return s;
}

void Shape_init(Shape * self, int x, int y)
{
	self->x = x;
	self->y = y;
}

void Shape_move(Shape * self, int dx, int dy)
{
	self->x += dx;
	self->y += dy;
}

// main.c
#include "shape.h"
int main(int argc, char *argv[])
{
	Shape * s = Shape_create(0, 0);
	Shape_move(s, 10, 10);
	return 0;
}

这里定义了一个叫做 Shape 的结构体,外界只能通过相关的函数来对这个 Shape 进行操作,例如创建(Shape_create), 初始化(Shape_init), 移动(Shape_move)等,不能直接访问 Shape 的内部数据结构。

如果想隐藏某个方法,即变成私有方法private,只需要在shape.c源文件中相应的方法前加上static限制该函数的作用范围为本文件内就可以了,既然隐藏了该方法也就不必在shape.h中声明该函数了。

虽然这里没有 class 这样的关键字,数据结构和相关操作是分开写的,看起来不太完美, 但确实是实现了封装。
Shape示例对象结构

1.2 继承—复用现有代码

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

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

继续上面的例子,再定义一个Rectangle结构体,里面嵌套Shape结构体,示例代码如下:

// rectangle.h
#include "shape.h"

typedef struct{
	Shape base;
	int width;
	int height;
}Rectangle;

Rectangle * Rectangle_create(int x, int y, int width, int height);

// rectangle.c
Rectangle * Rectangle_create(int x, int y, int width, int height)
{
	Rectangle * r = malloc(sizeof(Rectangle));
	Shape_init((Shape *) r, x, y);
	r->width = width;
	r->height = height;
	return r;
}

// main.c
include "rectangle.h"

int main(int argc, char *argv[])
{
	Rectangle * r = Rectangle_create(5, 5, 20, 10);
	Shape_move((Shape *) r, 30, 40);
	return 0;
}

结构体Rectangle和Shape在内存中的分布关系如下:
结构体Rectangle与Shape的内存分布关系
通过这种组合方式,也算是实现了继承,继承关系如下图所示:
结构体Rectangle与Shape的继承关系

1.3 多态—改写对象行为

对象根据所接收的消息而做出动作。同一消息为不同的对象接受时可产生完全不同的行动,这种现象称为多态性。利用多态性用户可发送一个通用的信息,而将所有的实现细节都留给接受消息的对象自行决定,如是,同一消息即可调用不同的方法。例如:RT-Thread系统中的设备:抽象设备具备接口统一的读写接口。串口是设备的一种,也应支持设备的读写。但串口的读写操作是串口所特有的,不应和其他设备操作完全相同,例如操作串口的操作不应应用于SD卡设备中。

C语言的多态性用到了函数指针,而且在结构体中封装了函数指针,封装函数指针的结构体可以成为虚函数表,比如我们仍然基于上面的例子,在结构体中封装两个函数指针(计算图像面积area、画出图形draw)构成虚函数表ShapeVtbl,然后在结构体Shape中包含该虚函数表,示例代码如下:

// shape.h

typedef struct tag_ShapeVtbl ShapeVtbl;
typedef struct tag_Shape Shape;

struct tag_ShapeVtbl{
	float (* area)(Shape * self);
	void (* draw)(Shape * self);
};

struct tag_Shape{
	ShapeVtbl * vptr;
	int x;
	int y;
};

float Shape_area(Shape * self);

// shape.c

float Shape_area(Shape * self)
{
	return (* self->vptr->area)(self);
}

每当调用area方法时,实际上是去虚函数表ShapeVtbl中找对应的方法,只要子类比如Rectangle能把vptr指向不同的函数表比如RectangleTbl,调用Shape_area方法实际上执行的是子类实现的函数比如Rectangle_area,多态就实现了。下面给出Rectangle与Shape对象的多态实现过程示意图:
Rectangle与Shape对象的多态实现示意图
无论是 Rectangle 对象,还是 Square 对象,在调用 Shape_area 方法的时候, 都需要通过 vptr 这个指针找到虚函数表中的 area 方法,对于 Rectangle,找到的是 Rectangel_area 方法,对于 Square,找到的是 Square_area 方法。

在Linux和RT-Thread中对文件和设备的管理就使用了这种面向对象的方法,下面给出RT-Thread中设备操作结构体(虚函数表)代码示例:

// rt-thread-4.0.1\include\rtdef.h
/**
 * operations set for device object
 */
struct rt_device_ops
{
    /* common device interface */
    rt_err_t  (*init)   (rt_device_t dev);
    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
    rt_err_t  (*close)  (rt_device_t dev);
    rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
    rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
};

多态性的实现受到继承性的支持,利用类继承的层次关系,把具有通用功能的协议存放在类层次中尽可能高的地方,而将实现这一功能的不同方法置于较低层次,这样,在这些低层次上生成的对象就能给通用消息以不同的响应。

二、RT-thread内核对象模型

2.1 静态对象和动态对象

RT-Thread 内核采用面向对象的设计思想进行设计,系统级的基础设施都是一种内核对象,例如线程,信号量,互斥量,定时器等。内核对象分为两类:静态内核对象和动态内核对象,静态内核对象通常放在 RW 段和 ZI 段中,在系统启动后在程序中初始化;动态内核对象则是从内存堆中创建的,而后手工做初始化。

STM32的存储空间分布在之前的博客存储管理与虚拟内存中已经介绍过了,这里再简单回顾下程序内存分布图:
RT-Thread程序内存分布图
STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。

静态对象会占用 RAM 空间,不依赖于内存堆管理器,内存分配时间确定。动态对象则依赖于内存堆管理器,运行时申请 RAM 空间,当对象被删除后,占用的 RAM 空间被释放。这两种方式各有利弊,可以根据实际环境需求选择具体使用方式。

一般属于某个类的对象会有一个统一的创建、析构过程。在RT-Thread中,针对静态对象和动态对象的创建、析构过程并不相同。静态对象内存数据块已经存在,静态对象的创建只需要对它进行初始化(init)即可,对应的析构只需要从管理结构中脱离(detach)即可;动态对象内存数据块还未分配,动态对象的创建需要先分配内存数据块再对其进行初始化,对应的析构也需要删除该内核对象并释放为其分配的内存数据块。举例如下(以semaphore对象为例):

静态对象创建 / 析构: rt_sem_init / rt_sem_detach
动态对象创建 / 析构: rt_sem_create / rt_sem_delete

2.2 内核对象管理架构

RT-Thread 采用内核对象管理系统来访问 / 管理所有内核对象,内核对象包含了内核中绝大部分设施,这些内核对象可以是静态分配的静态对象,也可以是从系统内存堆中分配的动态对象。通过这种内核对象的设计方式,RT-Thread 做到了不依赖于具体的内存分配方式,系统的灵活性得到极大的提高。

RT-Thread 内核对象包括:线程,信号量,互斥量,事件,邮箱,消息队列和定时器,内存池,设备驱动等。对象容器中包含了每类内核对象的信息,包括对象类型,大小等。对象容器给每类内核对象分配了一个链表,所有的内核对象都被链接到该链表上,如图 RT-Thread 的内核对象容器及链表如下图所示:
RT-thread内核对象容器及链表
对于每一种具体内核对象和对象控制块,除了基本结构外,还有自己的扩展属性(私有属性),例如,对于线程控制块,在基类对象基础上进行扩展,增加了线程状态、优先级等属性,这些属性在基类对象的操作中不会用到,只有在与具体线程相关的操作中才会使用。因此从面向对象的观点,可以认为每一种具体对象是抽象对象的派生,继承了基本对象的属性并在此基础上扩展了与自己相关的属性。下图则显示了 RT-Thread 中各类内核对象的派生和继承关系:
RT-Thread内核对象派生与继承关系
在对象管理模块中,定义了通用的数据结构,用来保存各种对象的共同属性,各种具体对象只需要在此基础上加上自己的某些特别的属性,就可以清楚的表示自己的特征。这种设计方法有两大优点:

  • 提高了系统的可重用性和扩展性,增加新的对象类别很容易,只需要继承通用对象的属性再加少量扩展即可;
  • 提供统一的对象操作方式,简化了各种具体对象的操作,提高了系统的可靠性。

2.3 内核对象数据结构

在.\include\rtdef.h头文件中可以找到内核对象的结构定义如下:

// rt-thread-4.0.1\include\rtdef.h
/**
 * Base structure of Kernel object
 */
struct rt_object
{
    char       name[RT_NAME_MAX];                       /**< name of kernel object */
    rt_uint8_t type;                                    /**< type of kernel object */
    rt_uint8_t flag;                                    /**< flag of kernel object */

#ifdef RT_USING_MODULE
    void      *module_id;                               /**< id of application module */
#endif
    rt_list_t  list;                                    /**< list node of kernel object */
};
typedef struct rt_object *rt_object_t;                  /**< Type for kernel objects. */

/**
 *  The object type can be one of the follows with specific
 *  macros enabled:
 *  - Thread
 *  - Semaphore
 *  - Mutex
 *  - Event
 *  - MailBox
 *  - MessageQueue
 *  - MemHeap
 *  - MemPool
 *  - Device
 *  - Timer
 *  - Module
 *  - Unknown
 *  - Static
 */
enum rt_object_class_type
{
    RT_Object_Class_Null   = 0,                         /**< The object is not used. */
    RT_Object_Class_Thread,                             /**< The object is a thread. */
    RT_Object_Class_Semaphore,                          /**< The object is a semaphore. */
    RT_Object_Class_Mutex,                              /**< The object is a mutex. */
    RT_Object_Class_Event,                              /**< The object is a event. */
    RT_Object_Class_MailBox,                            /**< The object is a mail box. */
    RT_Object_Class_MessageQueue,                       /**< The object is a message queue. */
    RT_Object_Class_MemHeap,                            /**< The object is a memory heap */
    RT_Object_Class_MemPool,                            /**< The object is a memory pool. */
    RT_Object_Class_Device,                             /**< The object is a device */
    RT_Object_Class_Timer,                              /**< The object is a timer. */
    RT_Object_Class_Module,                             /**< The object is a module. */
    RT_Object_Class_Unknown,                            /**< The object is unknown. */
    RT_Object_Class_Static = 0x80                       /**< The object is a static object. */
};

/**
 * Double List structure
 */
struct rt_list_node
{
    struct rt_list_node *next;                          /**< point to next node. */
    struct rt_list_node *prev;                          /**< point to prev node. */
};
typedef struct rt_list_node rt_list_t;                  /**< Type for lists. */

// projects\stm32l4xx\rtconfig.h
#define RT_NAME_MAX 8

内核对象名称是一个自定义字符串,需要注意名称最大长度有限制(RT_NAME_MAX宏定义在rtconfig.h中)。

从类型说明看,如果是静态对象,对象类型的最高位是 1(是RT_Object_Class_Static 与其他对象类型的与操作),否则就是动态对象,系统最多能够容纳的对象类别数目是 127 个。

内核对象标志在不同的派生对象类型中含义不同,比如在IPC(Inter-Process Communication)中flag bit0若为0表示RT_IPC_FLAG_FIFO:按消息队列先进先出的方式处理.,若为1表示RT_IPC_FLAG_PRIO:按线程优先级的方式处理。

每个内核对象的具体实例被以双向链表的形式组织起来,对内核对象的管理操作有相当一部分是对双向链表的操作,RT-Thread为此实现了一套单双链表管理操作的API接口函数(位于rt-thread-4.0.1\include\rtservice.h中),列举几个最常用的链表操作接口函数如下:

// rt-thread-4.0.1\include\rtservice.h

/**
 * @brief initialize a list
 * @param l list to be initialized
 */
rt_inline void rt_list_init(rt_list_t *l)
{
    l->next = l->prev = l;
}

/**
 * @brief insert a node after a list
 * @param l list to insert it
 * @param n new node to be inserted
 */
rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
    l->next->prev = n;
    n->next = l->next;

    l->next = n;
    n->prev = l;
}

/**
 * @brief insert a node before a list
 * @param n new node to be inserted
 * @param l list to insert it
 */
rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
    l->prev->next = n;
    n->prev = l->prev;

    l->prev = n;
    n->next = l;
}

/**
 * @brief remove node from list.
 * @param n the node to remove from the list.
 */
rt_inline void rt_list_remove(rt_list_t *n)
{
    n->next->prev = n->prev;
    n->prev->next = n->next;

    n->next = n->prev = n;
}

/**
 * @brief tests whether a list is empty
 * @param l the list to test.
 */
rt_inline int rt_list_isempty(const rt_list_t *l)
{
    return l->next == l;
}

/**
 * @brief get the list length
 * @param l the list to get.
 */
rt_inline unsigned int rt_list_len(const rt_list_t *l)
{
    unsigned int len = 0;
    const rt_list_t *p = l;
    while (p->next != l){
        p = p->next;
        len ++;
    }
    return len;
}

/**
 * @brief get the struct for this entry
 * @param node the entry point
 * @param type the type of structure
 * @param member the name of list in structure
 */
#define rt_list_entry(node, type, member) \
    rt_container_of(node, type, member)

/**
 * rt_container_of - return the member address of ptr, if the type of ptr is the
 * struct type.
 */
#define rt_container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

宏定义rt_container_of也是C语言结构体实现面向对象编程的一个技巧,该宏可以通过结构体的某成员指针及结构体类型获取到该结构体的指针,有点类似于C++类Class中this指针的概念。有了rt_container_of宏定义,RT-Thread就可以通过内核对象控制块中的任一成员地址,获取到该内核对象实例句柄,进而访问该内核对象的其它成员变量,方便了C语言对象化模型的实现。该宏定义用法举例如下:

int main(int argc, char **argv)
{
	struct rt_object	obj, *pobj;
	
	obj.list = object_list;
	pobj = rt_container_of(&obj.list, struct rt_object, list);
	
	return 0;
}

2.4 内核对象容器

RT-Thread使用内核对象容器来管理同一类型的内核对象,并将其放入同一链表中,便于访问,内核对象信息的结构定义如下:

// rt-thread-4.0.1\include\rtdef.h

/**
 * The information of the kernel object
 */
struct rt_object_information
{
    enum rt_object_class_type type;                     /**< object class type */
    rt_list_t                 object_list;              /**< object list */
    rt_size_t                 object_size;              /**< object size */
};

一类对象由一个 rt_object_information 结构体来管理,每一个这类对象的具体实例都通过链表的形式挂接在 object_list 上。而这一类对象的内存块尺寸由 object_size 标识出来(每一类对象的具体实例,他们占有的内存块大小都是相同的)。

由于每一类内核对象都对应着有一个这样的内核对象容器来管理,那么所有内核对象容器整体就叫做内核对象管理系统。内核对象管理系统是用一个rt_object_information数组来实现的,代码如下:

// rt-thread-4.0.1\src\object.c

#define _OBJ_CONTAINER_LIST_INIT(c)     \
    {&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)}
    
static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] =
{
    /* initialize object container - thread */
    {RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread)},
#ifdef RT_USING_SEMAPHORE
    /* initialize object container - semaphore */
    {RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore)},
#endif
#ifdef RT_USING_MUTEX
    /* initialize object container - mutex */
    {RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex), sizeof(struct rt_mutex)},
#endif
#ifdef RT_USING_EVENT
    /* initialize object container - event */
    {RT_Object_Class_Event, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event), sizeof(struct rt_event)},
#endif
#ifdef RT_USING_MAILBOX
    /* initialize object container - mailbox */
    {RT_Object_Class_MailBox, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox), sizeof(struct rt_mailbox)},
#endif
#ifdef RT_USING_MESSAGEQUEUE
    /* initialize object container - message queue */
    {RT_Object_Class_MessageQueue, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue), sizeof(struct rt_messagequeue)},
#endif
#ifdef RT_USING_MEMHEAP
    /* initialize object container - memory heap */
    {RT_Object_Class_MemHeap, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap), sizeof(struct rt_memheap)},
#endif
#ifdef RT_USING_MEMPOOL
    /* initialize object container - memory pool */
    {RT_Object_Class_MemPool, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool), sizeof(struct rt_mempool)},
#endif
#ifdef RT_USING_DEVICE
    /* initialize object container - device */
    {RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device)},
#endif
    /* initialize object container - timer */
    {RT_Object_Class_Timer, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer), sizeof(struct rt_timer)},
#ifdef RT_USING_MODULE
    /* initialize object container - module */
    {RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), sizeof(struct rt_dlmodule)},
#endif
};

/*
 * define object_info for the number of rt_object_container items.
 */
enum rt_object_info_type
{
    RT_Object_Info_Thread = 0,                         /**< The object is a thread. */
#ifdef RT_USING_SEMAPHORE
    RT_Object_Info_Semaphore,                          /**< The object is a semaphore. */
#endif
#ifdef RT_USING_MUTEX
    RT_Object_Info_Mutex,                              /**< The object is a mutex. */
#endif
#ifdef RT_USING_EVENT
    RT_Object_Info_Event,                              /**< The object is a event. */
#endif
#ifdef RT_USING_MAILBOX
    RT_Object_Info_MailBox,                            /**< The object is a mail box. */
#endif
#ifdef RT_USING_MESSAGEQUEUE
    RT_Object_Info_MessageQueue,                       /**< The object is a message queue. */
#endif
#ifdef RT_USING_MEMHEAP
    RT_Object_Info_MemHeap,                            /**< The object is a memory heap */
#endif
#ifdef RT_USING_MEMPOOL
    RT_Object_Info_MemPool,                            /**< The object is a memory pool. */
#endif
#ifdef RT_USING_DEVICE
    RT_Object_Info_Device,                             /**< The object is a device */
#endif
    RT_Object_Info_Timer,                              /**< The object is a timer. */
#ifdef RT_USING_MODULE
    RT_Object_Info_Module,                             /**< The object is a module. */
#endif
    RT_Object_Info_Unknown,                            /**< The object is unknown. */
};

内核对象容器数组rt_object_container[RT_Object_Info_Unknown]中的每个成员都是一个内核对象信息结构体rt_object_information,数组元素个数为RT_Object_Info_Unknown (RT_Object_Info_Unknown为枚举类型rt_object_info_type最后一个元素,可以表示该枚举类型的元素数量)。rt_object_information结构体中的三个成员变量前面已经介绍过了,其中对象类型和对象大小两个成员变量的初始化不难理解,但rt_object_container[]中使用_OBJ_CONTAINER_LIST_INIT©宏定义来初始化内核对象容器链表乍一看可能有些费解。其实宏定义_OBJ_CONTAINER_LIST_INIT©的意思是将链表c的前一节点和后一节点在初始化时都指向本身所在地址,相当于下面的代码:

rt_object_container[c].object_list *next = &(rt_object_container[c].object_list);
rt_object_container[c].object_list *prev = &(rt_object_container[c].object_list);

为了方便从对象容器数组获取对象信息句柄,RT-Thread提供了一个获取内核信息句柄的接口函数rt_object_get_information,该函数可以通过对象类型获取相应的对象信息句柄,其实现代码如下:

// rt-thread-4.0.1\src\object.c

/**
 * This function will return the specified type of object information.
 * @param type the type of object
 * @return the object type information or RT_NULL
 */
struct rt_object_information *rt_object_get_information(enum rt_object_class_type type)
{
    int index;

    for (index = 0; index < RT_Object_Info_Unknown; index ++)
        if (rt_object_container[index].type == type) 
        	return &rt_object_container[index];

    return RT_NULL;
}
RTM_EXPORT(rt_object_get_information);

2.5 内核对象接口函数

前面也介绍过,针对静态对象和动态对象的构造、析构函数并不相同,静态对象的构造、析构函数实现代码如下:

// rt-thread-4.0.1\src\object.c

/**
 * This function will initialize an object and add it to object system
 * management.
 *
 * @param object the specified object to be initialized.
 * @param type the object type.
 * @param name the object name. In system, the object's name must be unique.
 */
void rt_object_init(struct rt_object         *object,
                    enum rt_object_class_type type,
                    const char               *name)
{
    register rt_base_t temp;
    struct rt_list_node *node = RT_NULL;
    struct rt_object_information *information;
#ifdef RT_USING_MODULE
    struct rt_dlmodule *module = dlmodule_self();
#endif

    /* get object information */
    information = rt_object_get_information(type);
    RT_ASSERT(information != RT_NULL);

    /* check object type to avoid re-initialization */

    /* enter critical */
    rt_enter_critical();
    /* try to find object */
    for (node  = information->object_list.next;
            node != &(information->object_list);
            node  = node->next)
    {
        struct rt_object *obj;

        obj = rt_list_entry(node, struct rt_object, list);
        RT_ASSERT(obj != object);
    }
    /* leave critical */
    rt_exit_critical();

    /* initialize object's parameters */
    /* set object type to static */
    object->type = type | RT_Object_Class_Static;
    /* copy name */
    rt_strncpy(object->name, name, RT_NAME_MAX);

    RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));

    /* lock interrupt */
    temp = rt_hw_interrupt_disable();

#ifdef RT_USING_MODULE
    if (module)
    {
        rt_list_insert_after(&(module->object_list), &(object->list));
        object->module_id = (void *)module;
    }
    else
#endif
    {
        /* insert object into information object list */
        rt_list_insert_after(&(information->object_list), &(object->list));
    }

    /* unlock interrupt */
    rt_hw_interrupt_enable(temp);
}

/**
 * This function will detach a static object from object system,
 * and the memory of static object is not freed.
 *
 * @param object the specified object to be detached.
 */
void rt_object_detach(rt_object_t object)
{
    register rt_base_t temp;

    /* object check */
    RT_ASSERT(object != RT_NULL);

    RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));

    /* reset object type */
    object->type = 0;

    /* lock interrupt */
    temp = rt_hw_interrupt_disable();

    /* remove from old list */
    rt_list_remove(&(object->list));

    /* unlock interrupt */
    rt_hw_interrupt_enable(temp);
}

#define RT_OBJECT_HOOK_CALL(func, argv) \
    do { if ((func) != RT_NULL) func argv; } while (0)

静态对象的初始化和从链表脱离并不涉及内存数据块的分配和释放,但需要用户通过变量定义在初始化前为内核对象实例分配内存空间。

动态对象的构造、析构函数实现代码如下:

// rt-thread-4.0.1\src\object.c

/**
 * This function will allocate an object from object system
 * @param type the type of object
 * @param name the object name. In system, the object's name must be unique.
 * @return object
 */
rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name)
{
    struct rt_object *object;
    register rt_base_t temp;
    struct rt_object_information *information;
#ifdef RT_USING_MODULE
    struct rt_dlmodule *module = dlmodule_self();
#endif

    RT_DEBUG_NOT_IN_INTERRUPT;

    /* get object information */
    information = rt_object_get_information(type);
    RT_ASSERT(information != RT_NULL);

    object = (struct rt_object *)RT_KERNEL_MALLOC(information->object_size);
    if (object == RT_NULL)
    {
        /* no memory can be allocated */
        return RT_NULL;
    }

    /* clean memory data of object */
    rt_memset(object, 0x0, information->object_size);

    /* initialize object's parameters */

    /* set object type */
    object->type = type;

    /* set object flag */
    object->flag = 0;

    /* copy name */
    rt_strncpy(object->name, name, RT_NAME_MAX);

    RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));

    /* lock interrupt */
    temp = rt_hw_interrupt_disable();

#ifdef RT_USING_MODULE
    if (module)
    {
        rt_list_insert_after(&(module->object_list), &(object->list));
        object->module_id = (void *)module;
    }
    else
#endif
    {
        /* insert object into information object list */
        rt_list_insert_after(&(information->object_list), &(object->list));
    }

    /* unlock interrupt */
    rt_hw_interrupt_enable(temp);

    /* return object */
    return object;
}

/**
 * This function will delete an object and release object memory.
 * @param object the specified object to be deleted.
 */
void rt_object_delete(rt_object_t object)
{
    register rt_base_t temp;

    /* object check */
    RT_ASSERT(object != RT_NULL);
    RT_ASSERT(!(object->type & RT_Object_Class_Static));

    RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));

    /* reset object type */
    object->type = 0;

    /* lock interrupt */
    temp = rt_hw_interrupt_disable();

    /* remove from old list */
    rt_list_remove(&(object->list));

    /* unlock interrupt */
    rt_hw_interrupt_enable(temp);

    /* free the memory of object */
    RT_KERNEL_FREE(object);
}

// rt-thread-4.0.1\include\rtdef.h
#define RT_KERNEL_MALLOC(sz)            rt_malloc(sz)
#define RT_KERNEL_FREE(ptr)             rt_free(ptr)

动态对象的创建和删除依赖于内存堆管理器,其中RT_KERNEL_MALLOC与RT_KERNEL_FREE是RT-Thread提供的内存管理统一接口。在进行内存分配时,可用内存是否满足需求,内存是否分配成功有一定的不确定性,所以为动态对象分配内存空间后要检查内存分配是否成功,以免引入错误。

还有一些判断内核对象是否为静态对象、获取对象类型、根据对象名称与类型查找对象句柄等接口函数,这些函数的实现代码如下:

/**
 * This function will judge the object is system object or not.
 * Normally, the system object is a static object and the type
 * of object set to RT_Object_Class_Static.
 * @param object the specified object to be judged.
 * @return RT_TRUE if a system object, RT_FALSE for others.
 */
rt_bool_t rt_object_is_systemobject(rt_object_t object)
{
    /* object check */
    RT_ASSERT(object != RT_NULL);

    if (object->type & RT_Object_Class_Static)
        return RT_TRUE;

    return RT_FALSE;
}

/**
 * This function will return the type of object without
 * RT_Object_Class_Static flag.
 * @param object the specified object to be get type.
 * @return the type of object.
 */
rt_uint8_t rt_object_get_type(rt_object_t object)
{
    /* object check */
    RT_ASSERT(object != RT_NULL);

    return object->type & ~RT_Object_Class_Static;
}

/**
 * This function will find specified name object from object
 * container.
 * @param name the specified name of object.
 * @param type the type of object
 * @return the found object or RT_NULL if there is no this object
 * in object container.
 * @note this function shall not be invoked in interrupt status.
 */
rt_object_t rt_object_find(const char *name, rt_uint8_t type)
{
    struct rt_object *object = RT_NULL;
    struct rt_list_node *node = RT_NULL;
    struct rt_object_information *information = RT_NULL;

    /* parameter check */
    if ((name == RT_NULL) || (type > RT_Object_Class_Unknown))
        return RT_NULL;

    /* which is invoke in interrupt status */
    RT_DEBUG_NOT_IN_INTERRUPT;

    /* enter critical */
    rt_enter_critical();

    /* try to find object */
    if (information == RT_NULL)
    {
        information = rt_object_get_information((enum rt_object_class_type)type);
        RT_ASSERT(information != RT_NULL);
    }
    for (node  = information->object_list.next;
            node != &(information->object_list);
            node  = node->next)
    {
        object = rt_list_entry(node, struct rt_object, list);
        if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0)
        {
            /* leave critical */
            rt_exit_critical();

            return object;
        }
    }

    /* leave critical */
    rt_exit_critical();

    return RT_NULL;
}

更多文章:

  • 6
    点赞
  • 4
    评论
  • 15
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 4 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页

打赏作者

流云IoT

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值