您好,欢迎来到宝玛科技网。
搜索
您的当前位置:首页eCos系统的启动——补充版本

eCos系统的启动——补充版本

来源:宝玛科技网


eCos系统启动流程

张龙 2010-9-24

通过调研,总结了eCos系统的启动流程,我认为可以分为以下两大步:

1. HAL的启动;

2. 内核的启动。

一.HAL的启动

HAL是对硬件进行初始化操作, 当然跟硬件平台有关,不过大致流程是相似的,这里以基于PC的硬件平台为例,图1.1是HAL的启动流程图。

Hardware PowerupReset_vector_startHal_cpu_initHal_hardware_initSetup interrupt stackHal_mon_initClear bss sectionSetup C function call stackHal_platform_initHal_MMU_initHal_enable_cachesHal_IRQ_initCyg_hal_invoke_constructorsInitialize_stubCyg_start On to Kernal startup..

图1.1 HAL启动流程图

下面具体解说一下流程图1.1:

1. 系统启动的开端是电复位,一般是从0X0开始;

2. 电复位之后,跳转到reset_vector,在arch文件夹下的文件vectors.S中,这个文件包含所有HAL包的启动点,reset vector应用最小的处理器寄存器配置来使得系统进行初始化操作;

3. Reset vector跳转到_start函数处,这个函数仍然存在于vectors.S文件中,_start是整个HAL初始化的开始处;

4. 跳转到hal_cpu_init子程序,这个函数存在于variant.inc或者是arch.inc文件中,这个函数设置处理器特定的寄存器,如禁用指令和数据高速缓存,以确保处理器在其余的初始化过程中是一个已知的状态;

5. 跳转到hal_hardware_init子程序,进行cache配置、设置中断寄存器到一个默认的状态,关闭看门狗,设置实时时钟寄存器,和进行基于特殊硬件平台的芯片选择寄存器的配置等操作;

6. 建立中断堆栈区域,当出现中断时用来保存处理器的状态信息;

7. 进行hal_mon_init程序,它位于文件variant.inc或platform.inc中,为了能够正确地访问RAM, ROM 和 I/O设备,设立所有的CPU内存控制器。除非完成了这个动作,否则不可能访问到RAM。当然这个对于不同的存储器会存在差异;

8. 清空BSS段,它包含了所有未初始化的局部和全局变量,还包括静态存储类;

9. 设置堆栈,为执行C语言代码做好准备,通常可以把SP的值设置在上面所分配的RAM空间的最顶端(堆栈向下生长);

10. 执行hal_platform_init程序,位于hal_aux.c文件中,进行平台初始化,这个要根据硬件平台的不同,初始化的操作也有所不同,如进行I/O接口的初始化、PCI的初始化等等;

11. 初始化MMU,MMU处理逻辑地址和物理地址之间的映射,还提供保护机制和缓存机制。在位于hal_misc.c文件中的函数hal_MMU_init中;

12. 使能数据catch和指令catch,函数名为hal_enable_catches,位于文件hal_misc.c中;

13. 对于基于PC处理器的硬件平台来说,此时会运行hal_IRQ_init函数,用来设置通信处理模块(Communications Processor Module),它用来接收和排序内部和外部中断,这个函数存在于文件hal_intr.c中;

14. 在cyg_hal_invoke_constructors()中调用所有全局C++对象的构造函数,这个函数存在于hal_misc.c文件中;

15. 如果配置是一个debug环境,这里会调用位于generic_stub.c文件中的initialize_stub函数,它负责安装标准陷阱处理和初始化硬件来debug;

16. 读取内核映像和根文件系统从Flash或ROM到RAM空间中,转入内核的初始化,调用函数cyg_start;

二.内核的启动

内核的启动时由HAL的启动结束时唤醒的,内核的启动被包含在函数cyg_start中,

它通过调用一些启动函数来处理各种各样的初始化工作。Cyg_start位于文件startup.cxx中。图2.1描述了内核启动的过程。

HAL_startup ProcedureCyg_startCyg_prestartCyg_package_startCyg_user_startStart the scheduler 图2.1 内核启动流程图

1. 启动cyg_start()

启动cyg_start()函数是ecos启动机制的核心。该函数位于源文件infra/current/src/startup.cxx,它依次调用下列函数:

cyg_prestart()

cyg_package_start()

cyg_user_start()

如果在配置时选择了调度器,它还将启动被选择的ecos调度器。

这只是ecos所提供的一个实现方法。用户服务在进行自己的开发时,也可以使用下面的函数原型对该函数进行修改:

void cyg_start(void)

一般来说不需要对该函数进行修改,因为cyg_prestart()函数和cyg_user_start()函数具有足够的灵活性,允许用户加入其他程序代码,几乎可以满足所有的应用需求。

2. 函数cyg_prestart()

该函数位于源文件infra/current/src/prestart.cxx内。ecos提供了默认的cyg_prestart()函数,不做任何操作。如果在进行其他系统级初始化操作之前还需要进行某些初始化操作,则可以使用该函数来实现。该函数原型为:

void cyg_prestart(void)

3. 函数cyg_package_start()

该函数位于源程序infra/current/src/pkgstart.cxx内。它允许在进入用户主程序之前对个别包进行初始化操作。

cyg_package_start()函数包含了两个包:UTTRON包和标准C库包。基础结构包中包含了两个配置选项 CYGSEM_START_UITRON_COMPATIBILITY

CYGSEM_START_ISO_COMPATIBILITY,它们用于控制这 些特殊的包的初始化。函数原型为:

void cyg_package_start()

用户可以根据该原型编写自己的cyg_package_start()函数,但在初始化默认包时必须加以小心。下面是用户编写该函数的一个例子:

void cyg_package_start(start)

{

#ifdef CYGSEM_START_UITRON_COMPATABILITY

cyg_uitron_start(); /*keep the uITRON initialization */

#endif

my_package_start(); /* make sure I initialize my package */

}

4.函数cyg_user_start()

该函数位于源文件infra/current/src/userstart.cxx内。cyg_user_start()函数是用户程序的正常入口点。ecos源码提供的该函数不做任何操作。这是用户创建自己线程的一个理想的地方。如果在配置时没有选择ISO标准C库包,则必须实现该函数,此时它是用户程序的一个强制性入口。其函数原型为:

void cyg_user_start(void)

用户可以编写自己的cyg_user_start()函数。ecos为该函数提供了一个默认实现,但不做任何工作。应用程序使用该函数作为入口时,将覆盖其默认实现,实现应用程序与ecos系统的连接。

如果配置时选择了ISO标准C库包,这个库提供了一个默认的cyg_user_start()函数,这个默认的函数会产生一个线程,来调用用户的main函数。[1]

当从cyg_user_start()函数返回时,cyg_start()函数将启动调度器,用户在cyg_user_start()函数中所产生和唤醒的所有线程都将开始执行。另一个值得注意的地方是由于cyg_user_start()函数是在调度器启动之前执行的,因此在该函数中不要使用任何需要调度器的内核服务。

在开始调度的时候,cyg_Scheduler::start_cpu()开启系统需要的中断。

三.具体的从main()函数启动的过程

通过查看我们的配置文件,可以看到是包含了ISO标准C库的,其配置选项为:CYGPKG_LIBC,基本流程如图3.1图:

Cyg_startCyg_prestartCyg_package_startCYGSEM_LIBC_STARTUP_MAIN_THREADCYGSEM_LIBC_STARTUP_MAIN_INITCONTEXTCyg_iso_c_startCyg_user_startCyg_libc_main_threadCyg_libc_invoke_mainMain 图3.1 调用main函数的过程

下面具体地讲解一下这个过程:

1.从cyg_package_start()说起,通过下面这一段代码可以看出在配置了ISO标准C库的时候,即配置了CYGPKG_LIBC时,调用cyg_iso_c_start()函数。

externC void

cyg_package_start( void )

{

#ifdef CYGPKG_LIBC

cyg_iso_c_start();

#else

(void)main(0, NULL);

#endif

} // cyg_package_start()

2. 进入cyg_iso_c_start(void)函数之后,通过查看

\\packages\\language\\c\\libc\\startup\\current\\src\\Cstartup.cxx,里面有这样的一段代码:

#ifdef CYGSEM_LIBC_STARTUP_MAIN_THREAD

extern Cyg_Thread cyg_libc_main_thread;

// FUNCTIONS

externC void

cyg_iso_c_start( void )

{

static int initialized=0;

CYG_REPORT_FUNCNAME( \"cyg_iso_c_start\" );

CYG_REPORT_FUNCARGVOID();

if (initialized++ == 0) {

CYG_TRACE0( true, \"Resuming cyg_libc_main_thread\" );

cyg_libc_main_thread.resume();

}

CYG_REPORT_RETURN();

} // cyg_iso_c_start()

就是说当配置了CYGSEM_LIBC_STARTUP_MAIN_THREAD时,就是如果配置了从线程启动main函数的时候,就定义一个线程cyg_libc_main_thread,然后cyg_iso_c_start()函数中就启动这个线程。

而如果没有配置CYGSEM_LIBC_STARTUP_MAIN_THREAD,而配置了CYGSEM_LIBC_STARTUP_MAIN_INITCONTEXT时,就重定义cyg_user_start()函数,在这个函数里面调用了另一个函数:cyg_libc_invoke_main(0),这时的cyg_iso_c_start()函数中也调用了这个函数:cyg_libc_invoke_main(0)。

#elif defined( CYGSEM_LIBC_STARTUP_MAIN_INITCONTEXT )

externC void

cyg_user_start(void)

{

cyg_libc_invoke_main(0);

}

externC void

cyg_iso_c_start( void )

{

static int initialized=0;

CYG_REPORT_FUNCNAME( \"cyg_iso_c_start\" );

CYG_REPORT_FUNCARGVOID();

// In case they want to explicitly invoke the C library from

// cyg_user_start() themselves

if (initialized++ == 0) {

cyg_libc_invoke_main(0);

}

CYG_REPORT_RETURN();

}

而我们的配置里面是走的前者,就是说配置了CYGSEM_LIBC_STARTUP_MAIN_THREAD,这样的话,就是通过cyg_iso_c_start()函数启动了一个线程cyg_libc_main_thread。

3. 再来看线程cyg_libc_main_thread,可以在文件mainthread.cxx中看到:

Cyg_Thread cyg_libc_main_thread CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_LIBC) =

Cyg_Thread(CYGNUM_LIBC_MAIN_THREAD_PRIORITY,

&cyg_libc_invoke_main, (CYG_ADDRWORD) 0,

\"main\",

(CYG_ADDRESS) &cyg_libc_main_stack[0],

#ifdef CYGSEM_LIBC_MAIN_STACK_FROM_SYSTEM

CYGNUM_LIBC_MAIN_DEFAULT_STACK_SIZE

#else

cyg_libc_main_stack_size

#endif

);

这是这个线程的定义,启动它的时候,就会通过函数cyg_libc_invoke_main调用main()函数。

4. 下面来看一下函数cyg_libc_invoke_main(),可以在文件invokemain.cxx中看到:

externC void

cyg_libc_invoke_main( CYG_ADDRWORD )

{

CYG_REPORT_FUNCNAME( \"cyg_libc_invoke_main\" );

CYG_REPORT_FUNCARG1( \"argument is %s\

#ifdef CYGSEM_LIBC_INVOKE_DEFAULT_STATIC_CONSTRUCTORS

// finish invoking constructors that weren't called by default

cyg_hal_invoke_constructors();

#endif

// argv[argc] must be NULL according to the ISO C standard 5.1.2.2.1

char *temp_argv[] = CYGDAT_LIBC_ARGUMENTS ;

int rc;

rc = main( (sizeof(temp_argv)/sizeof(char *)) - 1, &temp_argv[0] );

CYG_TRACE1( true, \"main() has returned with code %d. Calling exit()\

rc );

#ifdef CYGINT_ISO_PTHREAD_IMPL

// It is up to pthread_exit() to call exit() if needed

pthread_exit( (void *)rc );

CYG_FAIL( \"pthread_exit() returned\" );

#else

exit(rc);

CYG_FAIL( \"exit() returned\" );

#endif

CYG_REPORT_RETURN();

} // cyg_libc_invoke_main()

可以看到这里面通过配置选项CYGDAT_LIBC_ARGUMENTS为main()函数传递参数,然后完成了mian()函数的调用。

下面说说上面第2步中的配置CYGSEM_LIBC_STARTUP_MAIN_THREAD和配置CYGSEM_LIBC_STARTUP_MAIN_INITCONTEXT两者之间的区别,虽然两者最后都用到了函数cyg_libc_invoke_main(),但是还是有所不同:

·前者呢,是通过产生一个eCos线程cyg_libc_main_thread来调用main()函数,那么线程就必须在启用调度后才开启,就是cyg_Scheduler调度器启动后,才会根据优先级、调度策略等等来启动这个线程,然后调用main()函数;

·后者呢,它是通过重载的cyg_user_start()函数调用的,是在系统初始化的时候运行,而不是eCos内核调度器启动的时候运行, 这就意味着应用程序不能和内核进行交互,必须进行操作。

我们的配置是前者。

参考文献

1. Anthony J.Massa,《Embedded software development with eCos》.

2.《嵌入式可配置实时操作系统eCos技术及实现机制》.

3.《嵌入式可配置实时操作系统eCos开发与应用》.

4. 网上资源:凌阳大学计划论坛eCos技术讨论区www.unsp.com.

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- baomayou.com 版权所有 赣ICP备2024042794号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务