在 Zephyr 内核调试工具 Object Cores 一文中了解了 Object Cores 的用法,本文进一步说明其实现原理和其在 Zephyr 中的使用状态。
原理简要 链接到标题
对象管理 链接到标题
Object Cores 其本质就是一个 单链表 ,将同一类对象加入到一个链表中,然后提供遍历函数进行访问。 一个 Object Cores 管理的对象对应一个单链表,其信息使用 struct k_obj_type
管理
/** Object type structure */
struct k_obj_type {
sys_snode_t node; //Object Cores 节点
sys_slist_t list; //同类对象链表
uint32_t id; //对象 ID
size_t obj_core_offset; //obj_core 在被管理对象中的偏移地址,因为有 CONTAINER_OF, 该偏移值目前没有使用
# ifdef CONFIG_OBJ_CORE_STATS
// 统计状态描述符
struct k_obj_core_stats_desc *stats_desc;
# endif
};
在 z_obj_type_init
进行初始化时会创建一个单链表,并将 id
和 obj_core_offset
初始化
struct k_obj_type *z_obj_type_init ( struct k_obj_type *type,
uint32_t id, size_t off )
{
//初始化对象管理链表
sys_slist_init ( &type->list ) ;
sys_slist_append ( &z_obj_type_list, &type->node ) ;
//初始化 id
type->id = id;
//初始化 offset
type->obj_core_offset = off;
return type;
}
每一类对象会有一个类型 id, id 使用下列宏进行字符串转 32bit 值定义
# define K_OBJ_TYPE_ID_GEN ( s ) (( s [0] << 24 ) | ( s [1] << 16 ) | ( s [2] << 8 ) | ( s [3] ))
在 zephyr/include/zephyr/kernel/obj_core.h
定义了一组为内核预留的类型,用户要自定义类型时需要避开这些预定义类型
/* Known kernel object types */
/** Condition variable object type */
# define K_OBJ_TYPE_CONDVAR_ID K_OBJ_TYPE_ID_GEN ( "COND" )
/** CPU object type */
# define K_OBJ_TYPE_CPU_ID K_OBJ_TYPE_ID_GEN ( "CPU_" )
/** Event object type */
# define K_OBJ_TYPE_EVENT_ID K_OBJ_TYPE_ID_GEN ( "EVNT" )
/** FIFO object type */
# define K_OBJ_TYPE_FIFO_ID K_OBJ_TYPE_ID_GEN ( "FIFO" )
/** Kernel object type */
# define K_OBJ_TYPE_KERNEL_ID K_OBJ_TYPE_ID_GEN ( "KRNL" )
/** LIFO object type */
# define K_OBJ_TYPE_LIFO_ID K_OBJ_TYPE_ID_GEN ( "LIFO" )
/** Memory block object type */
# define K_OBJ_TYPE_MEM_BLOCK_ID K_OBJ_TYPE_ID_GEN ( "MBLK" )
/** Mailbox object type */
# define K_OBJ_TYPE_MBOX_ID K_OBJ_TYPE_ID_GEN ( "MBOX" )
/** Memory slab object type */
# define K_OBJ_TYPE_MEM_SLAB_ID K_OBJ_TYPE_ID_GEN ( "SLAB" )
/** Message queue object type */
# define K_OBJ_TYPE_MSGQ_ID K_OBJ_TYPE_ID_GEN ( "MSGQ" )
/** Mutex object type */
# define K_OBJ_TYPE_MUTEX_ID K_OBJ_TYPE_ID_GEN ( "MUTX" )
/** Pipe object type */
# define K_OBJ_TYPE_PIPE_ID K_OBJ_TYPE_ID_GEN ( "PIPE" )
/** Semaphore object type */
# define K_OBJ_TYPE_SEM_ID K_OBJ_TYPE_ID_GEN ( "SEM4" )
/** Stack object type */
# define K_OBJ_TYPE_STACK_ID K_OBJ_TYPE_ID_GEN ( "STCK" )
/** Thread object type */
# define K_OBJ_TYPE_THREAD_ID K_OBJ_TYPE_ID_GEN ( "THRD" )
/** Timer object type */
# define K_OBJ_TYPE_TIMER_ID K_OBJ_TYPE_ID_GEN ( "TIMR" )
从上一篇文章如何使用Object Cores中我们可以看到被 Object Cores 调试追踪的对象需要嵌入一个 struct k_obj_core obj_core
,结构体为
struct k_obj_core {
sys_snode_t node; //链表节点
struct k_obj_type *type; //指向自己所属的类型结构体
# ifdef CONFIG_OBJ_CORE_STATS
void *stats; /**< Pointer to kernel object's stats */
# endif
};
将这个 object_core
加入到链表内管理,需要先对节点初始化,再加入链表
//初始化对象节点
void k_obj_core_init ( struct k_obj_core *obj_core, struct k_obj_type *type )
{
obj_core->node.next = NULL;
obj_core->type = type; //指向自己的链表类型
# ifdef CONFIG_OBJ_CORE_STATS
obj_core->stats = NULL;
# endif
}
//将节点加入到链表
void k_obj_core_link ( struct k_obj_core *obj_core )
{
k_spinlock_key_t key = k_spin_lock ( &lock ) ;
sys_slist_append ( &obj_core->type->list, &obj_core->node ) ;
k_spin_unlock ( &lock, key ) ;
}
回到前面的 struct k_obj_type
你会发现还有个 node
成员,这是要将不同类型的对象链表再串接为一条链表方便遍历时访问:
//所有的对象链表都会加入到 z_obj_type_list 这条单链表中
sys_slist_t z_obj_type_list = SYS_SLIST_STATIC_INIT ( &z_obj_type_list ) ;
在 z_obj_type_init
中通过 sys_slist_append ( &z_obj_type_list, &type->node ) ;
完成。
有了这个链表后就可以通过定义的 Type 类型找到所属的 object cores:
struct k_obj_type *k_obj_type_find ( uint32_t type_id )
{
struct k_obj_type *type;
struct k_obj_type *rv = NULL;
sys_snode_t *node;
k_spinlock_key_t key = k_spin_lock ( &lock ) ;
//遍历 z_obj_type_list
SYS_SLIST_FOR_EACH_NODE ( &z_obj_type_list, node ) {
type = CONTAINER_OF ( node, struct k_obj_type, node ) ;
//对比 id,如果一致就找到该 Object Core
if ( type->id == type_id ) {
rv = type;
break;
}
}
k_spin_unlock ( &lock, key ) ;
return rv;
}
之后就可以对该 Object Core 进行遍历访问
int k_obj_type_walk_locked ( struct k_obj_type *type,
int ( *func ) ( struct k_obj_core *, void * ) ,
void *data )
{
k_spinlock_key_t key;
struct k_obj_core *obj_core;
sys_snode_t *node;
int status = 0;
key = k_spin_lock ( &lock ) ;
//遍历 Object Cores 的链表,执行回调函数 fun 进行访问
SYS_SLIST_FOR_EACH_NODE ( &type->list, node ) {
obj_core = CONTAINER_OF ( node, struct k_obj_core, node ) ;
status = func ( obj_core, data ) ;
if ( status != 0 ) {
break;
}
}
k_spin_unlock ( &lock, key ) ;
return status;
}
整个 Object Cores 的链表管理结构可以总结为下图:
状态统计 链接到标题
状态统计比较简单,就是把被管理的对象的状态统计函数注册到 Object Cores 中,然后再被调用, 下面展示 query 的流程, 其它 API 大同小异,就不列出分析了
static inline void k_obj_type_stats_init ( struct k_obj_type *type,
struct k_obj_core_stats_desc *stats_desc )
{
//注册回调描述符
type->stats_desc = stats_desc;
}
int k_obj_core_stats_query ( struct k_obj_core *obj_core, void *stats,
size_t stats_len )
{
int rv;
struct k_obj_core_stats_desc *desc;
k_spinlock_key_t key = k_spin_lock ( &lock ) ;
//获取并检查描述符中的回调
desc = obj_core->type->stats_desc;
if (( desc == NULL ) || ( desc->query == NULL )) {
/* The object type is not configured for this operation */
k_spin_unlock ( &lock, key ) ;
return -ENOTSUP;
}
//检查 qurey 的长度是否和描述符内一致
if (( desc->query_size != stats_len ) || ( obj_core->stats == NULL )) {
/*
- Either the size of the stats buffer is wrong or
- the kernel object was not registered for statistics.
*/
k_spin_unlock ( &lock, key ) ;
return -EINVAL;
}
//调用描述符内的回调获取状态
rv = desc->query ( obj_core, stats ) ;
k_spin_unlock ( &lock, key ) ;
return rv;
}
在 struct k_obj_core obj_core
另外还有一个 stat
字段,使用 k_obj_core_stats_register
和 k_obj_core_stats_deregister
可以将被统计对象的原始数据指针提供给该字段,但该字段在实际中并没有怎么使用,因此不做进一步分析。
Zephyr 内 Object Core 使用情况 链接到标题
目前 Object Cores 已被集成到以下内核对象中,只要配置 CONFIG_OBJ_CORE=y
就会全部启用
- 条件变量
CONFIG_OBJ_CORE_CONDVAR
- 事件
CONFIG_OBJ_CORE_EVENT
- FIFO
CONFIG_OBJ_CORE_FIFO
- LIFO
CONFIG_OBJ_CORE_LIFO
- 邮箱
CONFIG_OBJ_CORE_MAILBOX
- Mem Slab
CONFIG_OBJ_CORE_MEM_SLAB
- 互斥量
CONFIG_OBJ_CORE_MUTEX
- 消息队列
CONFIG_OBJ_CORE_MSGQ
- 信号量
CONFIG_OBJ_CORE_SEM
- 管道
CONFIG_OBJ_CORE_PIPE
- 堆栈
CONFIG_OBJ_CORE_STACK
- 线程
CONFIG_OBJ_CORE_THREAD
- 定时器
CONFIG_OBJ_CORE_TIMER
- CPU
CONFIG_OBJ_CORE_SYSTEM
- Mem block
CONFIG_OBJ_CORE_SYS_MEM_BLOCKS
此外 Zephyr 最新代码的网络模块中的 socket 也集成了 Object Cores, 在配置 CONFIG_NET_SOCKETS_OBJ_CORE=y
后即可启用。
以上只是负责了内核对象的串接管理,Object Cores 的统计功能也集成到了一部分内核对象中,只要配置 CONFIG_OBJ_CORE_stats=y
下面的配置项就会被打开
- Mem Slab
CONFIG_OBJ_CORE_STATS_MEM_SLAB
- 线程
CONFIG_OBJ_CORE_STATS_THREAD
- 系统
CONFIG_OBJ_CORE_STATS_SYSTEM
- Mem block
CONFIG_OBJ_CORE_STATS_SYS_MEM_BLOCKS
这些统计项对应的数据结构如下,在使用 k_obj_core_stats_raw
和 k_obj_core_stats_query
时需要匹配传入的参数
配置项 | Object | Raw Data Type | Query Data Type |
---|---|---|---|
CONFIG_OBJ_CORE_STATS_MEM_SLAB |
struct mem_slab |
struct mem_slab_info |
struct sys_memory_stats |
CONFIG_OBJ_CORE_STATS_SYS_MEM_BLOCKS |
struct sys_mem_blocks |
struct sys_mem_blocks_info |
struct sys_memory_stats |
CONFIG_OBJ_CORE_STATS_THREAD |
struct k_thread |
struct k_cycle_stats |
struct k_thread_runtime_stats |
CONFIG_OBJ_CORE_STATS_SYSTEM |
struct _cpu |
struct k_cycle_stats |
struct k_thread_runtime_stats |
CONFIG_OBJ_CORE_STATS_SYSTEM |
struct z_kernel |
struct k_cycle_stats [num CPUs] |
struct k_thread_runtime_stats |
参考 链接到标题
https://docs.zephyrproject.org/3.5.0/kernel/object_cores/index.html