struct rt_object
{
/* name of kernel object */
char name[RT_NAME_MAX];
/* type of kernel object */
rt_uint8_t type;
/* flag of kernel object */
rt_uint8_t flag;
/* list pointer of kernel object */
rt_list_t list;
};
为什么要给对象命名?
name保存着对象的名字。尽管就操作系统自身的运行而言,对象名称并不是必须的,但为其命名可以使我们在调试时区分各个对象。FinSH组件可以打印出对象名称。
建议的命名规则
以下是我在使用RTT和其他一些RTOS(如uc/OS)的过程中自己使用的一套对象命名规则:
对象类型+对象名称
对象类型使用两个字符表示。例如,td表示thread,mx表示mutex。
假如我有一个Thread,函数名为PollUserInput,那么我给这个thread命名为tdPUI。这样,一方面防止对象名重复,另一方面,在使用FinSH调试时,可以根据对象名知道该对象的用途。
flag有什么用?
例如,flag中有一位表示该对象是系统对象(静态分配)还是动态对象。当一个对象不再使用时,将根据这一位决定是否释放对象所占据的内存。
list
有了这个成员,各个相同类型的对象就可以形成链表,以便管理。
rt_object成员一般放置于各对象的开头。例如:
struct rt_ipc_object
{
struct rt_object parent;
rt_list_t suspend_thread;
};
为什么要把rt_object放在开头?
这就保证了各对象的起始地址同样就是嵌入在该对象头部的rt_object的起始地址。这样我们就可以方便的使用指针统一访问各对象,而不管该对象是什么类型。
下面这条语句访问某内核对象内嵌的rt_object对象中的list成员,而不管该对象是什么类型:
((struct rt_object *)(&some_kernel_obj))->list;
初步了解内核对象后,我们来看一个操作系统最基础的设施:线程和线程调度。
线程对象由struct rt_thread描述,定义于rtdef.h中。这里就不再将该结构体列出,直接分析其成员。作用比较明显的成员不再一一分析,仅分析比较关键的几个成员。
开头的4个成员(name, type, flags, list)实际上就是rt_object结构的成员。绝大多数内核对象(rt_sem)在其头部嵌入一个公共的rt_object结构,唯有rt_thread将这四个成员复制过来,应该是出于效率方面的考虑。
接下来的tlist成员也是一个链表项,根据线程状态的不同,将利用该表项将线程对象挂到不同的链表中去。例如,如果线程处于ready状态,就将
通过tlist将该线程挂到ready线程链表;如果线程处于suspend状态,就通过tlist将线程挂到suspend线程链表。
current_priority和init_priority
init_priority就是你创建线程时为其指定的优先级。一般情况下这两个值相等。但RTT允许你动态改变线程的优先级,如果你这么做了,那么这两个值是不相等的。
number、high_mask和number_mask
这三个成员用于快速查找当前最高优先级的线程。后面我们分析线程调度时再回到这三个成员上来。
event_set和event_info
用于进程间的IPC。在IPC相关代码中再详细分析其作用。
init_tick和remaining_tick
RTT允许多个线程使用同一优先级,在同一优先级下的线程使用时间片调度。init_tick就是分配给该线程的时间片。当某个优先级的线程刚被选中进入运行态时,remaining_tick将被赋予init_tick的值,并随着线程的执行而递减。当remaining_tick递减到0,表明该线程的时间片用完,同一优先级的下一个就绪线程将被选中进入运行态。
thread_timer
如果你在线程内调用sleep,或者在等待资源时指定了超时,RTT将使用thread_timer计算超时时间。