eCos组件初始化

mingdu.zheng <at> gmail <dot> com
http://blog.csdn.net/zoomdy/article/details/12754709

eCos组件初始化机制

eCos组件初始化利用了C++静态对象实例初始化的机制。C++对象在初始化时不像C语言中的静态变量那样只是在特定的内存单元写入特定的数值,C++对象在初始化时将会调用该对象类的构造函数来初始化对象。如果C++对象是在函数内声明,那么函数执行到对象的声明处调用类构造函数来初始化对象,如果C++对象在函数外声明,也就是将C++对象声明为全局对象或静态对象,那么编译器会将该对象的初始化代码地址(函数指针)写到名为".ctors"节,普通程序在进入main函数前由操作系统的运行时库调用存储在".ctors"中的函数指针列表来初始化全局或静态C++对象。

eCos应用在编译后同样会将全局或静态C++对象的初始化代码地址写到".ctors"节,在进入用户代码前,eCos HAL将调用cyg_hal_invoke_constructors函数,该函数调用存储在".ctors"节中的初始化代码。

需要注意的一点,".ctors"中存储的是对象初始化代码地址,而不是类构造函数,因为在初始化对象时,不仅要调用构造函数,而且还要给构造函数传入参数,即使是没有参数的默认构造函数也需要传入隐式的this指针。

忽略CYGSEM_HAL_STOP_CONSTRUCTORS_ON_FLAG选项的cyg_hal_invoke_constructors函数,如果配置了CYGSEM_HAL_STOP_CONSTRUCTORS_ON_FLAG选项,那么该函数将更加复杂些。

 

extern pfunc __init_array_start__[]; 
extern pfunc __init_array_end__[];  
#define CONSTRUCTORS_START  (__init_array_start__[0])// ".ctors"的起始地址
#define CONSTRUCTORS_END    (__init_array_end__) // ".ctors"的终止地址
#define NEXT_CONSTRUCTOR(c) ((c)++)

void
cyg_hal_invoke_constructors (void)
{
    pfunc *p;
    for (p = &CONSTRUCTORS_START; p != CONSTRUCTORS_END; NEXT_CONSTRUCTOR(p))
        (*p)();
}

 

C++语言实现的组件初始化

 

 

举个例子,每个操作系统都需要一个空闲线程,eCos也不例外,eCos的空闲线程就是以C++全局对象实例初始化机制来创建的。

 

Cyg_IdleThread idle_thread[CYGNUM_KERNEL_CPU_MAX] CYG_INIT_PRIORITY( IDLE_THREAD );

Cyg_IdleThread::Cyg_IdleThread()
    : Cyg_Thread( CYG_THREAD_MIN_PRIORITY,
                  idle_thread_main,
                  0,
                  (char *)"Idle Thread",
                  (CYG_ADDRESS)idle_thread_stack[this-&idle_thread[0]],
                  CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE)
{
    Cyg_Scheduler::scheduler.set_idle_thread( this, this-&idle_thread[0] );
}


从代码可以看出,eCos的空闲线程Cyg_IdleThread是从常规线程Cyg_Thread继承过来的,除了和常规线程一样的初始化过程外,额外地调用了Cyg_Scheduler::scheduler.set_idle_thread函数,该函数将本线程设置成eCos的空闲线程,并将本线程设置成就绪状态。

 

 

C语言实现的组件初始化

 

 

 

如果eCos组件是使用C语言实现的呢?举个例子,IO组件,该组件是使用C语言实现的,但是如果浏览他的源代码,会发现他有一个C++源代码ioinit.cxx,在这个文件中将定义一个C++类,而这个类唯一的作用就是在构造函数中调用cyg_io_init函数,然后声明一个静态的类实例来初始化IO组件。

 

externC void cyg_io_init(void);

class cyg_io_init_class {
public:
    cyg_io_init_class(void) { 
        cyg_io_init();
    }
};

static cyg_io_init_class _cyg_io_init CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO);
​

 

初始化次序的问题

 

 

因为组件之间的依赖关系,初始化过程需要按照一定的次序进行。初始化的次序由CYGBLD_ATTRIB_INIT_PRI宏指定,该宏带有一个参数,该参数数值越小,那么越早调用该实例的初始化代码,例如_cyg_io_init对象的CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO)展开后为__attribute__((init_priority(49000))),而idle_thread对象的宏展开后为__attribute__((init_priority(11100))),由此可以判断空闲线程在IO组件之前被初始化。
 

检查初始化代码

 

使用nm和grep可以打印过滤可执行文件的符号列表来检查初始化代码,符号前缀“_GLOBAL__sub_I”表示这是静态对象初始化代码,符号中间部分的数字决定初始化次序,数字后面的部分如果是全局对象,那么是对象标识符,如果是静态变量,与对象的存储位置有关,可能是所在文件名,也可能是对象所在位置后面的全局标识符,反正不是对象标识符,这个有点混乱。

 

$ nm app.elf | grep "t _GLOBAL"
01001b20 t _GLOBAL__sub_I.10100_diag_write_char
01002da0 t _GLOBAL__sub_I.11000_cyg_scheduler_sched_lock
01002340 t _GLOBAL__sub_I.11100__ZN18Cyg_HardwareThread12thread_entryEP10Cyg_Thread
01002630 t _GLOBAL__sub_I.12000__ZN13Cyg_Interrupt15disable_counterE
01005d70 t _GLOBAL__sub_I.14000__ZN9Cyg_Clock15real_time_clockE
01006020 t _GLOBAL__sub_I.19900_cygmem_pool_heap1
01000050 t _GLOBAL__sub_I.49000_ioinit.cxx
010063a0 t _GLOBAL__sub_I.56000__ZN20Cyg_libc_stdio_files5filesE
01003290 t _GLOBAL__sub_I.56000_cyg_libc_main_thread
01006300 t _GLOBAL__sub_I.56000_cyg_libc_stdio_stdin
01003490 t _GLOBAL__sub_I.56001_cyg_libc_stdio_stdout
01006260 t _GLOBAL__sub_I.56002_cyg_libc_stdio_stderr
01000cc0 t _GLOBAL__sub_I.56100_cyg_iso_c_start

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值