当前位置: 首页 > news >正文

Rt-thread源码剖析(1)——内核对象

前言

        该系列基于rtthread-nano的内核源码,来研究RTOS的底层逻辑,本文介绍RTT的内核对象,对于其他RTOS来说也可供参考,万变不离其宗,大家都是互相借鉴,实现不会差太多。

内核对象容器

        首先要明确的一点是什么是内核对象,在RTT中,利用结构体之间的嵌套,实现了一种类似面向对象的设计。所以这里的内核对象,是指线程,信号量,互斥量,事件,邮箱,消息队列和定时器,内存池,设备驱动等等任何通过动态/静态创建出来的对象。

       其次,需要知道的是,RTT是通过一个rt_object_container来管理这些所有的内核对象的。他是怎么做到的呢?我们来看一下(部分)代码就知道了(也可以直接看下面的总结)

//只展示部分代码
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};

        可以看到,本质上内核容器对象就是一个数组,里面的每个成员都是一个结构体如下所示

/*** 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_list_t又是一个双向链表,因此,rt_object_container其实就是一个rt_object_information结构体构成的数组,这个结构体里面又维护了

1.每种内核对象的类型

2.一个双向链表用于链接每个同类内核对象

3.该内核对象创建时需要的内存大小

程序运行过程中,每个创建的内核对象都会被放在这个内核对象容器中,最终形成如下结构

内核对象创建

        不同的内核对象会应用不同的创建函数,例如对于动态创建来说

        线程创建  ——> rt_thread_create

        定时器创建  ——>  rt_timer_create

        但只要查看这两个创建函数的源码,都可以看到他们都调用了同一个rt_object_allocate函数,这也是上文提到的,内核对象容器的部分意义所在。它抽象出了所有内核对象的一些共同属性,这些共同属性通过rt_object_allocate函数完成初始化,然后再通过自己的函数完成特有属性的初始化。

        本文重点阐述object的创建删除等操作,特定内核对象的创建将在后文表述。

动态创建

        RTT在动态创建内核对象时,用到了RT_KERNEL_MALLOC来动态分配内存,这里并不做展开,后续会专门对内存管理进行说明,现在我们看一下动态创建内核对象的源码

rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name)
{struct rt_object *object;rt_base_t level;struct rt_object_information *information;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 */level = rt_hw_interrupt_disable();{/* insert object into information object list */rt_list_insert_after(&(information->object_list), &(object->list));}/* unlock interrupt */rt_hw_interrupt_enable(level);/* return object */return object;
}

代码做了以下这些工作:

        1.通过RT_DEBUG_NOT_IN_INTERRUPT确认当前执行环境不在中断中,中断需要快速完成,动态申请内存显然是不合适的

        2.根据不同对象类型动态申请一块内存

        3.将该内存清零

        4.给内核对象共有的属性:类型,名称,标志位赋值

        5.将该内核对象插入内核对象容器

这里做一下展开说明,内核对象的结构体如下所示,他所需的大小也许是64字节:

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 */rt_list_t  list;                                    /**< list node of kernel object */
};

而一个具体的内核对象,如timer,他的结构体如下所示,他需要的内存大小远大于64字节

struct rt_timer
{struct rt_object parent;                            /**< inherit from rt_object */rt_list_t        row[RT_TIMER_SKIP_LIST_LEVEL];void (*timeout_func)(void *parameter);              /**< timeout function */void            *parameter;                         /**< timeout function's parameter */rt_tick_t        init_tick;                         /**< timer timeout tick */rt_tick_t        timeout_tick;                      /**< timeout tick */
};

在 rt_object_allocate 函数中

object = (struct rt_object *)RT_KERNEL_MALLOC(information->object_size);

申请的内存大小是具体的(如timer)需要的内存空间,然后用object对象先将他接收,把前64个字节进行赋值,其余内存空间进行清零,在rt_timer_create则对这些未0的字节进行赋值。

内核对象创建hook

   RT_OBJECT_HOOK_CALL 是 RT-Thread 中用于调用钩子函数的宏定义。创建的这个object对象传递会被传递给钩子函数,接下来简单讲一下如何注册钩子函数

void my_attach_hook(struct rt_object *object) 
{ // 用户自定义的处理代码 
}
rt_object_attach_sethook(my_attach_hook);

        在上述代码中,my_attach_hook 是用户定义的钩子函数,实现了对附加对象事件的处理。通过调用 rt_object_attach_sethook,将该函数注册为对象附加事件的钩子。当系统中有对象被附加时,my_attach_hook 会被自动调用。

 静态创建    

        在内核对象进行静态创建时,内存的分配是在编译时就完成的,因此创建的时间是完全可控的,对于严格的实时操作系统来说,这样的分配方式是更值得推荐的,只是编程上会没有那么舒适我们以timer的静态创建为例

void rt_timer_init(rt_timer_t  timer,const char *name,void (*timeout)(void *parameter),void       *parameter,rt_tick_t   time,rt_uint8_t  flag)
{/* parameter check */RT_ASSERT(timer != RT_NULL);RT_ASSERT(timeout != RT_NULL);RT_ASSERT(time < RT_TICK_MAX / 2);/* timer object initialization */rt_object_init(&(timer->parent), RT_Object_Class_Timer, name);_timer_init(timer, timeout, parameter, time, flag);
}

可以看到他在创建时调用了

rt_object_init(&(timer->parent), RT_Object_Class_Timer, name);

        任意静态的内核对象在创建时都会调用该函数,可以看到函数的入参是timer->paraent的地址,是在创建时就确定的一块地址空间;object_init的源码如下

void rt_object_init(struct rt_object         *object,enum rt_object_class_type type,const char               *name)
{rt_base_t level;struct rt_list_node *node = RT_NULL;struct rt_object_information *information;/* get object information */information = rt_object_get_information(type);RT_ASSERT(information != RT_NULL);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);if (obj) /* skip warning when disable debug */{RT_ASSERT(obj != object);}}rt_exit_critical();/* 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 */level = rt_hw_interrupt_disable();/* insert object into information object list */rt_list_insert_after(&(information->object_list), &(object->list));/* unlock interrupt */rt_hw_interrupt_enable(level);
}

这里比起动态创建值得说的有如下几点:

1.在 rt_object_init 函数中,系统会遍历对象列表,检查是否存在同名对象。为了防止在遍历过程中其他线程对对象列表进行修改,可能导致数据不一致或系统崩溃,必须在遍历前关闭调度器,进入临界区。遍历完成后,再退出临界区,恢复调度器的正常运行。这种做法确保了对象管理操作的原子性和系统的稳定性

    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);if (obj) /* skip warning when disable debug */{RT_ASSERT(obj != object);}}rt_exit_critical();

2.在往内核容器中插入内核对象的时候,采取了关闭硬件中断的做法,这里和上面关闭调度器的做法产生了差异,这是因为:

关闭调度器(调度锁):

        通过调用 rt_enter_critical()rt_exit_critical() 函数,可以进入和退出调度临界区。在调度锁持有期间,系统停止线程调度,不会发生线程切换,但仍然响应中断。这意味着当前线程独占 CPU 资源,其他线程即使优先级更高也无法抢占执行。这种方法适用于需要防止线程切换的临界区,但对中断的响应不受影响。

关闭中断:

        通过调用 rt_hw_interrupt_disable()rt_hw_interrupt_enable() 函数,可以禁用和启用中断。在中断被禁用期间,系统不会响应任何中断,包括时钟中断,从而也不会进行线程调度。这种方法确保了代码执行的原子性,适用于需要防止中断干扰的关键操作,例如修改与中断服务程序共享的数据。

为何在插入内核对象时关闭中断:

        在向内核对象列表中插入新对象时,涉及对全局链表的修改。如果在插入过程中发生中断,且中断服务程序也对该链表进行操作,可能导致数据不一致或系统崩溃。因此,需要通过关闭中断来保护这段临界区代码,确保插入操作的原子性。

为何在遍历对象列表时关闭调度器:

        在遍历对象列表时,如果其他线程可能对该列表进行修改(例如插入或删除对象),可能导致遍历过程中的数据不一致。通过关闭调度器,可以防止其他线程在遍历期间进行修改操作,确保遍历过程的安全性。由于遍历操作可能耗时较长,关闭中断会影响系统的实时性,因此选择关闭调度器以平衡安全性和实时性。

        综上所述,检测内核容器中是否存在同名对象时,仅仅设计对内核容器的遍历查询;但插入时要修改内核容器的链表,修改链表时出现问题,将造成整个系统的崩溃,需要更加严格的保护

内核对象删除

        内核对象的删除相对简单,静态删除和动态删除差异不大,仅仅是动态删除时会释放动态创建时malloc出来的内存,而静态没有这个逻辑;其余则仅仅需要将内核对象容器中的链表上将该对象删掉就行了,简单看下源码

动态删除

void rt_object_delete(rt_object_t object)
{rt_base_t level;/* 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 = RT_Object_Class_Null;/* lock interrupt */level = rt_hw_interrupt_disable();/* remove from old list */rt_list_remove(&(object->list));/* unlock interrupt */rt_hw_interrupt_enable(level);/* free the memory of object */RT_KERNEL_FREE(object);
}

静态删除

void rt_object_detach(rt_object_t object)
{rt_base_t level;/* object check */RT_ASSERT(object != RT_NULL);RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));/* reset object type */object->type = 0;/* lock interrupt */level = rt_hw_interrupt_disable();/* remove from old list */rt_list_remove(&(object->list));/* unlock interrupt */rt_hw_interrupt_enable(level);
}


http://www.mrgr.cn/news/92604.html

相关文章:

  • 自然语言处理:稀疏向量表示
  • 智慧校园平台在学生学习与生活中的应用
  • [Lc优选算法] 双指针 | 移动零 | 复写零 | 快乐数
  • MySQL 创建指定IP用户并赋予全部权限(兼容8.0以下及8.0以上版本)
  • NFC拉起微信小程序申请URL scheme 汇总
  • Pytest自定义测试用例执行顺序
  • 【漫话机器学习系列】109.线性无关(Linearly Independent)
  • IO流(师从韩顺平)
  • 分类预测 | Matlab实现CPO-SVM冠豪猪算法优化支持向量机多特征分类预测
  • 【DeepSeek-R1背后的技术】系列十三:归一化方式介绍(BatchNorm, LayerNorm, Instance Norm 和 GroupNorm)
  • JVM线程分析详解
  • win11编译pytorch cuda128版本流程
  • 点云 PCL 滤波在自动驾驶的用途。
  • 部分简单字符串算法题解(自用)
  • string类在OJ的使用
  • 人类驾驶的人脑两种判断模式(反射和预判)-->自动驾驶两种AI模式
  • docker通用技术介绍
  • Hadoop架构详解
  • java网络编程--基于TCP协议的网络编程
  • MySQL 用户权限管理深度解析:从基础到高阶实践(2000字指南)