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 进行初始化时会创建一个单链表,并将 idobj_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_registerk_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_rawk_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